diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index baed6c5d..5447429a 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -2932,10 +2932,13 @@ Collection of other methods that control smaller, yet still important aspects of ## Sounds -### `sound(name, pos, volume?, pitch?)` +### `sound(name, pos, volume?, pitch?, mixer?)` -Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`. `pos` can be -either a block, triple of coords, or a list of thee numbers. Uses the same options as a corresponding `playsound` command. +Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under +optional `mixer`. Default values for `volume`, `pitch` and `mixer` are `1.0`, `1.0`, and `master`. +Valid mixer options are `master`, `music`, `record`, `weather`, `block`, `hostile`,`neutral`, `player`, `ambient` +and `voice`. `pos` can be either a block, triple of coords, or a list of thee numbers. Uses the same options as a + corresponding `playsound` command. ## Particles @@ -3004,23 +3007,52 @@ run('give @s stone 4') -> 1 // this operation was successful once Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to performance reasons and saves the world only on demand. -### `load_app_data(), load_app_data(file)` +### `load_app_data(), load_app_data(file), load_app_data(file, shared?)` Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the -scripts folder. +scripts folder that belongs exclusively to the app. if `shared` is true, the file location is not exclusive +to the app anymore, but located in a shared app space. + +File descriptor can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped +from the name before saving/loading. Empty descriptors are invalid. + +Function returns nbt value with the file content, or `null` if the file is missing or there were problems +with retrieving the data. + +The default no-name app, via `/script run` command can only save/load file from the shared data location. + +If the app's name is `'foo'`, the script location would +be `world/scripts/foo.sc`, system-managed default app data storage is in `world/scripts/foo.data.nbt`, app +specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and shared data space is under +`world/scripts/shared/bar/../baz.nbt`. You can use app data to save non-vanilla information separately from the world and other scripts. -### `store_app_data(tag), store_app_data(tag, file)` +### `store_app_data(tag), store_app_data(tag, file), store_app_data(tag, file, shared?)` -Stores the app data associated with the app from the world /scripts folder. With the `file` parameter saves -immediately and with every call, without `file` parameter, it may take up to 10 seconds for the output file +Stores the app data associated with the app from the world `/scripts` folder. With the `file` parameter saves +immediately and with every call to a specific file defined by the `file`, either in app space, or in the scripts +shared space if `shared` is true. Without `file` parameter, it may take up to 10 + seconds for the output file to sync preventing flickering in case this tag changes frequently. It will be synced when server closes. +Returns `true` if the file was saved successfully, `false` otherwise. + +Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`. + ### `tick_time()` -Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time +Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time. + +### `world_time()` + +Returns dimension-specific tick counter. + +### `day_time(new_time?)` + +Returns current daytime clock value. If `new_time` is specified, sets a new clock +to that value. Daytime clocks are shared between all dimensions. ### `game_tick(mstime?)` @@ -3071,7 +3103,8 @@ For the options of `entry`, consult your statistics page, or give it a guess. The call will return `null` if the statistics options are incorrect, or player didn't get these in their history. If the player encountered the statistic, or game created for him empty one, it will return a number. Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response -it could either mean your input is wrong, or statistic has effectively a value of `0`.# `/script run` command +it could either mean your input is wrong, or statistic has effectively a value of `0`. +# `/script run` command Primary way to input commands. The command executes in the context, position, and dimension of the executing player, commandblock, etc... The command receives 4 variables, `x`, `y`, `z` and `p` indicating position and diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index 17b1d003..52941068 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -4,10 +4,13 @@ Collection of other methods that control smaller, yet still important aspects of ## Sounds -### `sound(name, pos, volume?, pitch?)` +### `sound(name, pos, volume?, pitch?, mixer?)` -Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`. `pos` can be -either a block, triple of coords, or a list of thee numbers. Uses the same options as a corresponding `playsound` command. +Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under +optional `mixer`. Default values for `volume`, `pitch` and `mixer` are `1.0`, `1.0`, and `master`. +Valid mixer options are `master`, `music`, `record`, `weather`, `block`, `hostile`,`neutral`, `player`, `ambient` +and `voice`. `pos` can be either a block, triple of coords, or a list of thee numbers. Uses the same options as a + corresponding `playsound` command. ## Particles @@ -76,23 +79,52 @@ run('give @s stone 4') -> 1 // this operation was successful once Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to performance reasons and saves the world only on demand. -### `load_app_data(), load_app_data(file)` +### `load_app_data(), load_app_data(file), load_app_data(file, shared?)` Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the -scripts folder. +scripts folder that belongs exclusively to the app. if `shared` is true, the file location is not exclusive +to the app anymore, but located in a shared app space. + +File descriptor can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped +from the name before saving/loading. Empty descriptors are invalid. + +Function returns nbt value with the file content, or `null` if the file is missing or there were problems +with retrieving the data. + +The default no-name app, via `/script run` command can only save/load file from the shared data location. + +If the app's name is `'foo'`, the script location would +be `world/scripts/foo.sc`, system-managed default app data storage is in `world/scripts/foo.data.nbt`, app +specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and shared data space is under +`world/scripts/shared/bar/../baz.nbt`. You can use app data to save non-vanilla information separately from the world and other scripts. -### `store_app_data(tag), store_app_data(tag, file)` +### `store_app_data(tag), store_app_data(tag, file), store_app_data(tag, file, shared?)` -Stores the app data associated with the app from the world /scripts folder. With the `file` parameter saves -immediately and with every call, without `file` parameter, it may take up to 10 seconds for the output file +Stores the app data associated with the app from the world `/scripts` folder. With the `file` parameter saves +immediately and with every call to a specific file defined by the `file`, either in app space, or in the scripts +shared space if `shared` is true. Without `file` parameter, it may take up to 10 + seconds for the output file to sync preventing flickering in case this tag changes frequently. It will be synced when server closes. +Returns `true` if the file was saved successfully, `false` otherwise. + +Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`. + ### `tick_time()` -Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time +Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time. + +### `world_time()` + +Returns dimension-specific tick counter. + +### `day_time(new_time?)` + +Returns current daytime clock value. If `new_time` is specified, sets a new clock +to that value. Daytime clocks are shared between all dimensions. ### `game_tick(mstime?)` @@ -143,4 +175,4 @@ For the options of `entry`, consult your statistics page, or give it a guess. The call will return `null` if the statistics options are incorrect, or player didn't get these in their history. If the player encountered the statistic, or game created for him empty one, it will return a number. Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response -it could either mean your input is wrong, or statistic has effectively a value of `0`. \ No newline at end of file +it could either mean your input is wrong, or statistic has effectively a value of `0`. diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index c98522d1..31aa5e6f 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -13,6 +13,7 @@ brightness chunk_tickets create_marker current_dimension +day_time destroy diamond drop_item @@ -95,4 +96,5 @@ top transparent update volume -without_updates \ No newline at end of file +without_updates +world_time \ No newline at end of file diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 8c06f293..6f5366e0 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -67,7 +67,7 @@ logger sound particle particle_line particle_rect - run save tick_time game_tick seed current_dimension in_dimension + run save tick_time world_time day_time game_tick seed current_dimension in_dimension plop harvest destroy schedule nbt create_marker remove_all_markers load_app_data store_app_data escape_nbt statistic diff --git a/src/main/java/carpet/commands/TickCommand.java b/src/main/java/carpet/commands/TickCommand.java index ac65b7bd..cc7dc38c 100644 --- a/src/main/java/carpet/commands/TickCommand.java +++ b/src/main/java/carpet/commands/TickCommand.java @@ -43,7 +43,8 @@ public class TickCommand c.getSource(), getInteger(c,"ticks"), getString(c, "tail command")))))). - then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource()))). + then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource(), false)). + then(literal("deep").executes( (c)-> toggleFreeze(c.getSource(), true)))). then(literal("step"). executes((c) -> step(1)). then(argument("ticks", integer(1,72000)). @@ -95,15 +96,18 @@ public class TickCommand return 1; } - private static int toggleFreeze(ServerCommandSource source) + private static int toggleFreeze(ServerCommandSource source, boolean isDeep) { TickSpeed.is_paused = !TickSpeed.is_paused; if (TickSpeed.is_paused) { - Messenger.m(source, "gi Game is paused"); + TickSpeed.deepFreeze = isDeep; + Messenger.m(source, "gi Game is "+(isDeep?"deeply ":"")+"frozen"); + } else { + TickSpeed.deepFreeze = false; Messenger.m(source, "gi Game runs normally"); } return 1; diff --git a/src/main/java/carpet/helpers/TickSpeed.java b/src/main/java/carpet/helpers/TickSpeed.java index 79d05576..2daf4fd1 100644 --- a/src/main/java/carpet/helpers/TickSpeed.java +++ b/src/main/java/carpet/helpers/TickSpeed.java @@ -25,6 +25,7 @@ public class TickSpeed public static ServerCommandSource tick_warp_sender = null; public static int player_active_timeout = 0; public static boolean process_entities = true; + public static boolean deepFreeze = false; public static boolean is_paused = false; public static boolean is_superHot = false; diff --git a/src/main/java/carpet/logging/logHelpers/TNTLogHelper.java b/src/main/java/carpet/logging/logHelpers/TNTLogHelper.java index 7e51f743..b968d0b4 100644 --- a/src/main/java/carpet/logging/logHelpers/TNTLogHelper.java +++ b/src/main/java/carpet/logging/logHelpers/TNTLogHelper.java @@ -9,6 +9,8 @@ public class TNTLogHelper { public boolean initialized; private double primedX, primedY, primedZ; + private static long lastGametime = 0; + private static int tntCount = 0; private Vec3d primedAngle; /** * Runs when the TNT is primed. Expects the position and motion angle of the TNT. @@ -24,8 +26,13 @@ public class TNTLogHelper /** * Runs when the TNT explodes. Expects the position of the TNT. */ - public void onExploded(double x, double y, double z) + public void onExploded(double x, double y, double z, long gametime) { + if (!(lastGametime == gametime)){ + tntCount = 0; + lastGametime = gametime; + } + tntCount++; LoggerRegistry.getLogger("tnt").log( (option) -> { switch (option) { @@ -36,6 +43,10 @@ public class TNTLogHelper "r E ",Messenger.dblt("r",x, y, z))}; case "full": return new BaseText[]{Messenger.c( + "g #" + String.valueOf(tntCount), + "m @" + String.valueOf(gametime), + "g : ", + "g #" + String.valueOf(tntCount) + ": ", "l P ",Messenger.dblf("l",primedX,primedY,primedZ), "w ",Messenger.dblf("l", primedAngle.x, primedAngle.y, primedAngle.z), "r E ",Messenger.dblf("r",x, y, z))}; diff --git a/src/main/java/carpet/mixins/ChunkTicketManager_tickMixin.java b/src/main/java/carpet/mixins/ChunkTicketManager_tickMixin.java new file mode 100644 index 00000000..9b70b707 --- /dev/null +++ b/src/main/java/carpet/mixins/ChunkTicketManager_tickMixin.java @@ -0,0 +1,23 @@ +package carpet.mixins; + +import carpet.helpers.TickSpeed; +import net.minecraft.server.world.ChunkTicketManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChunkTicketManager.class) +public class ChunkTicketManager_tickMixin +{ + @Shadow private long age; + + @Inject(method = "purge", at = @At("HEAD"), cancellable = true) + private void pauseTicketSystem(CallbackInfo ci) + { + // pausing expiry of tickets + // that will prevent also chunks from unloading, so require a deep frozen state + if (!TickSpeed.process_entities && TickSpeed.deepFreeze) age--; + } +} diff --git a/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java b/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java index 7ea3d2fb..98d1136d 100644 --- a/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java +++ b/src/main/java/carpet/mixins/MinecraftServer_tickspeedMixin.java @@ -117,7 +117,7 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec this.startMonitor(tickDurationMonitor); this.profiler.startTick(); this.profiler.push("tick"); - this.tick(TickSpeed.time_warp_start_time != 0 ? ()->true : this::shouldKeepTicking); + this.tick(TickSpeed.time_warp_start_time != 0 ? ()->!hasRunningTasks() : this::shouldKeepTicking); this.profiler.swap("nextTickWait"); this.waitingForNextTick = true; this.field_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference); diff --git a/src/main/java/carpet/mixins/TntEntityMixin.java b/src/main/java/carpet/mixins/TntEntityMixin.java index 4a296389..2e67b1e5 100644 --- a/src/main/java/carpet/mixins/TntEntityMixin.java +++ b/src/main/java/carpet/mixins/TntEntityMixin.java @@ -72,7 +72,7 @@ public abstract class TntEntityMixin extends Entity implements TntEntityInterfac private void onExplode(CallbackInfo ci) { if (LoggerRegistry.__tnt && logHelper != null) - logHelper.onExploded(getX(), getY(), getZ()); + logHelper.onExploded(getX(), getY(), getZ(), this.world.getTime()); if (mergedTNT > 1) for (int i = 0; i < mergedTNT - 1; i++) diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index cb1789b1..56f6b2af 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -131,6 +131,7 @@ import java.util.Optional; import java.util.UUID; import java.util.function.BiFunction; import java.util.function.Predicate; +import java.util.stream.Collector; import java.util.stream.Collectors; import static carpet.script.utils.WorldTools.canHasChunk; @@ -2377,6 +2378,8 @@ public class CarpetExpression }); } + private static Map mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k)); + private void API_AuxiliaryAspects() { this.expr.addLazyFunction("sound", -1, (c, t, lv) -> { @@ -2387,12 +2390,19 @@ public class CarpetExpression throw new InternalExpressionException("No such sound: "+soundName.getPath()); float volume = 1.0F; float pitch = 1.0F; + SoundCategory mixer = SoundCategory.MASTER; if (lv.size() > 0+locator.offset) { volume = (float) NumericValue.asNumber(lv.get(0+locator.offset).evalValue(c)).getDouble(); if (lv.size() > 1+locator.offset) { pitch = (float) NumericValue.asNumber(lv.get(1+locator.offset).evalValue(c)).getDouble(); + if (lv.size() > 2+locator.offset) + { + String mixerName = lv.get(2+locator.offset).evalValue(c).getString(); + mixer = mixerMap.get(mixerName.toLowerCase(Locale.ROOT)); + if (mixer == null) throw new InternalExpressionException(mixerName +" is not a valid mixer name"); + } } } Vec3d vec = locator.vec; @@ -2401,7 +2411,7 @@ public class CarpetExpression for (ServerPlayerEntity player : cc.s.getWorld().getPlayers( (p) -> p.squaredDistanceTo(vec) < d0)) { count++; - player.networkHandler.sendPacket(new PlaySoundIdS2CPacket(soundName, SoundCategory.PLAYERS, vec, volume, pitch)); + player.networkHandler.sendPacket(new PlaySoundIdS2CPacket(soundName, mixer, vec, volume, pitch)); } int totalPlayed = count; return (_c, _t) -> new NumericValue(totalPlayed); @@ -2658,6 +2668,24 @@ public class CarpetExpression return (cc, tt) -> time; }); + this.expr.addLazyFunction("world_time", 0, (c, t, lv) -> + { + Value time = new NumericValue(((CarpetContext) c).s.getWorld().getTime()); + return (cc, tt) -> time; + }); + + this.expr.addLazyFunction("day_time", -1, (c, t, lv) -> + { + Value time = new NumericValue(((CarpetContext) c).s.getWorld().getTimeOfDay()); + if (lv.size() > 0) + { + long newTime = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong(); + if (newTime < 0) newTime = 0; + ((CarpetContext) c).s.getWorld().setTimeOfDay(newTime); + } + return (cc, tt) -> time; + }); + this.expr.addLazyFunction("game_tick", -1, (c, t, lv) -> { ServerCommandSource s = ((CarpetContext)c).s; if (!s.getMinecraftServer().isOnThread()) throw new InternalExpressionException("Unable to run ticks from threads"); @@ -2794,16 +2822,22 @@ public class CarpetExpression this.expr.addLazyFunction("load_app_data", -1, (c, t, lv) -> { String file = null; + boolean shared = false; if (lv.size()>0) { String origfile = lv.get(0).evalValue(c).getString(); - file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9]", ""); + file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9/]", ""); + file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/")); if (file.isEmpty()) { - throw new InternalExpressionException("Cannot use "+file+" as resource name - must have some letters and numbers"); + throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have at least some letters and numbers"); + } + if (lv.size() > 1) + { + shared = lv.get(1).evalValue(c).getBoolean(); } } - Tag state = ((CarpetScriptHost)((CarpetContext)c).host).getGlobalState(file); + Tag state = ((CarpetScriptHost)((CarpetContext)c).host).getGlobalState(file, shared); if (state == null) return (cc, tt) -> Value.NULL; Value retVal = new NBTSerializableValue(state); @@ -2816,21 +2850,27 @@ public class CarpetExpression throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file"); Value val = lv.get(0).evalValue(c); String file = null; + boolean shared = false; if (lv.size()>1) { String origfile = lv.get(1).evalValue(c).getString(); - file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9]", ""); + file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9/]", ""); + file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/")); if (file.isEmpty()) { - throw new InternalExpressionException("Cannot use "+file+" as resource name - must have some letters and numbers"); + throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have some letters and numbers"); + } + if (lv.size() > 2) + { + shared = lv.get(2).evalValue(c).getBoolean(); } } NBTSerializableValue tagValue = (val instanceof NBTSerializableValue) ? (NBTSerializableValue) val : new NBTSerializableValue(val.getString()); Tag tag = tagValue.getTag(); - ((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file); - return (cc, tt) -> Value.NULL; + boolean success = ((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file, shared); + return success?LazyValue.TRUE:LazyValue.FALSE; }); this.expr.addLazyFunction("statistic", 3, (c, t, lv) -> diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index 0b1a5eb7..c6a8afa8 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -361,32 +361,31 @@ public class CarpetScriptHost extends ScriptHost private void dumpState() { - main.saveData(null, globalState); + Module.saveData(main, null, globalState, false); } private Tag loadState() { - return main.getData(null); + return Module.getData(main, null, false); } - public Tag getGlobalState(String file) + public Tag getGlobalState(String file, boolean isShared) { - if (getName() == null ) return null; + if (getName() == null && !isShared) return null; if (file != null) - return main.getData(file); + return Module.getData(main, file, isShared); if (parent == null) return globalState; return ((CarpetScriptHost)parent).globalState; } - public void setGlobalState(Tag tag, String file) + public boolean setGlobalState(Tag tag, String file, boolean isShared) { - if (getName() == null ) return; + if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host. if (file!= null) { - main.saveData(file, tag); - return; + return Module.saveData(main, file, tag, isShared); } CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this; @@ -396,6 +395,7 @@ public class CarpetScriptHost extends ScriptHost responsibleHost.dumpState(); responsibleHost.saveTimeout = 200; } + return true; } public void tick() diff --git a/src/main/java/carpet/script/bundled/FileModule.java b/src/main/java/carpet/script/bundled/FileModule.java index c9ff21e1..ef44a292 100644 --- a/src/main/java/carpet/script/bundled/FileModule.java +++ b/src/main/java/carpet/script/bundled/FileModule.java @@ -74,7 +74,7 @@ public class FileModule extends Module } //copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write - public static void write(Tag tag_1, File file) + public static boolean write(Tag tag_1, File file) { File file_2 = new File(file.getAbsolutePath() + "_tmp"); if (file_2.exists()) file_2.delete(); @@ -90,9 +90,10 @@ public class FileModule extends Module } catch (IOException e) { - return; + return false; } if (file.exists()) file.delete(); if (!file.exists()) file_2.renameTo(file); + return true; } } diff --git a/src/main/java/carpet/script/bundled/Module.java b/src/main/java/carpet/script/bundled/Module.java index a5543129..575b7730 100644 --- a/src/main/java/carpet/script/bundled/Module.java +++ b/src/main/java/carpet/script/bundled/Module.java @@ -4,7 +4,6 @@ import carpet.CarpetServer; import net.minecraft.nbt.Tag; import java.io.File; -import java.io.IOException; import java.nio.file.Files; import static carpet.script.bundled.FileModule.read; @@ -16,28 +15,36 @@ public abstract class Module public abstract String getCode(); public abstract boolean isLibrary(); - public Tag getData(String file) + private static String getDescriptor(Module module, String file, boolean isShared) { - File dataFile = CarpetServer.minecraft_server.method_27050().resolve("scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt").toFile(); + if (isShared) + { + return "shared/"+file; + } + else if (module != null && module.getName() != null) // appdata + { + return module.getName()+".data"+(file==null?"":"/"+file); + } + else + { + throw new RuntimeException("Invalid file descriptor: "+file); + } + } + + public static Tag getData(Module module, String file, boolean isShared) + { + if (!isShared && (module == null || module.getName() == null)) return null; + File dataFile = CarpetServer.minecraft_server.method_27050().resolve("scripts/"+getDescriptor(module, file, isShared)+".nbt").toFile(); if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null; return read(dataFile); } - public void saveData(String file, Tag globalState) + public static boolean saveData(Module module, String file, Tag globalState, boolean isShared) { - File dataFile =CarpetServer.minecraft_server.method_27050().resolve("scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt").toFile(); - if (!Files.exists(dataFile.toPath().getParent())) - { - try - { - Files.createDirectory(dataFile.toPath().getParent()); - } - catch (IOException ignored) - { - } - } - write(globalState, dataFile); - + if (!isShared && (module == null || module.getName() == null)) return false; + File dataFile =CarpetServer.minecraft_server.method_27050().resolve("scripts/"+getDescriptor(module, file, isShared)+".nbt").toFile(); + if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false; + return write(globalState, dataFile); } @Override diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index a11c3601..a69f0b80 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -25,6 +25,7 @@ "ServerWorld_tickMixin", "ServerChunkManager_tickMixin", "ThreadedAnvilChunkStorage_tickMixin", + "ChunkTicketManager_tickMixin", "ThreadedAnvilChunkStorage_scarpetChunkCreationMixin", "ChunkHolder_scarpetChunkCreationMixin",