diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md
index 717784c7..0e1a614e 100644
--- a/docs/scarpet/Full.md
+++ b/docs/scarpet/Full.md
@@ -3154,25 +3154,23 @@ Optional shared shape attributes:
in the form of `0xRRGGBBAA`, with the default of `-1`, so white opaque, or `0xFFFFFFFF`.
* `player` - name or player entity to send the shape to. If specified, the shapes will appear only for the specified
player, otherwise it will be send to all players in the dimension.
+ * `width` - line thickness, defaults to 2.0pt
+ * `fill` - color for the faces, defaults to no fill. Use `color` attribute format
+ * `follow` - entity, or player name. Shape will follow an entity instead of being static.
+ Follow attribute requires all positional arguments to be relative to the entity and disallow
+ of using entity or block as position markers. You must specify positions as a triple.
Available shapes:
* `'line'` - draws a straight line between two points
* Required attributes:
* `from` - triple coordinates, entity, or block value indicating one end of the line
* `to` - other end of the line, same format as `from`
- * Optional attributes:
- * `width` - line thickness, defaults to 2.0pt
- * `follow` - entity, or player name. Shape will follow an entity instead of being static.
- Follow attribute requires all positional arguments to be relative to the entity and disallow
- of using entity or block as position markers. You must specify positions as a triple.
* `'box'` - draws a box with corners in specified points
* Required attributes:
* `from` - triple coordinates, entity, or block value indicating one corner of the box
* `to` - other corner, same format as `from`
- * Optional attributes:
- * `width` - mesh line thickness, defaults to 2.0pt
- * `fill` - color for the box faces, defaults to no fill, use shared color attribute format
+
* `'sphere'` - draws a sphere
* Required attributes:
* `center` - center of the sphere
@@ -3180,7 +3178,6 @@ Available shapes:
* Optional attributes:
* `level` - level of details, or grid size. The more the denser your sphere. Default level of 0, means that the
level of detail will be selected automatically based on radius.
- * `width` - line thickness
### `create_marker(text, pos, rotation?, block?)`
@@ -3269,6 +3266,63 @@ Example usages:
Prints the message to system logs, and not to chat.
+### read_file(resource, type)
+### delete_file(resource, type)
+### write_file(resource, type, data, ...)
+
+With the specified `resource` in the scripts folder, of a specific `type`, writes/appends `data` to it, reads its
+ content, or deletes the resource.
+
+Resource is identified by a path to the file.
+A path can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped
+from the name. Empty descriptors are invalid. Do not add file extensions to the descriptor - extensions are inferred
+based on the `type` of the file.
+
+Resources can be located in the app specific space, or a shared space for all the apps. Accessing of app-specific
+resources is guaranteed to be isolated from other apps. Shared resources are... well, shared across all apes, meaning
+they can eat of each others file, however all access to files is synchronized, and files are never left open, so
+this should not lead to any access problems.
+
+If the app's name is `'foo'`, the script location would
+be `world/scripts/foo.sc`, app
+specific data directory is under `world/scripts/foo.data/...`, and shared data space is under
+`world/scripts/shared/...`.
+
+The default no-name app, via `/script run` command can only save/load/read files from the shared space.
+
+Functions return `null` if an error is encounter or no file is present (for read and delete operations). Returns `true`
+for success writes and deletes, and requested data, based on the file type, for read operations.
+
+NBT files can be written once as they an store one tag at a time. Consecutive writes will overwrite previous data.
+
+Write operations to text files always result in appending to the existing file, so consecutive writes will increase
+the size of the file and add data to it. Since files are closed after each write, sending multiple lines of data to
+write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
+third argument.
+ * `write_file('temp', 'text', 'foo', 'bar', 'baz')` or
+ * write_file('temp', 'text', l('foo', 'bar', 'baz'))
+
+To log a single line of string
+
+Supported values for resource `type` is:
+ * `nbt` - NBT tag
+ * `text` - text resource with automatic newlines added
+ * `raw` - text resource without implied newlines
+ * `shared_nbt`, `shared_text`, `shared_raw` - shared versions of the above
+
+NBT files have extension `.nbt`, store one NBT tag, and return a NBT type value. Text files have `.txt` extension,
+stores multiple lines of text and returns lists of all lines from the file. With `write_file`, multiple lines can be
+sent to the file at once. The only difference between `raw` and `text` types are automatic newlines added after each
+record to the file.
+
+
+write_file('foo', 'shared_text, l('one', 'two'));
+write_file('foo', 'shared_text', 'three\n', 'four\n');
+write_file('foo', 'shared_raw', 'five\n', 'six\n');
+
+read_file('foo', 'shared_text') => ['one', 'two', 'three', '', 'four', '', 'five', 'six']
+
+
### `run(expr)`
Runs a vanilla command from the string result of the `expr` and returns its success count
@@ -3283,7 +3337,10 @@ 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(file, shared?)`
+### `load_app_data()`
+
+NOTE: usages with arguments, so `load_app_data(file)` and `load_app_data(file, shared?)` are deprecated.
+Use `read_file` instead.
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
@@ -3291,7 +3348,7 @@ scripts folder that belongs exclusively to the app. if `shared` is true, the fil
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.
+from the name before saving/loading. Empty descriptors are invalid. Do not add file extensions to the descriptor
Function returns nbt value with the file content, or `null` if the file is missing or there were problems
with retrieving the data.
@@ -3305,7 +3362,9 @@ specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and sh
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, file, shared?)`
+### `store_app_data(tag)`
+
+Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
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
diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md
index b55c9f5a..d4de9940 100644
--- a/docs/scarpet/api/Auxiliary.md
+++ b/docs/scarpet/api/Auxiliary.md
@@ -172,6 +172,63 @@ Example usages:
Prints the message to system logs, and not to chat.
+### read_file(resource, type)
+### delete_file(resource, type)
+### write_file(resource, type, data, ...)
+
+With the specified `resource` in the scripts folder, of a specific `type`, writes/appends `data` to it, reads its
+ content, or deletes the resource.
+
+Resource is identified by a path to the file.
+A path can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped
+from the name. Empty descriptors are invalid. Do not add file extensions to the descriptor - extensions are inferred
+based on the `type` of the file.
+
+Resources can be located in the app specific space, or a shared space for all the apps. Accessing of app-specific
+resources is guaranteed to be isolated from other apps. Shared resources are... well, shared across all apes, meaning
+they can eat of each others file, however all access to files is synchronized, and files are never left open, so
+this should not lead to any access problems.
+
+If the app's name is `'foo'`, the script location would
+be `world/scripts/foo.sc`, app
+specific data directory is under `world/scripts/foo.data/...`, and shared data space is under
+`world/scripts/shared/...`.
+
+The default no-name app, via `/script run` command can only save/load/read files from the shared space.
+
+Functions return `null` if an error is encounter or no file is present (for read and delete operations). Returns `true`
+for success writes and deletes, and requested data, based on the file type, for read operations.
+
+NBT files can be written once as they an store one tag at a time. Consecutive writes will overwrite previous data.
+
+Write operations to text files always result in appending to the existing file, so consecutive writes will increase
+the size of the file and add data to it. Since files are closed after each write, sending multiple lines of data to
+write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
+third argument.
+ * `write_file('temp', 'text', 'foo', 'bar', 'baz')` or
+ * write_file('temp', 'text', l('foo', 'bar', 'baz'))
+
+To log a single line of string
+
+Supported values for resource `type` is:
+ * `nbt` - NBT tag
+ * `text` - text resource with automatic newlines added
+ * `raw` - text resource without implied newlines
+ * `shared_nbt`, `shared_text`, `shared_raw` - shared versions of the above
+
+NBT files have extension `.nbt`, store one NBT tag, and return a NBT type value. Text files have `.txt` extension,
+stores multiple lines of text and returns lists of all lines from the file. With `write_file`, multiple lines can be
+sent to the file at once. The only difference between `raw` and `text` types are automatic newlines added after each
+record to the file.
+
+
+write_file('foo', 'shared_text, l('one', 'two'));
+write_file('foo', 'shared_text', 'three\n', 'four\n');
+write_file('foo', 'shared_raw', 'five\n', 'six\n');
+
+read_file('foo', 'shared_text') => ['one', 'two', 'three', '', 'four', '', 'five', 'six']
+
+
### `run(expr)`
Runs a vanilla command from the string result of the `expr` and returns its success count
@@ -186,7 +243,10 @@ 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(file, shared?)`
+### `load_app_data()`
+
+NOTE: usages with arguments, so `load_app_data(file)` and `load_app_data(file, shared?)` are deprecated.
+Use `read_file` instead.
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
@@ -194,7 +254,7 @@ scripts folder that belongs exclusively to the app. if `shared` is true, the fil
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.
+from the name before saving/loading. Empty descriptors are invalid. Do not add file extensions to the descriptor
Function returns nbt value with the file content, or `null` if the file is missing or there were problems
with retrieving the data.
@@ -208,7 +268,9 @@ specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and sh
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, file, shared?)`
+### `store_app_data(tag)`
+
+Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
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
diff --git a/src/main/java/carpet/CarpetServer.java b/src/main/java/carpet/CarpetServer.java
index 44f0e91b..c024b3f9 100644
--- a/src/main/java/carpet/CarpetServer.java
+++ b/src/main/java/carpet/CarpetServer.java
@@ -57,7 +57,7 @@ public class CarpetServer // static for now - easier to handle all around the co
if (sm != null) sm.attachServer(server);
e.onServerLoaded(server);
});
- scriptServer = new CarpetScriptServer();
+ scriptServer = new CarpetScriptServer(server);
scriptServer.loadAllWorldScripts();
MobAI.resetTrackers();
LoggerRegistry.initLoggers();
diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java
index 7ffb206c..c39065b6 100644
--- a/src/main/java/carpet/CarpetSettings.java
+++ b/src/main/java/carpet/CarpetSettings.java
@@ -498,8 +498,8 @@ public class CarpetSettings
if (server.isDedicated())
{
int vd = (newValue >= 2)?newValue:((DedicatedServer) server).getProperties().viewDistance;
- if (vd != CarpetServer.minecraft_server.getPlayerManager().getViewDistance())
- CarpetServer.minecraft_server.getPlayerManager().setViewDistance(vd);
+ if (vd != server.getPlayerManager().getViewDistance())
+ server.getPlayerManager().setViewDistance(vd);
return newValue;
}
else
@@ -546,6 +546,7 @@ public class CarpetSettings
//must been some startup thing
return newValue;
}
+ if (CarpetServer.minecraft_server == null) return newValue;
ServerWorld currentOverworld = CarpetServer.minecraft_server.getWorld(World.OVERWORLD); // OW
if (currentOverworld != null)
{
diff --git a/src/main/java/carpet/commands/SpawnCommand.java b/src/main/java/carpet/commands/SpawnCommand.java
index 63ba9752..7b5064df 100644
--- a/src/main/java/carpet/commands/SpawnCommand.java
+++ b/src/main/java/carpet/commands/SpawnCommand.java
@@ -16,6 +16,7 @@ import net.minecraft.command.arguments.DimensionArgumentType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.command.ServerCommandSource;
+import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.DyeColor;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.RegistryKey;
@@ -197,7 +198,7 @@ public class SpawnCommand
TickSpeed.tickrate_advance(null, 0, null, null);
// tick warp given player
ServerCommandSource csource = null;
- PlayerEntity player = null;
+ ServerPlayerEntity player = null;
try
{
player = source.getPlayer();
diff --git a/src/main/java/carpet/commands/TickCommand.java b/src/main/java/carpet/commands/TickCommand.java
index 0b559f38..e705c8b9 100644
--- a/src/main/java/carpet/commands/TickCommand.java
+++ b/src/main/java/carpet/commands/TickCommand.java
@@ -9,6 +9,7 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.BaseText;
import net.minecraft.server.command.ServerCommandSource;
@@ -80,7 +81,7 @@ public class TickCommand
private static int setWarp(ServerCommandSource source, int advance, String tail_command)
{
- PlayerEntity player = null;
+ ServerPlayerEntity player = null;
try
{
player = source.getPlayer();
diff --git a/src/main/java/carpet/helpers/OptimizedExplosion.java b/src/main/java/carpet/helpers/OptimizedExplosion.java
index 7628024c..bcf1b85c 100644
--- a/src/main/java/carpet/helpers/OptimizedExplosion.java
+++ b/src/main/java/carpet/helpers/OptimizedExplosion.java
@@ -7,8 +7,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import carpet.logging.logHelpers.ExplosionLogHelper;
import carpet.mixins.ExplosionAccessor;
import carpet.CarpetSettings;
+import carpet.mixins.ExplosionMixin;
import carpet.utils.Messenger;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
@@ -51,6 +53,7 @@ public class OptimizedExplosion
private static List entitylist;
private static Vec3d vec3dmem;
private static long tickmem;
+ private static Vec3d vec3dZero = new Vec3d(0, 0, 0);
// For disabling the explosion particles and sound
public static int explosionSound = 0;
@@ -66,7 +69,7 @@ public class OptimizedExplosion
private static ArrayList chances = new ArrayList<>();
private static BlockPos blastChanceLocation;
- public static void doExplosionA(Explosion e) {
+ public static void doExplosionA(Explosion e, ExplosionLogHelper eLogger) {
ExplosionAccessor eAccess = (ExplosionAccessor) e;
blastCalc(e);
@@ -120,6 +123,9 @@ public class OptimizedExplosion
entity.getX() == eAccess.getEntity().getX() &&
entity.getY() == eAccess.getEntity().getY() &&
entity.getZ() == eAccess.getEntity().getZ()) {
+ if (eLogger != null) {
+ eLogger.onEntityImpacted(entity, new Vec3d(0,-0.9923437498509884d, 0));
+ }
continue;
}
@@ -158,6 +164,10 @@ public class OptimizedExplosion
d11 = ProtectionEnchantment.transformExplosionKnockback((LivingEntity) entity, d10);
}
+ if (eLogger != null) {
+ eLogger.onEntityImpacted(entity, new Vec3d(d5 * d11, d7 * d11, d9 * d11));
+ }
+
entity.setVelocity(entity.getVelocity().add(d5 * d11, d7 * d11, d9 * d11));
if (entity instanceof PlayerEntity) {
diff --git a/src/main/java/carpet/helpers/TickSpeed.java b/src/main/java/carpet/helpers/TickSpeed.java
index dd642112..575867eb 100644
--- a/src/main/java/carpet/helpers/TickSpeed.java
+++ b/src/main/java/carpet/helpers/TickSpeed.java
@@ -7,10 +7,10 @@ import java.util.function.BiConsumer;
import carpet.CarpetServer;
import carpet.network.ServerNetworkHandler;
import carpet.utils.Messenger;
-import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
+import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.BaseText;
public class TickSpeed
@@ -21,7 +21,7 @@ public class TickSpeed
public static long time_bias = 0;
public static long time_warp_start_time = 0;
public static long time_warp_scheduled_ticks = 0;
- public static PlayerEntity time_advancerer = null;
+ public static ServerPlayerEntity time_advancerer = null;
public static String tick_warp_callback = null;
public static ServerCommandSource tick_warp_sender = null;
public static int player_active_timeout = 0;
@@ -68,13 +68,14 @@ public class TickSpeed
player_active_timeout = PLAYER_GRACE+ticks;
}
- public static BaseText tickrate_advance(PlayerEntity player, int advance, String callback, ServerCommandSource source)
+ public static BaseText tickrate_advance(ServerPlayerEntity player, int advance, String callback, ServerCommandSource source)
{
if (0 == advance)
{
tick_warp_callback = null;
- tick_warp_sender = null;
+ if (source != tick_warp_sender) tick_warp_sender = null;
finish_time_warp();
+ tick_warp_sender = null;
return Messenger.c("gi Warp interrupted");
}
if (time_bias > 0)
diff --git a/src/main/java/carpet/logging/LoggerRegistry.java b/src/main/java/carpet/logging/LoggerRegistry.java
index 6034a702..9d397c59 100644
--- a/src/main/java/carpet/logging/LoggerRegistry.java
+++ b/src/main/java/carpet/logging/LoggerRegistry.java
@@ -30,6 +30,7 @@ public class LoggerRegistry
public static boolean __packets;
public static boolean __weather;
public static boolean __pathfinding;
+ public static boolean __explosions;
public static void initLoggers()
{
@@ -52,6 +53,7 @@ public class LoggerRegistry
registerLogger("packets", HUDLogger.stardardHUDLogger("packets", null, null));
registerLogger("counter",HUDLogger.stardardHUDLogger("counter","white", Arrays.stream(DyeColor.values()).map(Object::toString).toArray(String[]::new)));
registerLogger("mobcaps", HUDLogger.stardardHUDLogger("mobcaps", "dynamic",new String[]{"dynamic", "overworld", "nether","end"}));
+ registerLogger("explosions", HUDLogger.stardardLogger("explosions", "brief",new String[]{"brief", "full"}));
}
diff --git a/src/main/java/carpet/logging/logHelpers/ExplosionLogHelper.java b/src/main/java/carpet/logging/logHelpers/ExplosionLogHelper.java
new file mode 100644
index 00000000..c61b8771
--- /dev/null
+++ b/src/main/java/carpet/logging/logHelpers/ExplosionLogHelper.java
@@ -0,0 +1,156 @@
+package carpet.logging.logHelpers;
+
+import carpet.logging.LoggerRegistry;
+import carpet.utils.Messenger;
+import com.google.common.collect.Lists;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.text.BaseText;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.world.explosion.Explosion;
+
+import java.util.List;
+
+public class ExplosionLogHelper
+{
+ private final boolean createFire;
+ private final Explosion.DestructionType blockDestructionType;
+ public final double x;
+ public final double y;
+ public final double z;
+ public final Entity entity;
+ private final float power;
+ private boolean affectBlocks = false;
+ private List entityChangedStatusWithCount = Lists.newArrayList();
+
+ private static long lastGametime = 0;
+ private static int explosionCountInCurretGT = 0;
+
+ public ExplosionLogHelper(Entity entity, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType blockDestructionType) {
+ this.entity = entity;
+ this.power = power;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.createFire = createFire;
+ this.blockDestructionType = blockDestructionType;
+ }
+
+ public void setAffectBlocks(boolean b)
+ {
+ affectBlocks = b;
+ }
+
+ public void onExplosionDone(long gametime)
+ {
+ BaseText timeDisplay;
+ if (!(lastGametime == gametime)){
+ explosionCountInCurretGT = 0;
+ lastGametime = gametime;
+ timeDisplay = Messenger.c("wb " + ">>>>>> CURRENT GAME TIME : ", "d " + gametime, "wb >>>>>>\n");
+ }
+ else
+ {
+ timeDisplay = Messenger.s("");
+ }
+ explosionCountInCurretGT++;
+ LoggerRegistry.getLogger("explosions").log( (option) -> {
+ switch (option)
+ {
+ case "brief":
+ return new BaseText[]{Messenger.c(
+ timeDisplay, "gb EXPLOSION", "d #" + explosionCountInCurretGT,"gb -> ",
+ "w P: ", "d ",Messenger.dblt("l",x ,y ,z),
+ "w affectBlocks: ", "m " + this.affectBlocks
+ )};
+ case "full":
+ return new BaseText[]{Messenger.c(
+ timeDisplay, "gb EXPLOSION", "d #" + explosionCountInCurretGT,"gb -> ",
+ "w P: ", "d ",Messenger.dblt("l",x ,y ,z),
+ "w createFire: ", "m " + this.createFire,
+ "w power: ", "c " + this.power,
+ "w destructionType: ", "c " + this.blockDestructionType.name(),
+ "w affectBlocks: ", "m " + this.affectBlocks,
+ "b \n", ListToText()
+ )};
+ }
+ return null;
+ });
+ }
+
+ public void onEntityImpacted(Entity entity, Vec3d deltaVelocity)
+ {
+ for (EntityChangedStatusWithCount e : entityChangedStatusWithCount)
+ {
+ if (e.checkIfEquals(entity, deltaVelocity))
+ {
+ e.count++;
+ return;
+ }
+ }
+ entityChangedStatusWithCount.add(new EntityChangedStatusWithCount(entity.getPos(), entity.getType(), deltaVelocity));
+ }
+
+ public BaseText ListToText()
+ {
+ BaseText s = Messenger.s("Entities Impacted:\n");
+ for (EntityChangedStatusWithCount entityChanges : entityChangedStatusWithCount)
+ {
+ s.append(entityChanges.asText());
+ s.append("\n");
+ }
+ return s;
+ }
+
+
+ public static class EntityChangedStatusWithCount
+ {
+ public final Vec3d entityPos;
+ public final EntityType entityType;
+ public final Vec3d deltaSpeed;
+ public int count;
+ public EntityChangedStatusWithCount(Vec3d entityPos, EntityType entityType, Vec3d deltaSpeed)
+ {
+ this.entityPos = entityPos;
+ this.entityType = entityType;
+ this.deltaSpeed = deltaSpeed;
+ this.count = 1;
+ }
+
+ public boolean equals(EntityChangedStatusWithCount e)
+ {
+ return (
+ e.entityType == this.entityType &&
+ e.deltaSpeed.equals(this.deltaSpeed) &&
+ e.entityPos.equals(this.entityPos)
+ );
+ }
+
+ public boolean checkIfEquals(Entity e, Vec3d deltaSpeed)
+ {
+ return (
+ e.getType() == this.entityType &&
+ deltaSpeed.equals(this.deltaSpeed) &&
+ e.getPos().equals(this.entityPos)
+ );
+ }
+
+ public String toString()
+ {
+ return String.format("{EntityType: %s, EntityPos: %s ,DeltaSpeed: %s}", entityType.getName().asString(), entityPos.toString(), deltaSpeed.toString());
+ }
+
+ public BaseText asText()
+ {
+ return Messenger.c(
+ "m Entity(count="+ this.count +") of",
+ "l " + Registry.ENTITY_TYPE.getId(entityType), "g {",
+ "w P: ", "y ", Messenger.dblt("t", entityPos.x, entityPos.y, entityPos.z),
+ "w dV: ", "gbi " + (deltaSpeed.equals(new Vec3d(0,-0.9923437498509884d, 0)) ? "":""),"y ", Messenger.dblt("d", deltaSpeed.x, deltaSpeed.y, deltaSpeed.z),
+ "g }"
+ );
+ }
+
+ }
+}
diff --git a/src/main/java/carpet/mixins/ExplosionMixin.java b/src/main/java/carpet/mixins/ExplosionMixin.java
index 8e74cda1..b2358119 100644
--- a/src/main/java/carpet/mixins/ExplosionMixin.java
+++ b/src/main/java/carpet/mixins/ExplosionMixin.java
@@ -2,9 +2,13 @@ package carpet.mixins;
import carpet.helpers.OptimizedExplosion;
import carpet.CarpetSettings;
+import carpet.logging.LoggerRegistry;
+import carpet.logging.logHelpers.ExplosionLogHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
+import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.spongepowered.asm.mixin.Final;
@@ -24,13 +28,17 @@ public abstract class ExplosionMixin
@Final
private List affectedBlocks;
+ @Shadow @Final private World world;
+
+ private ExplosionLogHelper eLogger;
+
@Inject(method = "collectBlocksAndDamageEntities", at = @At("HEAD"),
cancellable = true)
private void onExplosionA(CallbackInfo ci)
{
if (CarpetSettings.optimizedTNT)
{
- OptimizedExplosion.doExplosionA((Explosion) (Object) this);
+ OptimizedExplosion.doExplosionA((Explosion) (Object) this, eLogger);
ci.cancel();
}
}
@@ -39,6 +47,11 @@ public abstract class ExplosionMixin
cancellable = true)
private void onExplosionB(boolean spawnParticles, CallbackInfo ci)
{
+ if (eLogger != null)
+ {
+ eLogger.setAffectBlocks( ! affectedBlocks.isEmpty());
+ eLogger.onExplosionDone(this.world.getTime());
+ }
if (CarpetSettings.explosionNoBlockDamage)
{
affectedBlocks.clear();
@@ -58,4 +71,24 @@ public abstract class ExplosionMixin
if (CarpetSettings.explosionNoBlockDamage) return Blocks.BEDROCK.getDefaultState();
return world.getBlockState(pos);
}
+
+ @Inject(method = "(Lnet/minecraft/world/World;Lnet/minecraft/entity/Entity;DDDFZLnet/minecraft/world/explosion/Explosion$DestructionType;)V",
+ at = @At(value = "RETURN"))
+ private void onExplosionCreated(World world, Entity entity, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType blockDestructionType, CallbackInfo ci)
+ {
+ if (LoggerRegistry.__explosions && ! world.isClient)
+ {
+ eLogger = new ExplosionLogHelper(entity, x, y, z, power, createFire, blockDestructionType);
+ }
+ }
+
+ @Redirect(method = "collectBlocksAndDamageEntities",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;setVelocity(Lnet/minecraft/util/math/Vec3d;)V"))
+ private void setVelocityAndUpdateLogging(Entity entity, Vec3d velocity)
+ {
+ if (eLogger != null) {
+ eLogger.onEntityImpacted(entity, velocity.subtract(entity.getVelocity()));
+ }
+ entity.setVelocity(velocity);
+ }
}
diff --git a/src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java b/src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java
index a5750724..8d776a32 100644
--- a/src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java
+++ b/src/main/java/carpet/mixins/PistonBlock_movableTEMixin.java
@@ -5,6 +5,7 @@ import carpet.fakes.PistonBlockEntityInterface;
import com.google.common.collect.Lists;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.block.piston.PistonBehavior;
import net.minecraft.block.piston.PistonHandler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
@@ -62,6 +63,17 @@ public abstract class PistonBlock_movableTEMixin extends FacingBlock
return !(CarpetSettings.movableBlockEntities && isPushableBlockEntity(block));
}
}
+
+ @Redirect(method = "isMovable", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/block/BlockState;getPistonBehavior()Lnet/minecraft/block/piston/PistonBehavior;"
+ ))
+ private static PistonBehavior moveGrindstones(BlockState blockState)
+ {
+ if (CarpetSettings.movableBlockEntities && blockState.getBlock() == Blocks.GRINDSTONE) return PistonBehavior.NORMAL;
+ return blockState.getPistonBehavior();
+ }
+
@Inject(method = "move", at = @At(value = "INVOKE", shift = At.Shift.BEFORE,
target = "Ljava/util/List;size()I", ordinal = 4),locals = LocalCapture.CAPTURE_FAILHARD)
private void onMove(World world_1, BlockPos blockPos_1, Direction direction_1, boolean boolean_1,
diff --git a/src/main/java/carpet/mixins/PlayerEntity_antiCheatDisabled.java b/src/main/java/carpet/mixins/PlayerEntity_antiCheatDisabled.java
index 87ac368c..fd50bb93 100644
--- a/src/main/java/carpet/mixins/PlayerEntity_antiCheatDisabled.java
+++ b/src/main/java/carpet/mixins/PlayerEntity_antiCheatDisabled.java
@@ -23,7 +23,7 @@ public abstract class PlayerEntity_antiCheatDisabled
@Inject(method = "checkFallFlying", at = @At("HEAD"), cancellable = true)
private void allowDeploys(CallbackInfoReturnable cir)
{
- if (CarpetSettings.antiCheatDisabled && CarpetServer.minecraft_server.isDedicated())
+ if (CarpetSettings.antiCheatDisabled && CarpetServer.minecraft_server != null && CarpetServer.minecraft_server.isDedicated())
{
ItemStack itemStack_1 = getEquippedStack(EquipmentSlot.CHEST);
if (itemStack_1.getItem() == Items.ELYTRA && ElytraItem.isUsable(itemStack_1)) {
diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java
index 048e7ce1..6541144b 100644
--- a/src/main/java/carpet/script/CarpetEventServer.java
+++ b/src/main/java/carpet/script/CarpetEventServer.java
@@ -13,6 +13,7 @@ import carpet.script.value.Value;
import carpet.utils.Messenger;
import net.minecraft.block.BlockState;
import net.minecraft.entity.damage.DamageSource;
+import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
@@ -53,6 +54,7 @@ import java.util.stream.Collectors;
public class CarpetEventServer
{
public final List scheduledCalls = new LinkedList<>();
+ public final MinecraftServer server;
public static class Callback
{
@@ -658,8 +660,9 @@ public class CarpetEventServer
}
- public CarpetEventServer()
+ public CarpetEventServer(MinecraftServer server)
{
+ this.server = server;
for (Event e: Event.values())
e.handler.callList.clear();
}
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index 38a63113..2df7e305 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -2489,6 +2489,18 @@ public class CarpetExpression
}
+ public static String recognizeResource(Value value)
+ {
+ String origfile = value.getString();
+ String 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 "+origfile+" as resource name - must have some letters and numbers");
+ }
+ return file;
+ }
+
private void API_AuxiliaryAspects()
{
this.expr.addLazyFunction("sound", -1, (c, t, lv) -> {
@@ -2877,13 +2889,6 @@ public class CarpetExpression
// implmenetation should dock the task on the main thread.
});
- this.expr.addLazyFunction("logger", 1, (c, t, lv) ->
- {
- Value res = lv.get(0).evalValue(c);
- CarpetSettings.LOG.error(res.getString());
- return (_c, _t) -> res; // pass through for variables
- });
-
this.expr.addLazyFunction("run", 1, (c, t, lv) -> {
BlockPos target = ((CarpetContext)c).origin;
Vec3d posf = new Vec3d((double)target.getX()+0.5D,(double)target.getY(),(double)target.getZ()+0.5D);
@@ -3070,25 +3075,108 @@ public class CarpetExpression
return (c_, t_) -> Value.TRUE;
});
+ this.expr.addLazyFunction("logger", 1, (c, t, lv) ->
+ {
+ Value res = lv.get(0).evalValue(c);
+ CarpetSettings.LOG.error(res.getString());
+ return (_c, _t) -> res; // pass through for variables
+ });
+
+ this.expr.addLazyFunction("read_file", 2, (c, t, lv) -> {
+ String resource = recognizeResource(lv.get(0).evalValue(c));
+ String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
+ boolean shared = origtype.startsWith("shared_");
+ String type = shared ? origtype.substring(7) : origtype; //len(shared_)
+ if (!type.equals("raw") && !type.equals("text") && !type.equals("nbt"))
+ throw new InternalExpressionException("Unsupported file type: "+origtype);
+ Value retVal;
+ if (type.equals("nbt"))
+ {
+ Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(resource, shared);
+ if (state == null) return LazyValue.NULL;
+ retVal = new NBTSerializableValue(state);
+ }
+ else
+ {
+ List content = ((CarpetScriptHost) ((CarpetContext) c).host).readTextResource(resource, shared);
+ if (content == null) return LazyValue.NULL;
+ retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
+ }
+ return (cc, tt) -> retVal;
+ });
+
+ this.expr.addLazyFunction("delete_file", 2, (c, t, lv) -> {
+ String resource = recognizeResource(lv.get(0).evalValue(c));
+ String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
+ boolean shared = origtype.startsWith("shared_");
+ String type = shared ? origtype.substring(7) : origtype; //len(shared_)
+ if (!type.equals("raw") && !type.equals("text") && !type.equals("nbt"))
+ throw new InternalExpressionException("Unsupported file type: "+origtype);
+ boolean success = ((CarpetScriptHost)((CarpetContext)c).host).removeResourceFile(resource, shared, type);
+ return success?LazyValue.TRUE:LazyValue.FALSE;
+ });
+
+ this.expr.addLazyFunction("write_file", -1, (c, t, lv) -> {
+ if (lv.size() < 3) throw new InternalExpressionException("'write_file' requires three or more arguments");
+ String resource = recognizeResource(lv.get(0).evalValue(c));
+ String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
+ boolean shared = origtype.startsWith("shared_");
+ String type = shared ? origtype.substring(7) : origtype; //len(shared_)
+ if (!type.equals("raw") && !type.equals("text") && !type.equals("nbt"))
+ throw new InternalExpressionException("Unsupported file type: "+origtype);
+ boolean success;
+ if (type.equals("nbt"))
+ {
+ Value val = lv.get(2).evalValue(c);
+ NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
+ ? (NBTSerializableValue) val
+ : new NBTSerializableValue(val.getString());
+ Tag tag = tagValue.getTag();
+ success = ((CarpetScriptHost)((CarpetContext)c).host).writeTagFile(tag, resource, shared);
+ }
+ else
+ {
+ List data = new ArrayList<>();
+ if (lv.size()==3)
+ {
+ Value val = lv.get(2).evalValue(c);
+ if (val instanceof ListValue)
+ {
+ List lval = ((ListValue) val).getItems();
+ lval.forEach(v -> data.add(v.getString()));
+ }
+ else
+ {
+ data.add(val.getString());
+ }
+ }
+ else
+ {
+ for(int i = 2; i < lv.size(); i++)
+ {
+ data.add(lv.get(i).evalValue(c).getString());
+ }
+ }
+ success = ((CarpetScriptHost) ((CarpetContext) c).host).appendLogFile(resource, shared, type, data);
+ }
+ return success?LazyValue.TRUE:LazyValue.FALSE;
+ });
+
+ //write_file
+
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 = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
- if (file.isEmpty())
- {
- throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have at least some letters and numbers");
- }
+ file = recognizeResource(lv.get(0).evalValue(c));
if (lv.size() > 1)
{
shared = lv.get(1).evalValue(c).getBoolean();
}
}
- Tag state = ((CarpetScriptHost)((CarpetContext)c).host).getGlobalState(file, shared);
+ Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(file, shared);
if (state == null)
return (cc, tt) -> Value.NULL;
Value retVal = new NBTSerializableValue(state);
@@ -3104,13 +3192,7 @@ public class CarpetExpression
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 = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
- if (file.isEmpty())
- {
- throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have some letters and numbers");
- }
+ file = recognizeResource(lv.get(1).evalValue(c));
if (lv.size() > 2)
{
shared = lv.get(2).evalValue(c).getBoolean();
@@ -3120,7 +3202,7 @@ public class CarpetExpression
? (NBTSerializableValue) val
: new NBTSerializableValue(val.getString());
Tag tag = tagValue.getTag();
- boolean success = ((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file, shared);
+ boolean success = ((CarpetScriptHost)((CarpetContext)c).host).writeTagFile(tag, file, shared);
return success?LazyValue.TRUE:LazyValue.FALSE;
});
diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java
index 94f3b2fc..25892565 100644
--- a/src/main/java/carpet/script/CarpetScriptHost.java
+++ b/src/main/java/carpet/script/CarpetScriptHost.java
@@ -323,7 +323,7 @@ public class CarpetScriptHost extends ScriptHost
{
try
{
- callUDF(BlockPos.ORIGIN, CarpetServer.minecraft_server.getCommandSource(), closing, Collections.emptyList());
+ callUDF(BlockPos.ORIGIN, scriptServer.server.getCommandSource(), closing, Collections.emptyList());
}
catch (InvalidCallbackException ignored)
{
@@ -334,8 +334,8 @@ public class CarpetScriptHost extends ScriptHost
FunctionValue userClosing = value.getFunction("__on_close");
if (userClosing != null)
{
- ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key);
- ServerCommandSource source = (player != null)?player.getCommandSource():CarpetServer.minecraft_server.getCommandSource();
+ ServerPlayerEntity player = scriptServer.server.getPlayerManager().getPlayer(key);
+ ServerCommandSource source = (player != null)?player.getCommandSource():scriptServer.server.getCommandSource();
try
{
((CarpetScriptHost) value).callUDF(BlockPos.ORIGIN, source, userClosing, Collections.emptyList());
@@ -347,7 +347,7 @@ public class CarpetScriptHost extends ScriptHost
});
String markerName = CarpetExpression.MARKER_STRING+"_"+((getName()==null)?"":getName());
- for (ServerWorld world : CarpetServer.minecraft_server.getWorlds())
+ for (ServerWorld world : scriptServer.server.getWorlds())
{
for (Entity e : world.getEntities(EntityType.ARMOR_STAND, (as) -> as.getScoreboardTags().contains(markerName)))
{
@@ -369,7 +369,7 @@ public class CarpetScriptHost extends ScriptHost
return Module.getData(main, null, false);
}
- public Tag getGlobalState(String file, boolean isShared)
+ public Tag readFileTag(String file, boolean isShared)
{
if (getName() == null && !isShared) return null;
if (file != null)
@@ -379,7 +379,7 @@ public class CarpetScriptHost extends ScriptHost
return ((CarpetScriptHost)parent).globalState;
}
- public boolean setGlobalState(Tag tag, String file, boolean isShared)
+ public boolean writeTagFile(Tag tag, String file, boolean isShared)
{
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
@@ -398,6 +398,25 @@ public class CarpetScriptHost extends ScriptHost
return true;
}
+ public boolean removeResourceFile(String resource, boolean isShared, String type)
+ {
+ if (getName() == null && !isShared) return false; //
+ return Module.dropExistingFile(main, resource, type.equals("nbt")?"nbt":"txt", isShared);
+ }
+
+ public boolean appendLogFile(String resource, boolean isShared, String type, List data)
+ {
+ if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
+ return Module.appendToTextFile(main, resource, type, isShared, data);
+ }
+
+ public List readTextResource(String resource, boolean isShared)
+ {
+ if (getName() == null && !isShared) return null; //
+ return Module.listFile(main, resource, "txt", isShared);
+ }
+
+
public void tick()
{
if (this.saveTimeout > 0)
diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java
index 0717e875..6470606f 100644
--- a/src/main/java/carpet/script/CarpetScriptServer.java
+++ b/src/main/java/carpet/script/CarpetScriptServer.java
@@ -36,6 +36,7 @@ import static net.minecraft.server.command.CommandManager.literal;
public class CarpetScriptServer
{
//make static for now, but will change that later:
+ public final MinecraftServer server;
public CarpetScriptHost globalHost;
public Map modules;
public long tickStart;
@@ -51,19 +52,20 @@ public class CarpetScriptServer
add(new BundledModule("math", true));
}};
- public CarpetScriptServer()
+ public CarpetScriptServer(MinecraftServer server)
{
init();
}
private void init()
{
+ this.server = server;
ScriptHost.systemGlobals.clear();
- events = new CarpetEventServer();
+ events = new CarpetEventServer(server);
modules = new HashMap<>();
tickStart = 0L;
stopAll = false;
- holyMoly = CarpetServer.minecraft_server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet());
+ holyMoly = server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet());
globalHost = CarpetScriptHost.create(this, null, false, null);
}
@@ -71,10 +73,10 @@ public class CarpetScriptServer
{
if (CarpetSettings.scriptsAutoload)
{
- Messenger.m(CarpetServer.minecraft_server.getCommandSource(), "Auto-loading world scarpet apps");
+ Messenger.m(server.getCommandSource(), "Auto-loading world scarpet apps");
for (String moduleName: listAvailableModules(false))
{
- addScriptHost(CarpetServer.minecraft_server.getCommandSource(), moduleName, true, true);
+ addScriptHost(server.getCommandSource(), moduleName, true, true);
}
}
@@ -83,6 +85,8 @@ public class CarpetScriptServer
public Module getModule(String name, boolean allowLibraries)
{
File folder = CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts").toFile();
+ File folder = server.getLevelStorage().resolveFile(
+ server.getLevelName(), "scripts");
File[] listOfFiles = folder.listFiles();
if (listOfFiles != null)
for (File script : listOfFiles)
@@ -117,6 +121,8 @@ public class CarpetScriptServer
}
}
File folder = CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts").toFile();
+ File folder = server.getLevelStorage().resolveFile(
+ server.getLevelName(), "scripts");
File[] listOfFiles = folder.listFiles();
if (listOfFiles == null)
return moduleNames;
@@ -228,7 +234,7 @@ public class CarpetScriptServer
})));
}
Messenger.m(source, "gi "+hostName+" app loaded with /"+hostName+" command");
- CarpetServer.minecraft_server.getCommandManager().getDispatcher().register(command);
+ server.getCommandManager().getDispatcher().register(command);
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
}
diff --git a/src/main/java/carpet/script/Tokenizer.java b/src/main/java/carpet/script/Tokenizer.java
index f0c1d4eb..ef4c0d5e 100644
--- a/src/main/java/carpet/script/Tokenizer.java
+++ b/src/main/java/carpet/script/Tokenizer.java
@@ -173,9 +173,9 @@ public class Tokenizer implements Iterator
}
else if (nextChar == 't')
{
- throw new ExpressionException(context, this.expression, token,
- "Tab character is not supported");
- //token.append('\t');
+ //throw new ExpressionException(context, this.expression, token,
+ // "Tab character is not supported");
+ token.append('\t');
}
else if (nextChar == 'r')
{
diff --git a/src/main/java/carpet/script/bundled/FileModule.java b/src/main/java/carpet/script/bundled/FileModule.java
index ef44a292..4c116eec 100644
--- a/src/main/java/carpet/script/bundled/FileModule.java
+++ b/src/main/java/carpet/script/bundled/FileModule.java
@@ -1,17 +1,24 @@
package carpet.script.bundled;
-import carpet.CarpetServer;
import net.minecraft.nbt.PositionTracker;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.TagReaders;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
public class FileModule extends Module
@@ -96,4 +103,44 @@ public class FileModule extends Module
if (!file.exists()) file_2.renameTo(file);
return true;
}
+
+ public static boolean appendText(File filePath, boolean addNewLines, List data)
+ {
+ try
+ {
+ OutputStream out = Files.newOutputStream(filePath.toPath(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
+ try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
+ {
+ for (String line: data)
+ {
+ writer.append(line);
+ if (addNewLines) writer.newLine();
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ return true;
+ }
+
+
+ public static List listFileContent(File filePath)
+ {
+ try (BufferedReader reader = Files.newBufferedReader(filePath.toPath(), StandardCharsets.UTF_8)) {
+ List result = new ArrayList<>();
+ for (;;) {
+ String line = reader.readLine();
+ if (line == null)
+ break;
+ result.add(line.replaceAll("[\n\r]+",""));
+ }
+ return result;
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
}
diff --git a/src/main/java/carpet/script/bundled/Module.java b/src/main/java/carpet/script/bundled/Module.java
index 66fff341..2e4ccba2 100644
--- a/src/main/java/carpet/script/bundled/Module.java
+++ b/src/main/java/carpet/script/bundled/Module.java
@@ -6,15 +6,14 @@ import net.minecraft.util.WorldSavePath;
import java.io.File;
import java.nio.file.Files;
-
-import static carpet.script.bundled.FileModule.read;
-import static carpet.script.bundled.FileModule.write;
+import java.util.List;
public abstract class Module
{
public abstract String getName();
public abstract String getCode();
public abstract boolean isLibrary();
+ public final static Object writeIOSync = new Object();
private static String getDescriptor(Module module, String file, boolean isShared)
{
@@ -34,18 +33,52 @@ public abstract class Module
public static Tag getData(Module module, String file, boolean isShared)
{
+ File dataFile = resolveResource(module, file, "nbt", isShared);
+ if (dataFile == null) return null;
if (!isShared && (module == null || module.getName() == null)) return null;
File dataFile = CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, file, isShared)+".nbt").toFile();
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
- return read(dataFile);
+ synchronized (writeIOSync) { return FileModule.read(dataFile); }
}
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
{
+ File dataFile = resolveResource(module, file, "nbt", isShared);
+ if (dataFile == null) return false;
if (!isShared && (module == null || module.getName() == null)) return false;
File dataFile =CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, file, isShared)+".nbt").toFile();
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
- return write(globalState, dataFile);
+ synchronized (writeIOSync) { return FileModule.write(globalState, dataFile); }
+ }
+
+ public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List message)
+ {
+ File dataFile = resolveResource(module, resourceName, "txt", isShared);
+ if (dataFile == null) return false;
+ if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
+ synchronized (writeIOSync) { return FileModule.appendText(dataFile, type.equals("text"), message); }
+ }
+
+ public static boolean dropExistingFile(Module module, String resourceName, String ext, boolean isShared)
+ {
+ File dataFile = resolveResource(module, resourceName, ext, isShared);
+ if (dataFile == null) return false;
+ synchronized (writeIOSync) { return dataFile.delete(); }
+ }
+
+ public static List listFile(Module module, String resourceName, String ext, boolean isShared)
+ {
+ File dataFile = resolveResource(module, resourceName, ext, isShared);
+ if (dataFile == null) return null;
+ if (!dataFile.exists()) return null;
+ synchronized (writeIOSync) { return FileModule.listFileContent(dataFile); }
+ }
+
+ private static File resolveResource(Module module, String resourceName, String ext, boolean isShared)
+ {
+ if (!isShared && (module == null || module.getName() == null)) return null;
+ return CarpetServer.minecraft_server.getLevelStorage().resolveFile(
+ CarpetServer.minecraft_server.getLevelName(), "scripts/"+getDescriptor(module, resourceName, isShared)+"."+ext);
}
@Override
diff --git a/src/main/java/carpet/settings/SettingsManager.java b/src/main/java/carpet/settings/SettingsManager.java
index e371d8e5..1fadef0b 100644
--- a/src/main/java/carpet/settings/SettingsManager.java
+++ b/src/main/java/carpet/settings/SettingsManager.java
@@ -361,8 +361,7 @@ public class SettingsManager
{
if (dispatcher.getRoot().getChildren().stream().anyMatch(node -> node.getName().equalsIgnoreCase(identifier)))
{
- Messenger.print_server_message(CarpetServer.minecraft_server,
- Messenger.c("rb Failed to add settings command for " + identifier + ". It is masking previous command."));
+ CarpetSettings.LOG.error("Failed to add settings command for " + identifier + ". It is masking previous command.");
return;
}