Skip to content

Commit c4c6523

Browse files
committed
Rewrite to greenlets (remove awaits from user programs)
1 parent a948091 commit c4c6523

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+4159
-3255
lines changed

README.md

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
# Pythonized CC Tweaked (ComputerCraft) API
22

3-
1. Enable localhost in mod server config
3+
**Warning**: CPython can't build safe sandboxes for arbitrary untrusted code
4+
[(read more)](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html).
5+
Never use code in this repo if you don't trust your players!
6+
7+
1. Download and install wheel from github releases
8+
9+
```sh
10+
pip install computercraft-*.whl
11+
```
12+
13+
2. Enable localhost in mod server config
414

515
In case of singleplayer it's located inside your saves folder.
616
In case of multiplayer check your server folder.
@@ -13,80 +23,90 @@
1323
action = "allow" # change here deny to allow
1424
```
1525
16-
2. Create module named `examplemod.py`:
26+
3. Start python server:
1727
18-
```python
19-
async def hello(api):
20-
await api.print('Hello world!')
21-
```
22-
23-
3. Start a server:
24-
25-
```bash
26-
python -m computercraft.server examplemod
28+
```sh
29+
python -m computercraft.server
2730
```
2831
2932
4. In minecraft, open up any computer and type:
3033
31-
```bash
34+
```sh
3235
wget http://127.0.0.1:8080/ py
33-
py hello
36+
py
3437
```
3538
39+
Now you have python REPL in computercraft!
40+
To quit REPL type `exit()` and press enter.
41+
3642
`py` is short Lua program that interacts with the server.
37-
Argument is the name of coroutine inside the module.
38-
`api` object contains almost everything *as is* in ComputerCraft documentation:
43+
`cc` module contains almost everything *as is* in ComputerCraft documentation:
3944
4045
```python
41-
async def program(api):
42-
await api.disk.eject('right')
43-
await api.print(await api.os.getComputerLabel())
44-
# ...
46+
from cc import disk, os
47+
48+
disk.eject('right')
49+
print(os.getComputerLabel())
4550
```
4651
47-
Using python coroutines allows launching commands in parallel, effectively replacing `parallel` API:
52+
Opening a file:
4853
4954
```python
50-
async def program(api):
51-
# Since os.sleep is mostly waiting for events, it doesn't block execution of parallel threads
52-
# and this snippet takes approximately 2 seconds to complete.
53-
await asyncio.gather(api.os.sleep(2), api.os.sleep(2))
55+
from cc import fs
56+
57+
with fs.open('filename', 'r') as f:
58+
for line in f:
59+
print(line)
5460
```
5561
56-
Opening a file:
62+
Waiting for event:
5763
5864
```python
59-
async def program(api):
60-
async with api.fs.open('filename', 'r') as f:
61-
async for line in f:
62-
await api.print(line)
65+
from cc import os
66+
67+
timer_id = os.startTimer(2)
68+
while True:
69+
e = os.pullEvent('timer')
70+
if e[1] == timer_id:
71+
print('Timer reached')
72+
break
6373
```
6474
65-
Capturing event:
75+
Using modems:
6676
6777
```python
68-
async def program(api):
69-
async with api.os.captureEvent('timer') as timer_event_queue:
70-
timer_id = await api.os.startTimer(2)
71-
async for etid, *_ in timer_event_queue:
72-
if etid == timer_id:
73-
await api.print('Timer reached')
74-
break
78+
from cc import peripheral
79+
80+
modem = peripheral.wrap('back')
81+
listen_channel = 5
82+
# this automatically opens and closes modem on listen_channel
83+
for msg in modem.receive(listen_channel):
84+
print(repr(msg))
85+
if msg.content == 'stop':
86+
break
87+
else:
88+
modem.transmit(msg.reply_channel, listen_channel, msg.content)
7589
```
7690
77-
Using modems:
91+
Using parallel:
92+
93+
```python
94+
from cc import parallel, os
95+
96+
def fn():
97+
os.sleep(2)
98+
print('done')
99+
100+
parallel.waitForAll(fn, fn, fn)
101+
```
102+
103+
Importing in-game files as modules:
78104
79105
```python
80-
async def program(api):
81-
modem = await api.peripheral.wrap('back')
82-
listen_channel = 5
83-
async with modem.receive(listen_channel) as q:
84-
async for msg in q:
85-
await api.print(repr(msg))
86-
if msg.content == 'stop':
87-
break
88-
else:
89-
await modem.transmit(msg.reply_channel, listen_channel, msg.content)
106+
from cc import import_file
107+
108+
p = import_file('/disk/program.py') # absolute
109+
m = import_file('lib.py', __file__) # relative to current file
90110
```
91111
92-
More examples can be found in `testmod.py`.
112+
More examples can be found in repository.

computercraft/back.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ while true do
5656
ycounts[msg.task_id] = 0
5757
end
5858
end
59+
elseif msg.action == 'drop' then
60+
for _, task_id in ipairs(msg.task_ids) do
61+
tasks[task_id] = nil
62+
ycounts[task_id] = nil
63+
end
5964
elseif msg.action == 'sub' then
6065
event_sub[msg.event] = true
6166
elseif msg.action == 'unsub' then
@@ -88,12 +93,12 @@ while true do
8893
})
8994
del_tasks[task_id] = true
9095
else
91-
ycounts[msg.task_id] = ycounts[msg.task_id] + 1
96+
ycounts[task_id] = ycounts[task_id] + 1
9297
end
9398
end
9499
for task_id in pairs(del_tasks) do
95100
tasks[task_id] = nil
96-
ycounts[msg.task_id] = nil
101+
ycounts[task_id] = nil
97102
end
98103
end
99104

computercraft/lua.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,11 @@ def lua_args(*params):
7373
idx = -1
7474
params = params[:idx + 1]
7575
return ', '.join(lua_value(p) for p in params)
76+
77+
78+
def lua_call(name, *params):
79+
return '{}({})'.format(name, lua_args(*params))
80+
81+
82+
def return_lua_call(name, *params):
83+
return 'return ' + lua_call(name, *params)

0 commit comments

Comments
 (0)