Skip to content

Instantly share code, notes, and snippets.

@tombailey
Last active April 24, 2019 21:36
Show Gist options
  • Select an option

  • Save tombailey/e1e2a06b7507937b573706a6ef056789 to your computer and use it in GitHub Desktop.

Select an option

Save tombailey/e1e2a06b7507937b573706a6ef056789 to your computer and use it in GitHub Desktop.
Tested on Minecraft BE v1.11.0 for Windows 10
sources/handlers are of the form:
when SOME_EVENT "POTENTIALLY A PARAMETER HERE", SOME_ACTION "POTENTIALLY A PARAMETER HERE"
As an example, the following will send a chat message saying "Hello world" when a player first enters a world:
when player enters world, send chat message "Hello world"
Since sources/handlers are strings of text, you can either use single quotes when creating your string literal or simply escape the nested double quote of any parameter. For example:
const sources = ['when player enters world, send chat message "Hello world"'];
Or:
const sources = ["when player enters world, send chat message \"Hello world\""];
At this early stage, the only supported events are:
player enters world
and
player looks at mob "minecraft:pig"
The only supported actions are:
send chat message "Hello world"
and
spawn particle "minecraft:mobflame_emitter" at position "0 70 0"
If any of this peaks your interest and you want to discuss it further tweet me, @tombailey64 on twitter
//Super early preview of a parsing script for Minecraft Bedrock edition
//Just edit "const sources = [...]" to create a mod with no JavaScript knowledge required
//of course you need to know the syntax for sources (see the description for this gist)
//comments, questions, tweet me @tombailey64 on twitter
const clientSystem = client.registerSystem(0, 0);
class Controller {
constructor() {
this.eventNameToHandler = {
[Controller.EVENT_PLAYER_ENTERS_WORLD]: [],
[Controller.EVENT_PLAYER_LOOKS_AT_MOB]: [],
};
}
add(event, action) {
this.eventNameToHandler[event.name].push({
event,
action,
});
}
invokeAction(action) {
Controller.ACTIONS[action.name]
.implementation
.apply(undefined, action.params);
}
onPlayerEnterWorld() {
this.eventNameToHandler[Controller.EVENT_PLAYER_ENTERS_WORLD].forEach((handler) => {
this.invokeAction(handler.action);
});
}
onHitResultChanged(eventData) {
if (eventData.entity.__type__ == "entity") {
const identifier = eventData.entity.__identifier__;
this.eventNameToHandler[Controller.EVENT_PLAYER_LOOKS_AT_MOB].forEach((handler) => {
if (handler.event.params[0] == identifier) {
this.invokeAction(handler.action);
}
});
}
}
}
Controller.EVENT_PLAYER_ENTERS_WORLD = "player enters world";
Controller.EVENT_PLAYER_LOOKS_AT_MOB = "player looks at mob";
Controller.EVENTS = {
[Controller.EVENT_PLAYER_ENTERS_WORLD]: {
paramsLength: 0,
},
[Controller.EVENT_PLAYER_LOOKS_AT_MOB]: {
paramsLength: 1,
},
};
//TODO: figure out where to put this
function parsePosition(position) {
const coordinates = position.split(" ");
if (coordinates.length != 3) {
throw `Invalid position, 3 coordinates were expected but only ${coordinates.length} were given`;
} else {
const parsedCoordinates = coordinates.map((coordinate) => {
const parsedCoordinate = parseInt(coordinate);
if (isNaN(parsedCoordinate)) {
throw `Invalid position, integer expected but ${coordinate} was given`;
} else {
return parsedCoordinate;
}
});
return {
x: parsedCoordinates[0],
y: parsedCoordinates[1],
z: parsedCoordinates[2],
};
}
}
Controller.ACTION_SEND_CHAT_MESSAGE = "send chat message";
Controller.ACTION_SPAWN_PARTICLE_AT_POSITION = "spawn particle at position";
Controller.ACTIONS = {
[Controller.ACTION_SEND_CHAT_MESSAGE]: {
implementation: (message) => {
clientSystem.broadcastEvent("minecraft:display_chat_event", message);
},
paramsLength: 1,
},
[Controller.ACTION_SPAWN_PARTICLE_AT_POSITION]: {
implementation: (effect, position) => {
clientSystem.broadcastEvent("minecraft:spawn_particle_in_world", {
effect,
position: parsePosition(position),
});
},
paramsLength: 2,
},
};
class Parser {
parse(source) {
const eventSource = this.getEventSource(source);
const actionSource = source.substring(Parser.WHEN_PREFIX.length + eventSource.length + 2);
const parsed = {
event: this.parseSource(eventSource),
action: this.parseSource(actionSource),
};
//TODO: debug only
clientSystem.broadcastEvent("minecraft:display_chat_event", "parsed=" + JSON.stringify(parsed));
this.validateEvent(parsed.event);
this.validateAction(parsed.action);
return parsed;
}
getEventSource(source) {
if (!source.startsWith(Parser.WHEN_PREFIX)) {
throw `Parsing error, "${Parser.WHEN_PREFIX}" expected but not found`;
} else {
let quoted = false;
let eventSource = "";
for (let index = Parser.WHEN_PREFIX.length; index < source.length; index++) {
if (source[index] == Parser.QUOTE) {
quoted = !quoted;
eventSource += source[index];
} else if (source[index] == Parser.SEPERATOR) {
break;
} else {
eventSource += source[index];
}
}
return eventSource;
}
}
parseSource(source) {
let name = "";
let currentParam = "";
const params = [];
let quoted = false;
for (let index = 0; index < source.length; index++) {
if (source[index] == Parser.QUOTE) {
quoted = !quoted;
if (!quoted) {
params.push(currentParam.replace(/ +/g, " "));
currentParam = "";
}
} else {
if (quoted) {
currentParam += source[index];
} else {
const nextCharacterIsQuote = source.indexOf(Parser.QUOTE, index) == index + 1;
if (!nextCharacterIsQuote) {
name += source[index];
}
}
}
}
return {
name: name,
params,
};
}
validateEvent(event) {
if (!Controller.EVENTS[event.name]) {
throw `${event.name} is not recognised as an event`;
} else if (Controller.EVENTS[event.name].paramsLength != event.params.length) {
throw `${event.params.length} paramaters given for event ${event.name} but ${Controller.EVENTS[event.name].paramsLength} parameters were expected`;
}
}
validateAction(action) {
if (!Controller.ACTIONS[action.name]) {
throw `${action.name} is not recognised as an action`;
} else if (Controller.ACTIONS[action.name].paramsLength != action.params.length) {
throw `${action.params.length} paramaters given for action ${action.name} but ${Controller.ACTIONS[action.name].paramsLength} parameters were expected`;
}
}
}
Parser.WHEN_PREFIX = "when ";
Parser.SEPERATOR = ",";
Parser.QUOTE = "\"";
clientSystem.initialize = () => {
const mainController = new Controller();
clientSystem.listenForEvent("minecraft:client_entered_world", () => {
mainController.onPlayerEnterWorld();
});
clientSystem.listenForEvent("minecraft:hit_result_changed", (eventData) => {
mainController.onHitResultChanged(eventData);
});
const sources = [
//INSERT SOURCES HERE
"when player enters world, send chat message \"Hello world\"",
"when player enters world, spawn particle \"minecraft:mobflame_emitter\" at position \"45 70 170\"",
"when player looks at mob \"minecraft:pig\", send chat message \"Oink!\"",
"when player looks at mob \"minecraft:cow\", send chat message \"Moo!\"",
"when player looks at mob \"minecraft:creeper\", send chat message \"Run!\"",
];
const parser = new Parser();
sources.forEach((source) => {
const parsed = parser.parse(source);
mainController.add(parsed.event, parsed.action);
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment