Skip to content

Commit 9837843

Browse files
committed
Add basic block API and add more entity API functions
Also update readme
1 parent c58eed5 commit 9837843

File tree

9 files changed

+165
-13
lines changed

9 files changed

+165
-13
lines changed

README.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,31 @@ keeps the usage of command blocks to a minimum.
1616
There is a C compiler that compiles to this assembly language,
1717
read more [here](https://github.com/simon816/Command-Block-Assembly/blob/master/README_C.md).
1818

19+
As shown in the [fibonacci sequence example](https://github.com/simon816/Command-Block-Assembly/blob/master/examples/fib.c),
20+
it's just like writing normal C code.
21+
22+
```c
23+
#include <stdio.h>
24+
25+
int x;
26+
int y;
27+
int old_x;
28+
int counter;
29+
30+
void main() {
31+
x = 0;
32+
y = 1;
33+
counter = 1;
34+
do {
35+
printf("fib(%d) = %d", counter++, x);
36+
sync;
37+
old_x = x;
38+
x = y;
39+
y += old_x;
40+
} while(x >= 0);
41+
}
42+
```
43+
1944
# The Assembly Language
2045

2146
It is a simple language with instructions similar to that of x86.
@@ -105,6 +130,17 @@ main:
105130
|PRINT|arg1, [...args]|Outputs arguments to chat for all players (`@a` selector)|
106131
|CMD|bare words|Runs the given command|
107132
|TEST|bare words|Runs the given command, skipping the next line if the command failed|
133+
|EXECAS|label, sel_type, sel_pairs|Runs the function defined in `label` using `/execute as` if the selector matches|
134+
|EXECASN|label, sel_type, sel_pairs|Same as EXECAS except runs if it does _not_ match the selector|
135+
|EXECAT|label, sel_type, sel_pairs|Runs the function defined in `label` using `/execute at` if the selector matches|
136+
|EXECATP|label, sel_type, sel_pairs|Runs the function defined in `label` using `/execute positioned as` if the selector matches|
137+
|EXECPOS|label, x, y, z|Runs the function defined in `label` using `/execute positioned`|
138+
|EXECALI|label, axes|Runs the function defined in `label` using `/execute align`|
139+
|EXECFACP|label, x, y, z|Runs the function defined in `label` using `/execute facing`|
140+
|EXECFAC|label, feature, sel_type, sel_pairs|Runs the function defined in `label` using `/execute facing entity`|
141+
|EXECROT|label, y, x|Runs the function defined in `label` using `/execute rotated`|
142+
|EXECROTE|label, sel_type, sel_pairs|Runs the function defined in `label` using `/execute rotated as`|
143+
|EXECANC|label, anchor|Runs the function defined in `label` using `/execute anchored`|
108144
|PUSH||Pushes stack register onto the stack, increments stack pointer|
109145
|POP||Pops stack into stack register, decrements stack pointer|
110146
|SYNC||Synchronises with the game tick. i.e. wait one tick before continuing|
@@ -161,6 +197,17 @@ into wherever the directive is.
161197
"Include headers". Does not load any code from the file, but pulls in the symbol table (subroutines, constants).
162198
Useful for using library code already running in the game. (i.e. library was loaded sometime beforehand).
163199

200+
#### `#event_handler label event_name condition1=value1;condition2=value2;...`
201+
202+
Runs the subroutine with the given `label` whenever the named event is triggered and the conditions match.
203+
The following is an example where the function `on_placed_stone` will get invoked every time a player places
204+
a stone block.
205+
206+
```
207+
#event_handler on_placed_stone minecraft:placed_block item.item=minecraft:stone
208+
on_placed_stone:
209+
...
210+
```
164211

165212
## Memory locations
166213

@@ -183,9 +230,11 @@ The assembler is invoked by calling `main.py`.
183230

184231
Command line parameters:
185232
```
186-
usage: main.py [-h] [--world-dir WORLD_DIR] [--namespace NAMESPACE]
233+
usage: main.py [-h] [--world-dir WORLD_DIR] [--as_zip] [--namespace NAMESPACE]
187234
[--rem-existing] [--debug] [--stack STACK] [--arg ARG]
188235
[--jump JUMP] [--place-location PLACE_LOCATION] [--enable-sync]
236+
[--setup-on-load] [--spawn-location SPAWN_LOCATION]
237+
[--pack-description PACK_DESCRIPTION]
189238
file
190239
191240
positional arguments:
@@ -195,6 +244,7 @@ optional arguments:
195244
-h, --help show this help message and exit
196245
--world-dir WORLD_DIR
197246
World Directory
247+
--as_zip Write datapack as zip file
198248
--namespace NAMESPACE
199249
Function namespace
200250
--rem-existing Remove existing functions in namespace
@@ -205,6 +255,11 @@ optional arguments:
205255
--place-location PLACE_LOCATION
206256
Location to place command blocks
207257
--enable-sync Enable SYNC opcode
258+
--setup-on-load Run setup on minecraft:load
259+
--spawn-location SPAWN_LOCATION
260+
Location to spawn hidden armor stand
261+
--pack-description PACK_DESCRIPTION
262+
Datapack description
208263
```
209264

210265
Notes:

README_C.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,27 @@ which is a 32 bit signed integer.
1212

1313
The compiler is invoked by calling `compiler_main.py`.
1414

15-
The parameters are similar to the assembler, with the addition of `--page-size` and `--dump-asm`.
16-
17-
1815
Command line parameters:
1916
```
20-
usage: compiler_main.py [-h] [--world-dir WORLD_DIR] [--namespace NAMESPACE]
21-
[--rem-existing] [--debug] [--stack STACK] [--arg ARG]
17+
usage: compiler_main.py [-h] [-E] [-S] [--world-dir WORLD_DIR] [--as_zip]
18+
[--namespace NAMESPACE] [--rem-existing] [--debug]
19+
[--stack STACK] [--arg ARG]
2220
[--place-location PLACE_LOCATION] [--enable-sync]
23-
[--page-size PAGE_SIZE] [--dump-asm]
21+
[--page-size PAGE_SIZE] [--setup-on-load]
22+
[--spawn-location SPAWN_LOCATION]
23+
[--pack-description PACK_DESCRIPTION]
2424
file
2525
2626
positional arguments:
2727
file C File
2828
2929
optional arguments:
3030
-h, --help show this help message and exit
31+
-E Only run preprocessor. Outputs to stdout
32+
-S Don't run assembler. Outputs ASM to stdout
3133
--world-dir WORLD_DIR
3234
World Directory
35+
--as_zip Write datapack as zip file
3336
--namespace NAMESPACE
3437
Function namespace
3538
--rem-existing Remove existing functions in namespace
@@ -41,10 +44,14 @@ optional arguments:
4144
--enable-sync Enable SYNC opcode
4245
--page-size PAGE_SIZE
4346
Memory page size
44-
--dump-asm Dump generated ASM
47+
--setup-on-load Run setup on minecraft:load
48+
--spawn-location SPAWN_LOCATION
49+
Location to spawn hidden armor stand
50+
--pack-description PACK_DESCRIPTION
51+
Datapack description
4552
```
4653

4754
There are some examples in the [examples](https://github.com/simon816/Command-Block-Assembly/tree/master/examples) directory.
4855

49-
The [mclib.h](https://github.com/simon816/Command-Block-Assembly/blob/master/examples/mclib.h) file
56+
The [mclib.h](https://github.com/simon816/Command-Block-Assembly/blob/master/compiler/include/mclib.h) file
5057
contains several useful macros and definitions.

assembler.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,9 +787,11 @@ def handle_execute_face_pos(self, lbl, x, y, z):
787787
self._execute_chain_helper(lbl, ExecuteChain().facing(
788788
self._read_pos(x, y, z)))
789789

790-
def handle_execute_face_entity(self, lbl, sel_type, *pairs):
790+
def handle_execute_face_entity(self, lbl, feature, sel_type, *pairs):
791+
arg_type, feature = feature
792+
assert arg_type == 'string'
791793
self._execute_chain_helper(lbl, ExecuteChain().facing_entity(
792-
self._read_selector(sel_type, pairs)))
794+
self._read_selector(sel_type, pairs), feature))
793795

794796
def handle_execute_rotate(self, lbl, y, x):
795797
y, x = self.resolve_ref(*y), self.resolve_ref(*x)

compiler/include/block.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* block.h
3+
*
4+
* Block utilities.
5+
*/
6+
7+
8+
#ifndef __BLOCK_H
9+
#define __BLOCK_H
10+
11+
int is_block(const char *location, const char *block_id, ...);
12+
13+
void set_block(const char *location, const char *block_id, ...);
14+
15+
#endif /* __BLOCK_H */

compiler/include/entity.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,10 @@ void remove_tag_this_entity(const char *tag);
3939

4040
void set_scoreboard_tracking(entity_local variable, const char *criterion);
4141

42+
void set_this_entity_rotation(int yaw, int pitch);
43+
44+
void summon_entity(const char *entity_name, const char *location, const char *nbt);
45+
46+
void kill_this_entity();
47+
4248
#endif /* __ENTITY_H */
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
#ifndef __MCLIB_H
9-
#define __MCLIB_H 1
9+
#define __MCLIB_H
1010

1111
/**
1212
* Stringify (add quotes to) the bare word argument

compiler/lib/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ def load():
33
from . import \
44
builtin, \
55
stdio, \
6-
entity
6+
entity, \
7+
block
78

89
return locals()
910

compiler/lib/block.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from ..ir import IR
2+
3+
def make_block_name(block_id, props):
4+
prop_dict = {}
5+
for prop in props:
6+
key, value = prop.split('=')
7+
prop_dict[key.strip()] = value.strip()
8+
return block_id + ('' if not props else '[%s]' % ','.join(
9+
'%s=%s' % item for item in prop_dict.items()))
10+
11+
def is_block(visitor, expr):
12+
assert len(expr.args) >= 2, "is_block takes at least 2 arguments"
13+
args = []
14+
for arg in expr.args:
15+
arg_val = visitor.visit_expression(arg)
16+
assert isinstance(arg_val, IR.LiteralString), \
17+
"All arguments to is_block must be constant strings"
18+
args.append(arg_val)
19+
loc, block, *props = map(lambda a:a.val, args)
20+
# TODO possible use of ExecSel
21+
cmd = 'execute if block %s %s' % (loc, make_block_name(block, props))
22+
res = IR.Slot(visitor.type('int'))
23+
visitor.emit(IR.Test(cmd, res))
24+
return res
25+
26+
def set_block(visitor, expr):
27+
assert len(expr.args) >= 2, "set_block takes at least 2 arguments"
28+
args = []
29+
for arg in expr.args:
30+
arg_val = visitor.visit_expression(arg)
31+
assert isinstance(arg_val, IR.LiteralString), \
32+
"All arguments to set_block must be constant strings"
33+
args.append(arg_val)
34+
loc, block, *props = map(lambda a:a.val, args)
35+
cmd = 'setblock %s %s' % (loc, make_block_name(block, props))
36+
visitor.emit(IR.Asm((('CMD ' + cmd, None, None),)))
37+
38+
def exports():
39+
return {
40+
'is_block': is_block,
41+
'set_block': set_block,
42+
}

compiler/lib/entity.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,33 @@ def set_tracking(visitor, expr):
3030
visitor.emit(IR.Asm((('CMD ' + rem_old, None, None),)))
3131
visitor.emit(IR.Asm((('CMD ' + add_new, None, None),)))
3232

33+
def set_rotation(visitor, expr):
34+
assert len(expr.args) == 2
35+
yaw, pitch = map(visitor.visit_expression, expr.args)
36+
assert isinstance(yaw, IR.LiteralInt)
37+
assert isinstance(pitch, IR.LiteralInt)
38+
visitor.emit(IR.Asm((('CMD tp @s ~ ~ ~ %d %d'
39+
% (yaw.val, pitch.val), None, None),)))
40+
41+
def summon_entity(visitor, expr):
42+
assert len(expr.args) == 3
43+
name, loc, nbt = map(visitor.visit_expression, expr.args)
44+
assert isinstance(name, IR.LiteralString)
45+
assert isinstance(loc, IR.LiteralString)
46+
assert isinstance(nbt, IR.LiteralString)
47+
visitor.emit(IR.Asm((('CMD summon %s %s %s'
48+
% (name.val, loc.val, nbt.val), None, None),)))
49+
50+
def kill_this_entity(visitor, expr):
51+
assert len(expr.args) == 0
52+
visitor.emit(IR.Asm((('CMD kill @s', None, None),)))
53+
3354
def exports():
3455
return {
3556
'add_tag_this_entity': add_tag,
3657
'remove_tag_this_entity': remove_tag,
3758
'set_scoreboard_tracking': set_tracking,
59+
'set_this_entity_rotation': set_rotation,
60+
'summon_entity': summon_entity,
61+
'kill_this_entity': kill_this_entity,
3862
}

0 commit comments

Comments
 (0)