Skip to content

Commit

Permalink
make all dimensions accessible from any script, regardless of the dim…
Browse files Browse the repository at this point in the history
…ension they have been loaded in
  • Loading branch information
Darmo117 committed Apr 27, 2023
1 parent 681ab62 commit 466799f
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 43 deletions.
7 changes: 6 additions & 1 deletion grammar/kate_syntax_highlighting/mccode.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@
<item>false</item>
</list>
<list name="builtin_constants">
<item>WORLD</item>
<item>DIMS</item>
<item>DIM</item>
<item>OVERWORLD</item>
<item>THE_NETHER</item>
<item>THE_END</item>

<item>INF</item>
<item>PI</item>
<item>E</item>
Expand Down
83 changes: 64 additions & 19 deletions src/main/java/net/darmo_creations/mccode/MCCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.darmo_creations.mccode.commands.argument_types.ModArgumentTypes;
import net.darmo_creations.mccode.interpreter.CallStackElement;
import net.darmo_creations.mccode.interpreter.ProgramManager;
import net.darmo_creations.mccode.interpreter.Utils;
import net.darmo_creations.mccode.interpreter.exceptions.ProgramErrorReport;
import net.darmo_creations.mccode.interpreter.exceptions.ProgramErrorReportElement;
import net.fabricmc.api.ModInitializer;
Expand All @@ -13,7 +14,6 @@
import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory;
import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
Expand All @@ -24,38 +24,54 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.*;

/**
* Mod’s common initializer
*/
public class MCCode implements ModInitializer {
public static final String MOD_ID = "mccode";
public static final Logger LOGGER = LoggerFactory.getLogger("MC Code");

public static final GameRules.Key<GameRules.BooleanRule> GR_SHOW_ERROR_MESSAGES =
GameRuleRegistry.register("show_mccode_error_messages", GameRules.Category.MISC, GameRuleFactory.createBooleanRule(true));
public static final GameRules.Key<GameRules.BooleanRule> GR_SHOW_ERROR_MESSAGES = GameRuleRegistry.register(
"show_mccode_error_messages",
GameRules.Category.MISC,
GameRuleFactory.createBooleanRule(true)
);

public static MCCode INSTANCE;
private static MCCode instance;

/**
* Return the instance of this mod.
*/
public static MCCode instance() {
return instance;
}

/**
* Map associating each world to their program managers.
*/
private final Map<World, ProgramManager> programManagers = new HashMap<>();
/**
* Queue to differ program managers’ loading until all dimensions have been loaded.
*/
private final List<ServerWorld> worldLoadQueue = new LinkedList<>();

@Override
public void onInitialize() {
instance = this;
ModArgumentTypes.registerAll();
LOGGER.info("[MC Code] Setting up…");
LOGGER.info("[MC Code] Setting up program manager");
LOGGER.info("Setting up…");
LOGGER.info("Setting up builtins…");
ProgramManager.declareDefaultBuiltinTypes();
ProgramManager.declareDefaultBuiltinFunctions();
ProgramManager.initialize();
LOGGER.info("[MC Code] Program manager setup done");
LOGGER.info("Builtins loaded");
ServerWorldEvents.LOAD.register((server, world) -> this.onWorldLoad(world));
ServerWorldEvents.UNLOAD.register((server, world) -> this.onWorldUnload(world));
ServerTickEvents.START_WORLD_TICK.register(this::onWorldTickStart);
ServerWorldEvents.LOAD.register(this::onWorldLoad);
this.registerCommands();
// Nasty hack, but don’t know how to do it any other way…
INSTANCE = this;
LOGGER.info("[MC Code] Setup done");
LOGGER.info("Setup done");
}

/**
Expand All @@ -72,19 +88,48 @@ public ProgramManager getProgramManager(final World world) {
* Registers all custom commands.
*/
private void registerCommands() {
LOGGER.info("Registering commands");
CommandRegistrationCallback.EVENT.register(
(dispatcher, registryAccess, environment) -> ProgramCommand.register(dispatcher));
}

private void onWorldLoad(MinecraftServer server, ServerWorld world) {
this.programManagers.put(world, world.getPersistentStateManager().getOrCreate(
compound -> new ProgramManager(world, compound),
() -> new ProgramManager(world),
"program_manager"
));
/**
* Queues a world to differ the loading of its program manager until all dimensions have loaded.
*/
private void onWorldLoad(final ServerWorld world) {
this.worldLoadQueue.add(world);
}

/**
* Removes the program manager of the given world, when it is unloaded, from the registry.
*/
private void onWorldUnload(final ServerWorld world) {
LOGGER.info("Unloading program manager of dimension " + Utils.getDimension(world));
this.programManagers.remove(world);
}

/**
* Run all active programs of the given world’s program manager for one tick.
* <p>
* If the world queue is not empty, the program managers of the contained worlds
* will be loaded and the queue emptied.
*/
private void onWorldTickStart(ServerWorld world) {
if (!this.worldLoadQueue.isEmpty()) {
// Load program managers of worlds that are in the queue
Iterator<ServerWorld> iterator = this.worldLoadQueue.iterator();
while (iterator.hasNext()) {
ServerWorld w = iterator.next();
LOGGER.info("Loading program manager of dimension " + Utils.getDimension(w));
this.programManagers.put(w, w.getPersistentStateManager().getOrCreate(
nbt -> new ProgramManager(w, nbt),
() -> new ProgramManager(w),
MOD_ID + ":program_manager"
));
iterator.remove();
}
}

ProgramManager programManager = this.programManagers.get(world);
// Log errors in chat and server console
for (ProgramErrorReport report : programManager.executePrograms()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
}

private static int loadAndRunProgram(CommandContext<ServerCommandSource> context, final boolean hasAlias, final boolean hasArgs) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
String programName = ProgramNameArgumentType.getName(context, PROGRAM_NAME_ARG);
String alias = hasAlias ? StringArgumentType.getString(context, PROGRAM_ALIAS_ARG) : null;
String[] args = hasArgs ? StringArgumentType.getString(context, PROGRAM_ARGUMENTS_ARG).split(" ") : new String[0];
Expand All @@ -138,7 +138,7 @@ private static int loadAndRunProgram(CommandContext<ServerCommandSource> context
}

private static int stopProgram(CommandContext<ServerCommandSource> context) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
String programName = ProgramNameArgumentType.getName(context, PROGRAM_NAME_ARG);
try {
pm.unloadProgram(programName);
Expand All @@ -152,7 +152,7 @@ private static int stopProgram(CommandContext<ServerCommandSource> context) {
}

private static int resetProgram(CommandContext<ServerCommandSource> context) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
String programName = ProgramNameArgumentType.getName(context, PROGRAM_NAME_ARG);
try {
pm.resetProgram(programName);
Expand All @@ -166,7 +166,7 @@ private static int resetProgram(CommandContext<ServerCommandSource> context) {
}

private static int pauseProgram(CommandContext<ServerCommandSource> context) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
String programName = ProgramNameArgumentType.getName(context, PROGRAM_NAME_ARG);
try {
pm.pauseProgram(programName);
Expand All @@ -180,7 +180,7 @@ private static int pauseProgram(CommandContext<ServerCommandSource> context) {
}

private static int listPrograms(CommandContext<ServerCommandSource> context) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
List<String> loadedPrograms = pm.getLoadedPrograms();
if (loadedPrograms.isEmpty()) {
context.getSource().sendError(Text.translatable("commands.program.error.no_loaded_programs"));
Expand All @@ -194,7 +194,7 @@ private static int listPrograms(CommandContext<ServerCommandSource> context) {
}

private static int getVariableValue(CommandContext<ServerCommandSource> context) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
String programName = ProgramNameArgumentType.getName(context, PROGRAM_NAME_ARG);
Optional<Program> program = pm.getProgram(programName);
if (program.isPresent()) {
Expand All @@ -217,7 +217,7 @@ private static int getVariableValue(CommandContext<ServerCommandSource> context)
}

private static int setVariableValue(CommandContext<ServerCommandSource> context) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
String programName = ProgramNameArgumentType.getName(context, PROGRAM_NAME_ARG);
Optional<Program> program = pm.getProgram(programName);
if (program.isPresent()) {
Expand Down Expand Up @@ -249,7 +249,7 @@ private static int setVariableValue(CommandContext<ServerCommandSource> context)
}

private static int deleteVariable(CommandContext<ServerCommandSource> context) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(context.getSource().getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(context.getSource().getWorld());
String programName = ProgramNameArgumentType.getName(context, PROGRAM_NAME_ARG);
Optional<Program> program = pm.getProgram(programName);
if (program.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public String parse(StringReader reader) {
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
S source = context.getSource();
if (source instanceof ServerCommandSource s) {
ProgramManager pm = MCCode.INSTANCE.getProgramManager(s.getWorld());
ProgramManager pm = MCCode.instance().getProgramManager(s.getWorld());
if (!this.loadedOnly) {
File dir = pm.getProgramsDirectory();
if (!dir.isDirectory()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> cont
S source = context.getSource();
if (source instanceof ServerCommandSource s) {
String programName = StringArgumentType.getString(context, ProgramCommand.PROGRAM_NAME_ARG);
Optional<Program> program = MCCode.INSTANCE.getProgramManager(s.getWorld()).getProgram(programName);
Optional<Program> program = MCCode.instance().getProgramManager(s.getWorld()).getProgram(programName);
if (program.isPresent()) {
Predicate<Variable> filter = switch (this.variableType) {
case ANY -> Variable::isPubliclyVisible;
Expand Down
24 changes: 22 additions & 2 deletions src/main/java/net/darmo_creations/mccode/interpreter/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import net.darmo_creations.mccode.interpreter.tags.StringListTag;
import net.darmo_creations.mccode.interpreter.tags.TagType;
import net.darmo_creations.mccode.interpreter.types.MCList;
import net.darmo_creations.mccode.interpreter.types.MCMap;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;

import java.util.*;
import java.util.stream.Collectors;
Expand All @@ -21,7 +24,11 @@
* The program’s state can be saved and restored and world loading and unloading.
*/
public class Program {
public static final String WORLD_VAR_NAME = "WORLD";
public static final String THIS_DIMENSION_VAR_NAME = "DIM";
public static final String OVERWORLD_VAR_NAME = "OVERWORLD";
public static final String THE_NETHER_VAR_NAME = "THE_NETHER";
public static final String THE_END_VAR_NAME = "THE_END";
public static final String DIMENSIONS_VAR_NAME = "DIMS";

public static final String NAME_KEY = "Name";
public static final String STATEMENTS_KEY = "Statements";
Expand Down Expand Up @@ -142,8 +149,21 @@ public Program(final CompoundTag tag, ProgramManager programManager) {
* Declare global variables.
*/
private void setup() {
this.scope.declareVariableGlobally(new Variable(WORLD_VAR_NAME, false, false, true,
// Worlds variables
this.scope.declareVariableGlobally(new Variable(THIS_DIMENSION_VAR_NAME, false, false, true,
false, this.programManager.getWorld()));
MinecraftServer server = this.programManager.getWorld().getServer();
this.scope.declareVariableGlobally(new Variable(OVERWORLD_VAR_NAME, false, false, true,
false, server.getWorld(World.OVERWORLD)));
this.scope.declareVariableGlobally(new Variable(THE_NETHER_VAR_NAME, false, false, true,
false, server.getWorld(World.NETHER)));
this.scope.declareVariableGlobally(new Variable(THE_END_VAR_NAME, false, false, true,
false, server.getWorld(World.END)));
MCMap dimensions = server.getWorldRegistryKeys().stream()
.collect(MCMap::new, (map, key) -> map.put(key.getValue().toString(), server.getWorld(key)), MCMap::putAll);
this.scope.declareVariableGlobally(new Variable(DIMENSIONS_VAR_NAME, false, false, true,
false, dimensions));
// Program arguments
this.scope.declareVariableGlobally(new Variable("$$", false, false, true, false, (long) this.args.size()));
this.scope.declareVariableGlobally(new Variable("$_", false, false, true, false, new MCList(this.args)));
for (int i = 0; i < this.args.size(); i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ public static TypeBase<?> getTypeForValue(final Object o) {
* Declare all default builtin types.
*/
public static void declareDefaultBuiltinTypes() {
MCCode.LOGGER.info("[MC Code] Loading default types");
MCCode.LOGGER.info("Loading default types");
declareType(AnyType.class);
declareType(NullType.class);
declareType(BooleanType.class);
Expand All @@ -499,7 +499,7 @@ public static void declareDefaultBuiltinTypes() {
declareType(FunctionType.class);
declareType(RangeType.class);
declareType(ModuleType.class);
MCCode.LOGGER.info("[MC Code] Default types loaded");
MCCode.LOGGER.info("Default types loaded");
}

/**
Expand All @@ -511,7 +511,7 @@ public static void declareDefaultBuiltinTypes() {
*/
public static <T extends TypeBase<?>> void declareType(final Class<T> typeClass) throws TypeException {
ensureNotInitialized();
MCCode.LOGGER.info("[MC Code] Found type wrapper %s class".formatted(typeClass.getSimpleName()));
MCCode.LOGGER.info("Found type wrapper %s class".formatted(typeClass.getSimpleName()));

T type;
try {
Expand Down Expand Up @@ -804,7 +804,7 @@ public static BuiltinFunction getBuiltinFunction(final String name) {
* Declare all default builtin functions.
*/
public static void declareDefaultBuiltinFunctions() {
MCCode.LOGGER.info("[MC Code] Loading default builtin functions");
MCCode.LOGGER.info("Loading default builtin functions");
declareBuiltinFunction(AbsFunction.class);
declareBuiltinFunction(AcosFunction.class);
declareBuiltinFunction(AllFunction.class);
Expand Down Expand Up @@ -877,7 +877,7 @@ public Object apply(final Scope scope, CallStack callStack) {
FUNCTIONS.put(name, function);
}
}
MCCode.LOGGER.info("[MC Code] Default builtin functions loaded");
MCCode.LOGGER.info("Default builtin functions loaded");
}

/**
Expand All @@ -887,7 +887,7 @@ public Object apply(final Scope scope, CallStack callStack) {
*/
public static void declareBuiltinFunction(final Class<? extends BuiltinFunction> functionClass) {
ensureNotInitialized();
MCCode.LOGGER.info("[MC Code] Found builtin function %s class".formatted(functionClass.getSimpleName()));
MCCode.LOGGER.info("Found builtin function %s class".formatted(functionClass.getSimpleName()));

Function functionAnnotation = functionClass.getAnnotation(Function.class);
if (functionAnnotation == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public Object apply(final Scope scope, CallStack callStack) {
.map(o -> ProgramManager.getTypeForValue(o).toString(o))
.collect(Collectors.joining(" "));
String dimension = Utils.getDimension(scope.getProgram().getProgramManager().getWorld());
server.sendMessage(Text.literal("[MCCode:%s][%s] %s".formatted(program.getName(), dimension, text)));
server.sendMessage(Text.literal("[MCCode:Program %s in %s] %s".formatted(program.getName(), dimension, text)));
return null;
}
}
Loading

0 comments on commit 466799f

Please sign in to comment.