diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index 466e9ec6..0b4fad16 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -610,7 +610,7 @@ Available options in the scarpet app space: * `world_folder` - name of the direct folder in the saves that holds world files * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event. * `world_gamerules` - returns all gamerules in a map form (`rule`->`value`). Like carpet rules, values are returned as strings, so you can use appropriate value conversions using `bool()` or `number()` to convert them to other values. Gamerules are read-only to discourage app programmers to mess up with the settings intentionally applied by server admins. Isn't that just super annoying when a datapack messes up with your gamerule settings? It is still possible to change them though using `run('gamerule ...`. - + * `world_spawn_point` - world spawn point Relevant gameplay related properties * `game_difficulty` - current difficulty of the game: `'peaceful'`, `'easy'`, `'normal'`, or `'hard'` diff --git a/docs/scarpet/language/SystemFunctions.md b/docs/scarpet/language/SystemFunctions.md index eb453bcd..1ddcc8e9 100644 --- a/docs/scarpet/language/SystemFunctions.md +++ b/docs/scarpet/language/SystemFunctions.md @@ -227,11 +227,12 @@ length('foo') => 3 ### `rand(expr), rand(expr, seed)` returns a random number from `0.0` (inclusive) to `expr` (exclusive). In boolean context (in conditions, -boolean functions, or `bool`), returns false if the randomly selected value is less than 1\. This means -that `rand(2)` returns true half of the time and `rand(5)` returns true for 80% (4/5) of the time. If seed is not -provided, uses a random seed. If seed is provided, each consecutive call to rand() will act like 'next' call to the -same random object. Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number -(per app), then your random sequence will revert to the beginning. +boolean functions, or `bool`), returns false if the randomly selected value is less than 1. This means +that `bool(rand(2))` returns true half of the time and `!rand(5)` returns true for 20% (1/5) of the time. If seed is not +provided, uses a random seed that's shared across all scarpet apps. +If seed is provided, each consecutive call to rand() will act like 'next' call to the +same random object. Scarpet keeps track of up to 65536 custom random number generators (custom seeds, per app), +so if you exceed this number, your random sequences will revert to the beginning and start over.
 map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
diff --git a/src/main/java/carpet/commands/PerimeterInfoCommand.java b/src/main/java/carpet/commands/PerimeterInfoCommand.java
index 9fb7a40b..73fce328 100644
--- a/src/main/java/carpet/commands/PerimeterInfoCommand.java
+++ b/src/main/java/carpet/commands/PerimeterInfoCommand.java
@@ -57,8 +57,8 @@ public class PerimeterInfoCommand
             });
             if (!(baseEntity instanceof  MobEntity))
             {
-                Messenger.m(source, "r Failed to spawn test entity");
-                if (baseEntity != null) baseEntity.discard(); // discard //remove();
+                Messenger.m(source, "r /perimeterinfo requires a mob entity to test agains.");
+                if (baseEntity != null) baseEntity.discard();
                 return 0;
             }
             entityliving = (MobEntity) baseEntity;
diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java
index 679474ef..5ad27430 100644
--- a/src/main/java/carpet/commands/ScriptCommand.java
+++ b/src/main/java/carpet/commands/ScriptCommand.java
@@ -13,6 +13,7 @@ import carpet.script.exception.CarpetExpressionException;
 import carpet.script.value.FunctionValue;
 import carpet.script.value.Value;
 import carpet.settings.SettingsManager;
+import carpet.utils.CarpetProfiler;
 import carpet.utils.Messenger;
 import com.mojang.brigadier.CommandDispatcher;
 import com.mojang.brigadier.arguments.StringArgumentType;
@@ -396,6 +397,7 @@ public class ScriptCommand
     {
         try
         {
+            CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet ace", CarpetProfiler.TYPE.GENERAL);
             host.setChatErrorSnooper(source);
             long start = System.nanoTime();
             Value result = call.get();
@@ -412,7 +414,9 @@ public class ScriptCommand
                 metric = "s";
             }
             Messenger.m(source, "wi  = ", "wb "+result.getString(), "gi  ("+time+metric+")");
-            return (int)result.readInteger();
+            int intres = (int)result.readInteger();
+            CarpetProfiler.end_current_section(currentSection);
+            return intres;
         }
         catch (CarpetExpressionException e)
         {
diff --git a/src/main/java/carpet/mixins/WallStandingBlockItem_creativeNoClipMixin.java b/src/main/java/carpet/mixins/WallStandingBlockItem_creativeNoClipMixin.java
new file mode 100644
index 00000000..b8035233
--- /dev/null
+++ b/src/main/java/carpet/mixins/WallStandingBlockItem_creativeNoClipMixin.java
@@ -0,0 +1,38 @@
+package carpet.mixins;
+
+import carpet.CarpetSettings;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.ShapeContext;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemPlacementContext;
+import net.minecraft.item.WallStandingBlockItem;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.shape.VoxelShape;
+import net.minecraft.world.WorldView;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+@Mixin(WallStandingBlockItem.class)
+public class WallStandingBlockItem_creativeNoClipMixin
+{
+    @Redirect(method = "getPlacementState", at = @At(
+                value = "INVOKE",
+                target = "Lnet/minecraft/world/WorldView;canPlace(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/ShapeContext;)Z"
+    ))
+    private boolean canCreativePlayerPlace(
+            WorldView worldView, BlockState state, BlockPos pos, ShapeContext context,
+            ItemPlacementContext itemcontext
+    )
+    {
+        PlayerEntity player = itemcontext.getPlayer();
+        if (CarpetSettings.creativeNoClip && player != null && player.isCreative() && player.abilities.flying)
+        {
+            // copy from canPlace
+            VoxelShape voxelShape = state.getCollisionShape(worldView, pos, context);
+            return voxelShape.isEmpty() || worldView.intersectsEntities(player, voxelShape.offset(pos.getX(), pos.getY(), pos.getZ()));
+
+        }
+        return worldView.canPlace(state, pos, context);
+    }
+}
diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java
index f017f574..3c1c084d 100644
--- a/src/main/java/carpet/script/CarpetEventServer.java
+++ b/src/main/java/carpet/script/CarpetEventServer.java
@@ -14,6 +14,7 @@ import carpet.script.value.StringValue;
 import carpet.script.value.Value;
 import carpet.script.value.ValueConversions;
 import carpet.settings.ParsedRule;
+import carpet.utils.CarpetProfiler;
 import carpet.utils.Messenger;
 import com.mojang.brigadier.exceptions.CommandSyntaxException;
 import net.minecraft.block.BlockState;
@@ -204,6 +205,7 @@ public class CarpetEventServer
         {
             if (callList.size() > 0)
             {
+                CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet events", CarpetProfiler.TYPE.GENERAL);
                 List argv = argumentSupplier.get(); // empty for onTickDone
                 ServerCommandSource source;
                 try
@@ -225,6 +227,7 @@ public class CarpetEventServer
                     if (!call.execute(source, argv)) fails.add(call);
                 }
                 for (Callback call : fails) callList.remove(call);
+                CarpetProfiler.end_current_section(currentSection);
             }
         }
 
diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java
index 8397fc86..f8bcd56f 100644
--- a/src/main/java/carpet/script/CarpetScriptHost.java
+++ b/src/main/java/carpet/script/CarpetScriptHost.java
@@ -18,6 +18,7 @@ import carpet.script.value.MapValue;
 import carpet.script.value.NumericValue;
 import carpet.script.value.StringValue;
 import carpet.script.value.Value;
+import carpet.utils.CarpetProfiler;
 import carpet.utils.Messenger;
 
 import com.google.gson.JsonElement;
@@ -127,6 +128,7 @@ public class CarpetScriptHost extends ScriptHost
 
     private static int execute(CommandContext ctx, String hostName, FunctionArgument funcSpec, List paramNames) throws CommandSyntaxException
     {
+        CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
         CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
         List argNames = funcSpec.function.getArguments();
         if ((argNames.size()-funcSpec.args.size()) != paramNames.size())
@@ -140,7 +142,9 @@ public class CarpetScriptHost extends ScriptHost
         Value response = cHost.handleCommand(ctx.getSource(), funcSpec.function, args);
         // will skip prints for new
         //if (!response.isNull()) Messenger.m(ctx.getSource(), "gi " + response.getString());
-        return (int) response.readInteger();
+        int intres = (int) response.readInteger();
+        CarpetProfiler.end_current_section(currentSection);
+        return intres;
     }
 
     public LiteralArgumentBuilder addPathToCommand(
@@ -210,12 +214,15 @@ public class CarpetScriptHost extends ScriptHost
         return s -> {
             try
             {
+                CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
                 CarpetScriptHost cHost = null;
                 cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(s);
                 Value response = cHost.handleCommand(s, fun, Collections.singletonList(
                         (s.getEntity() instanceof ServerPlayerEntity)?new EntityValue(s.getEntity()):Value.NULL)
                 );
-                return response.getBoolean();
+                boolean res = response.getBoolean();
+                CarpetProfiler.end_current_section(currentSection);
+                return res;
             }
             catch (CommandSyntaxException e)
             {
@@ -546,7 +553,10 @@ public class CarpetScriptHost extends ScriptHost
     {
         try
         {
-            return callLegacy(source, call, coords, arg);
+            CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet legacy", CarpetProfiler.TYPE.GENERAL);
+            Value res = callLegacy(source, call, coords, arg);
+            CarpetProfiler.end_current_section(currentSection);
+            return res;
         }
         catch (CarpetExpressionException exc)
         {
diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java
index d8dfbd35..b74ef32b 100644
--- a/src/main/java/carpet/script/CarpetScriptServer.java
+++ b/src/main/java/carpet/script/CarpetScriptServer.java
@@ -1,19 +1,15 @@
 package carpet.script;
 
 import carpet.CarpetSettings;
-import carpet.script.argument.FunctionArgument;
 import carpet.script.bundled.BundledModule;
 import carpet.CarpetServer;
 import carpet.script.bundled.FileModule;
 import carpet.script.bundled.Module;
-import carpet.script.command.CommandToken;
 import carpet.script.exception.InvalidCallbackException;
 import carpet.script.value.FunctionValue;
-import carpet.script.value.StringValue;
 import carpet.script.value.Value;
+import carpet.utils.CarpetProfiler;
 import carpet.utils.Messenger;
-import com.mojang.brigadier.arguments.StringArgumentType;
-import com.mojang.brigadier.builder.LiteralArgumentBuilder;
 import com.mojang.brigadier.exceptions.CommandSyntaxException;
 import com.mojang.brigadier.tree.CommandNode;
 
@@ -229,6 +225,7 @@ public class CarpetScriptServer
     public boolean addScriptHost(ServerCommandSource source, String name, Function commandValidator,
                                  boolean perPlayer, boolean autoload, boolean isRuleApp)
     {
+        CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet loading", CarpetProfiler.TYPE.GENERAL);
         if (commandValidator == null) commandValidator = p -> true;
         long start = System.nanoTime();
         name = name.toLowerCase(Locale.ROOT);
@@ -301,6 +298,7 @@ public class CarpetScriptServer
             FunctionValue onStart = newHost.getFunction("__on_start");
             if (onStart != null) newHost.callNow(onStart, Collections.emptyList());
         }
+        CarpetProfiler.end_current_section(currentSection);
         long end = System.nanoTime();
         CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
         return true;
@@ -412,11 +410,16 @@ public class CarpetScriptServer
 
     public void tick()
     {
+        CarpetProfiler.ProfilerToken token;
+        token = CarpetProfiler.start_section(null, "Scarpet schedule", CarpetProfiler.TYPE.GENERAL);
         events.tick();
+        CarpetProfiler.end_current_section(token);
+        token = CarpetProfiler.start_section(null, "Scarpet app data", CarpetProfiler.TYPE.GENERAL);
         for (CarpetScriptHost host : modules.values())
         {
             host.tick();
         }
+        CarpetProfiler.end_current_section(token);
     }
 
     public void onClose()
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index 7aa7ec0f..d0c36074 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -765,7 +765,7 @@ public class Expression
                     {
                         if (token.surface.equals(";"))
                         {
-                            throw new ExpressionException(c, this, token, "Unnecessary semicolon");
+                            throw new ExpressionException(c, this, token, "Empty expression found for ';'");
                         }
                         throw new ExpressionException(c, this, token, "Missing parameter(s) for operator " + token);
                     }
diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java
index 255f76d4..f57ae663 100644
--- a/src/main/java/carpet/script/ScriptHost.java
+++ b/src/main/java/carpet/script/ScriptHost.java
@@ -37,7 +37,7 @@ public abstract class ScriptHost
 
     public Random getRandom(long aLong)
     {
-        if (randomizers.size() > 1024)
+        if (randomizers.size() > 65536)
             randomizers.clear();
         return randomizers.computeIfAbsent(aLong, Random::new);
     }
diff --git a/src/main/java/carpet/script/command/CommandArgument.java b/src/main/java/carpet/script/command/CommandArgument.java
index 01163359..d38458d8 100644
--- a/src/main/java/carpet/script/command/CommandArgument.java
+++ b/src/main/java/carpet/script/command/CommandArgument.java
@@ -14,6 +14,7 @@ import carpet.script.value.NumericValue;
 import carpet.script.value.StringValue;
 import carpet.script.value.Value;
 import carpet.script.value.ValueConversions;
+import carpet.utils.CarpetProfiler;
 import com.google.common.collect.Lists;
 import com.mojang.authlib.GameProfile;
 import com.mojang.brigadier.arguments.ArgumentType;
@@ -365,6 +366,7 @@ public abstract class CommandArgument
     {
         if (customSuggester != null)
         {
+            CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
             Map params = new HashMap<>();
             for(ParsedCommandNode pnode : context.getNodes())
             {
@@ -379,7 +381,9 @@ public abstract class CommandArgument
             args.addAll(customSuggester.args);
             Value response = host.handleCommand(context.getSource(), customSuggester.function, args);
             if (!(response instanceof ListValue)) throw error("Custom suggester should return a list of options"+" for custom type "+suffix);
-            return ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
+            Collection res = ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
+            CarpetProfiler.end_current_section(currentSection);
+            return res;
         }
         if (needsMatching) return examples;
         //return Lists.newArrayList("");
diff --git a/src/main/java/carpet/script/utils/SystemInfo.java b/src/main/java/carpet/script/utils/SystemInfo.java
index df957900..60863f7b 100644
--- a/src/main/java/carpet/script/utils/SystemInfo.java
+++ b/src/main/java/carpet/script/utils/SystemInfo.java
@@ -18,6 +18,7 @@ import net.fabricmc.loader.api.FabricLoader;
 import net.minecraft.SharedConstants;
 import net.minecraft.util.WorldSavePath;
 import net.minecraft.world.GameRules;
+import net.minecraft.world.WorldProperties;
 
 import java.lang.management.ManagementFactory;
 import java.nio.file.Path;
@@ -52,6 +53,11 @@ public class SystemInfo {
             return StringValue.of(tlf);
         });
         put("world_dimensions", c -> ListValue.wrap(c.s.getMinecraftServer().getWorldRegistryKeys().stream().map(k -> ValueConversions.of(k.getValue())).collect(Collectors.toList())));
+        put("world_spawn_point", c -> {
+            WorldProperties prop = c.s.getMinecraftServer().getOverworld().getLevelProperties();
+            return ListValue.of(NumericValue.of(prop.getSpawnX()), NumericValue.of(prop.getSpawnY()), NumericValue.of(prop.getSpawnZ()));
+        });
+
         put("game_difficulty", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getDifficulty().getName()));
         put("game_hardcore", c -> new NumericValue(c.s.getMinecraftServer().getSaveProperties().isHardcore()));
         put("game_storage_format", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getFormatName(c.s.getMinecraftServer().getSaveProperties().getVersion())));
diff --git a/src/main/java/carpet/utils/CarpetProfiler.java b/src/main/java/carpet/utils/CarpetProfiler.java
index 01a51d1f..53ba857e 100644
--- a/src/main/java/carpet/utils/CarpetProfiler.java
+++ b/src/main/java/carpet/utils/CarpetProfiler.java
@@ -31,7 +31,9 @@ public class CarpetProfiler
     private static int tick_health_elapsed = 0;
     private static TYPE test_type = TYPE.NONE; //1 for ticks, 2 for entities;
     private static long current_tick_start = 0;
-    private static final String[] GENERAL_SECTIONS = {"Network", "Autosave", "Async Tasks", "Datapacks",};
+    private static final String[] GENERAL_SECTIONS = {"Network", "Autosave", "Async Tasks", "Datapacks",
+            "Scarpet ace", "Scarpet legacy", "Scarpet events", "Scarpet schedule",
+            "Scarpet command", "Scarpet loading", "Scarpet app data"};
     private static final String[] SECTIONS = {
             "Spawning and Random Ticks", "Ticket Manager","Unloading",
             "Blocks", "Entities", "Block Entities",
diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json
index 83f5c19e..cff2f2e5 100644
--- a/src/main/resources/carpet.mixins.json
+++ b/src/main/resources/carpet.mixins.json
@@ -196,6 +196,7 @@
     "PlayerEntity_creativeNoClipMixin",
     "PistonBlockEntity_playerHandlingMixin",
     "BlockItem_creativeNoClipMixin",
+    "WallStandingBlockItem_creativeNoClipMixin",
     "LivingEntity_creativeFlyMixin",
     "BaseRangeDecorator_cleanLogsMixin"
   ],