diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md
index 99523a4f..a8ab3b82 100644
--- a/docs/scarpet/Full.md
+++ b/docs/scarpet/Full.md
@@ -906,8 +906,11 @@ sleep(50)
### `time()`
-Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`.
-It returns a float, which has 1 microsecond precision (0.001 ms)
+Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`, which varies from system to
+system and from Java to Java. This measure should NOT be used to determine the current (date)time, but to measure
+durations of things.
+it returns a float with time in milliseconds (ms) for convenience and microsecond (μs) resolution for sanity.
+
start_time = time();
@@ -1513,7 +1516,7 @@ range(5,10) => [5, 6, 7, 8, 9]
range(20, 10, -2) => [20, 18, 16, 14, 12]
-### `element(list, index)` (deprecated)
+### `element(list, index)(deprecated)`
Legacy support for older method that worked only on lists. Please use `get(...)` for equivalent support, or `.`
operator. Also previous unique behaviours with `put` on lists has been removed to support all type of
@@ -3103,24 +3106,69 @@ and `voice`. `pos` can be either a block, triple of coords, or a list of thee nu
## Particles
-### `particle(name, pos, count?. spread?, speed?, playername?)`
+### `particle(name, pos, count?. spread?, speed?, player?)`
Renders a cloud of particles `name` centered around `pos` position, by default `count` 10 of them, default `speed`
of 0, and to all players nearby, but these options can be changed via optional arguments. Follow vanilla `/particle`
command on details on those options. Valid particle names are
for example `'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'`.
-### `particle_line(name, pos, pos2, density?)`
+### `particle_line(name, pos, pos2, density?, player?)`
Renders a line of particles from point `pos` to `pos2` with supplied density (defaults to 1), which indicates how far
-apart you would want particles to appear, so `0.1` means one every 10cm.
+apart you would want particles to appear, so `0.1` means one every 10cm. If a player (or player name) is supplied, only
+that player will receive particles.
-### `particle_rect(name, pos, pos2, density?)`
-Renders a cuboid of particles between points `pos` and `pos2` with supplied density.
+### `particle_box(name, pos, pos2, density?, player?)`
+### `particle_rect` (deprecated)
+
+Renders a cuboid of particles between points `pos` and `pos2` with supplied density. If a player (or player name) is
+supplied, only that player will receive particles.
## Markers
+### `draw_shape(shape, duration, key?, value?, ... )`, `draw_shape(shape, duration, l(key?, value?, ... ))`, `draw_shape(shape, duration, attribute_map)`
+
+Draws a shape in the world that will expire in `duration` ticks. Other attributes of the shape should be provided as
+consecutive key - value argument pairs, either as next arguments, or packed in a list, or supplied as a proper key-value
+`map`. Arguments may include shared shape attributes, which are all optional, as well as shape-specific attributes, that
+could be either optional or required. Shapes will draw properly on all carpet clients. Other connected players that don't
+have carpet installed will still be able to see the required shapes in the form of dust particles. Replacement shapes
+are not required to follow all attributes precisely, but will allow vanilla clients to receive some experience of your
+apps. One of the attributes that will definitely not be honored is the duration - particles will be send once
+per shape and last whatever they typically last in the game.
+
+Shapes will fail to draw and raise a runtime error if not all its required parameters
+are specified and all available shapes have some parameters that are required, so make sure to have them in place:
+
+On the client, shapes can recognize that they are being redrawn again with the same parameters, disregarding the
+duration parameter. This updates the expiry on the drawn shape to the new value, instead of adding new shape in its
+place. This can be used for toggling the shapes on and off that has been send previously with very large durations,
+or simply refresh the shapes periodically in more dynamic applications.
+
+Optional shared shape attributes:
+ * `color` - integer value indicating the main color of the shape in the form of red, green, blue and alpha components
+ 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.
+
+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
+
+ * `'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
+
### `create_marker(text, pos, rotation?, block?)`
Spawns a (permanent) marker entity with text or block at position. Returns that entity for further manipulations.
diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md
index 7d9da627..993620f7 100644
--- a/docs/scarpet/api/Auxiliary.md
+++ b/docs/scarpet/api/Auxiliary.md
@@ -36,19 +36,46 @@ supplied, only that player will receive particles.
## Markers
-### `marker_box(from, to, duration?, color?, player?)`
+### `draw_shape(shape, duration, key?, value?, ... )`, `draw_shape(shape, duration, l(key?, value?, ... ))`, `draw_shape(shape, duration, attribute_map)`
-Draws a box with `from` and `to` corners in the box. The boxes will dissipate after `duration` ticks (defaults to 10).
-The color is an integer that indicate red, green, blue and alpha components in the form of `0xRRGGBBAA`, with the
-default of `-1`, so white opaque, or `0xFFFFFFFF`. If player is specified, only displays for that player, otherwise all
-players will see them. `marker_box` requires proper client synchronization to display these, and on non-supported
-clients, the box will be replaced with close matching dust particles, so using of `marker_box` is safe to use for
-scripts regardless who is targeted. The only consideration is that with vanilla clients, duration of particles cannot
-be controlled, and last whatever the original particles do.
+Draws a shape in the world that will expire in `duration` ticks. Other attributes of the shape should be provided as
+consecutive key - value argument pairs, either as next arguments, or packed in a list, or supplied as a proper key-value
+`map`. Arguments may include shared shape attributes, which are all optional, as well as shape-specific attributes, that
+could be either optional or required. Shapes will draw properly on all carpet clients. Other connected players that don't
+have carpet installed will still be able to see the required shapes in the form of dust particles. Replacement shapes
+are not required to follow all attributes precisely, but will allow vanilla clients to receive some experience of your
+apps. One of the attributes that will definitely not be honored is the duration - particles will be send once
+per shape and last whatever they typically last in the game.
-### `marker_line(from, to, duration?, color?, player?)`
+Shapes will fail to draw and raise a runtime error if not all its required parameters
+are specified and all available shapes have some parameters that are required, so make sure to have them in place:
-Draws a marker line on client's screen. Uses similar setup as `marker_box`.
+On the client, shapes can recognize that they are being redrawn again with the same parameters, disregarding the
+duration parameter. This updates the expiry on the drawn shape to the new value, instead of adding new shape in its
+place. This can be used for toggling the shapes on and off that has been send previously with very large durations,
+or simply refresh the shapes periodically in more dynamic applications.
+
+Optional shared shape attributes:
+ * `color` - integer value indicating the main color of the shape in the form of red, green, blue and alpha components
+ 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.
+
+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
+
+ * `'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
### `create_marker(text, pos, rotation?, block?)`
diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt
index 81803965..279adc94 100644
--- a/docs/scarpet/resources/editors/idea/2.txt
+++ b/docs/scarpet/resources/editors/idea/2.txt
@@ -17,6 +17,7 @@ current_dimension
day_time
destroy
diamond
+draw_shape
drop_item
emitted_light
entity_area
diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml
index c17b3811..64158b61 100644
--- a/docs/scarpet/resources/editors/npp/scarpet.xml
+++ b/docs/scarpet/resources/editors/npp/scarpet.xml
@@ -70,7 +70,9 @@
run save tick_time world_time day_time last_tick_times 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
+ plop harvest destroy schedule nbt draw_shape create_marker remove_all_markers
+
+ load_app_data store_app_data escape_nbt statistic
euler pi null true false
x y z p
diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java
index ae2874cc..5b6ccf29 100644
--- a/src/main/java/carpet/CarpetSettings.java
+++ b/src/main/java/carpet/CarpetSettings.java
@@ -62,7 +62,7 @@ public class CarpetSettings
@Rule(
desc = "sets the language for carpet",
category = FEATURE,
- options = {"none", "zh_cn"},
+ options = {"none", "zh_cn", "zh_tw"},
strict = false,
validate = LanguageValidator.class
)
diff --git a/src/main/java/carpet/network/ClientNetworkHandler.java b/src/main/java/carpet/network/ClientNetworkHandler.java
index f155040f..294bb08e 100644
--- a/src/main/java/carpet/network/ClientNetworkHandler.java
+++ b/src/main/java/carpet/network/ClientNetworkHandler.java
@@ -35,7 +35,7 @@ public class ClientNetworkHandler
}
});
put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false));
- put("renderShape", (p, t) -> {
+ put("scShape", (p, t) -> {
if (CarpetClient.shapes != null)
CarpetClient.shapes.addShape((CompoundTag)t);
});
diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java
index 200a02f5..e3ebaabf 100644
--- a/src/main/java/carpet/script/CarpetEventServer.java
+++ b/src/main/java/carpet/script/CarpetEventServer.java
@@ -128,7 +128,13 @@ public class CarpetEventServer
List argv = argumentSupplier.get(); // empty for onTickDone
ServerCommandSource source = cmdSourceSupplier.get();
assert argv.size() == reqArgs;
- callList.removeIf(call -> !CarpetServer.scriptServer.runas(source, call.host, call.function, argv)); // this actually does the calls
+ List fails = new ArrayList<>();
+ for (Callback call: callList)
+ {
+ if (!CarpetServer.scriptServer.runas(source, call.host, call.function, argv))
+ fails.add(call);
+ }
+ for (Callback call : fails) callList.remove(call);
}
}
public boolean addEventCall(ServerCommandSource source, String hostName, String funName, Function verifier)
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index 67395cb6..715ccc81 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -2704,7 +2704,7 @@ public class CarpetExpression
ServerPlayerEntity player = null;
if (params.containsKey("player"))
{
- player = (ServerPlayerEntity) ((EntityValue)params.get("player")).getEntity();
+ player = (ServerPlayerEntity)((EntityValue)params.get("player")).getEntity();
}
ShapeDispatcher.sendShape(
(player==null)?cc.s.getWorld().getPlayers():Collections.singletonList(player),
diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java
index bd2b669e..94f3b2fc 100644
--- a/src/main/java/carpet/script/CarpetScriptHost.java
+++ b/src/main/java/carpet/script/CarpetScriptHost.java
@@ -96,7 +96,7 @@ public class CarpetScriptHost extends ScriptHost
public void addUserDefinedFunction(Context ctx, Module module, String funName, FunctionValue function)
{
super.addUserDefinedFunction(ctx, module, funName, function);
- // mcarpet
+ if (ctx.host.main != module) return; // not dealing with automatic imports / exports /configs / apps from imports
if (funName.startsWith("__")) // potential fishy activity
{
if (funName.startsWith("__on_")) // here we can make a determination if we want to only accept events from main module.
diff --git a/src/main/java/carpet/script/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java
index 42364245..62a47034 100644
--- a/src/main/java/carpet/script/utils/ShapeDispatcher.java
+++ b/src/main/java/carpet/script/utils/ShapeDispatcher.java
@@ -19,6 +19,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.arguments.ParticleArgumentType;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
@@ -59,7 +60,7 @@ public class ShapeDispatcher
if (ServerNetworkHandler.validCarpetPlayers.contains(player))
{
if (tag == null) tag = ExpiringShape.toTag(params);
- ServerNetworkHandler.sendCustomCommand(player,"renderShape", tag);
+ ServerNetworkHandler.sendCustomCommand(player,"scShape", tag);
}
else
{
@@ -105,16 +106,14 @@ public class ShapeDispatcher
public static ExpiringShape create(CarpetContext cc, String shapeType, Map userParams)
{
userParams.put("shape", new StringValue(shapeType));
- Map validatedParams = new HashMap<>();
- userParams.forEach((key, value) ->
- {
+ userParams.keySet().forEach(key -> {
Param param = Param.coders.get(key);
if (param==null) throw new InternalExpressionException("Unknown feature for shape: "+key);
- validatedParams.put(key, param.validate(cc, value));
+ userParams.put(key, param.validate(cc, userParams.get(key)));
});
Function