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 a8b097dc..993620f7 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -66,11 +66,16 @@ Available shapes: * 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/network/ClientNetworkHandler.java b/src/main/java/carpet/network/ClientNetworkHandler.java index 96524117..07ec99df 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/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java index 0aaadb12..a1d1f071 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; @@ -57,7 +58,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 { @@ -122,7 +123,7 @@ public class ShapeDispatcher Function decoder = Param.decoders.get(key); if (decoder==null) { - CarpetSettings.LOG.error("Unknown parameter for shape: "+key); + CarpetSettings.LOG.info("Unknown parameter for shape: "+key); return null; } Value decodedValue = decoder.apply(tag.get(key)); @@ -131,13 +132,13 @@ public class ShapeDispatcher Value shapeValue = options.get("shape"); if (shapeValue == null) { - CarpetSettings.LOG.error("Shape id missing in "+ String.join(", ", tag.getKeys())); + CarpetSettings.LOG.info("Shape id missing in "+ String.join(", ", tag.getKeys())); return null; } Function,ExpiringShape> factory = ExpiringShape.shapeProviders.get(shapeValue.getString()); if (factory == null) { - CarpetSettings.LOG.error("Unknown shape: "+shapeValue.getString()); + CarpetSettings.LOG.info("Unknown shape: "+shapeValue.getString()); return null; } try @@ -146,7 +147,7 @@ public class ShapeDispatcher } catch (InternalExpressionException exc) { - CarpetSettings.LOG.error(exc.getMessage()); + CarpetSettings.LOG.info(exc.getMessage()); } return null; } @@ -225,7 +226,10 @@ public class ShapeDispatcher } // list of params that need to be there private final Set required = ImmutableSet.of("duration", "shape", "dim"); - private final Map optional = ImmutableMap.of("color", new NumericValue(-1), "player", Value.NULL); + private final Map optional = ImmutableMap.of( + "color", new NumericValue(-1), + "player", Value.NULL + ); protected Set requiredParams() {return required;} // list of params that can be there, with defaults protected Set optionalParams() {return optional.keySet();} @@ -234,7 +238,10 @@ public class ShapeDispatcher public static class Box extends ExpiringShape { private final Set required = ImmutableSet.of("from", "to"); - private final Map optional = ImmutableMap.of(); + private final Map optional = ImmutableMap.of( + "width", new NumericValue(2.0), + "fill", new NumericValue(0xffffff00) + ); @Override protected Set requiredParams() { return Sets.union(super.requiredParams(), required); } @Override @@ -243,6 +250,10 @@ public class ShapeDispatcher float x1, y1, z1; float x2, y2, z2; + float lineWidth; + + protected float fr, fg, fb, fa; + protected int fillColor; @Override protected void init(Map options) @@ -256,6 +267,12 @@ public class ShapeDispatcher x2 = NumericValue.asNumber(to.get(0)).getFloat(); y2 = NumericValue.asNumber(to.get(1)).getFloat(); z2 = NumericValue.asNumber(to.get(2)).getFloat(); + lineWidth = NumericValue.asNumber(options.getOrDefault("width", optional.get("width"))).getFloat(); + fillColor = NumericValue.asNumber(options.getOrDefault("fill", optional.get("fill"))).getInt(); + this.fr = (float)(fillColor >> 24 & 0xFF) / 255.0F; + this.fg = (float)(fillColor >> 16 & 0xFF) / 255.0F; + this.fb = (float)(fillColor >> 8 & 0xFF) / 255.0F; + this.fa = (float)(fillColor & 0xFF) / 255.0F; } @Override @@ -281,6 +298,8 @@ public class ShapeDispatcher hash = 31*hash + Float.hashCode(x2); hash = 31*hash + Float.hashCode(y2); hash = 31*hash + Float.hashCode(z2); + hash = 31*hash + Float.hashCode(lineWidth); + if (fa != 0.0) hash = 31*hash + fillColor; return hash; } @@ -308,7 +327,7 @@ public class ShapeDispatcher public static class Line extends ExpiringShape { private final Set required = ImmutableSet.of("from", "to"); - private final Map optional = ImmutableMap.of(); + private final Map optional = ImmutableMap.of("width", new NumericValue(2.0)); @Override protected Set requiredParams() { return Sets.union(super.requiredParams(), required); } @Override @@ -321,6 +340,7 @@ public class ShapeDispatcher float x1, y1, z1; float x2, y2, z2; + float lineWidth; @Override protected void init(Map options) @@ -334,6 +354,7 @@ public class ShapeDispatcher x2 = NumericValue.asNumber(to.get(0)).getFloat(); y2 = NumericValue.asNumber(to.get(1)).getFloat(); z2 = NumericValue.asNumber(to.get(2)).getFloat(); + lineWidth = NumericValue.asNumber(options.getOrDefault("width", optional.get("width"))).getFloat(); } @Override @@ -361,6 +382,7 @@ public class ShapeDispatcher hash = 31*hash + Float.hashCode(x2); hash = 31*hash + Float.hashCode(y2); hash = 31*hash + Float.hashCode(z2); + hash = 31*hash + Float.hashCode(lineWidth); return hash; } } @@ -374,16 +396,19 @@ public class ShapeDispatcher put("color", ShapeDispatcher::decodeInt); put("from", Vec3Param::decode); put("to", Vec3Param::decode); - + put("width", ShapeDispatcher::decodeFloat); + put("fill", ShapeDispatcher::decodeInt); }}; Map coders = new HashMap(){{ - put("shape", new ShapeParam()); put("player", new PlayerParam()); + put("shape", new ShapeParam()); put("dim", new DimensionParam()); put("duration", new DurationParam()); put("color", new ColorParam()); put("from", new FromParam()); put("to", new ToParam()); + put("width", new WidthParam()); + put("fill", new FillColorParam()); }}; Tag toTag(Value value); //validates value, returning null if not necessary to keep it and serialize Value validate(CarpetContext cc, Value value); // makes sure the value is proper @@ -452,8 +477,6 @@ public class ShapeDispatcher } public static abstract class NumericParam implements Param { - @Override - public Tag toTag(Value value) { return IntTag.of(NumericValue.asNumber(value).getInt()); } @Override public Value validate(CarpetContext cc, Value value) { @@ -471,15 +494,34 @@ public class ShapeDispatcher return ret; } } + public static abstract class PositiveFloatParam extends PositiveParam + { + @Override + public Tag toTag(Value value) { return FloatTag.of(NumericValue.asNumber(value).getFloat()); } + } + public static abstract class PositiveIntParam extends PositiveParam + { + @Override + public Tag toTag(Value value) { return IntTag.of(NumericValue.asNumber(value).getInt()); } + + } public static Value decodeInt(Tag tag) { return new NumericValue(((IntTag)tag).getInt()); } - public static class DurationParam extends PositiveParam + public static Value decodeFloat(Tag tag) { return new NumericValue(((FloatTag)tag).getFloat()); } + public static class DurationParam extends PositiveIntParam { @Override public boolean appliesTo(ExpiringShape shape) { return true; } @Override public String identify() { return "duration"; } } + public static class WidthParam extends PositiveFloatParam + { + @Override + public boolean appliesTo(ExpiringShape shape) { return shape instanceof Box || shape instanceof Line; } + @Override + public String identify() { return "width"; } + } public static abstract class Vec3Param implements Param { @@ -546,12 +588,22 @@ public class ShapeDispatcher } public static class ColorParam extends NumericParam { + @Override + public Tag toTag(Value value) { return IntTag.of(NumericValue.asNumber(value).getInt()); } @Override public boolean appliesTo(ExpiringShape shape) { return true; } @Override public String identify() { return "color"; } } + public static class FillColorParam extends ColorParam + { + @Override + public boolean appliesTo(ExpiringShape shape) { return shape instanceof Box; } + @Override + public String identify() { return "fill"; } + } + public static class FromParam extends Vec3Param { @Override diff --git a/src/main/java/carpet/script/utils/ShapesRenderer.java b/src/main/java/carpet/script/utils/ShapesRenderer.java index 4bb50aa7..46ee549b 100644 --- a/src/main/java/carpet/script/utils/ShapesRenderer.java +++ b/src/main/java/carpet/script/utils/ShapesRenderer.java @@ -91,12 +91,9 @@ public class ShapesRenderer if (shape == null) return; BiFunction> shapeFactory; shapeFactory = renderedShapes.get(tag.getString("shape")); - - - if (shapeFactory == null) { - CarpetSettings.LOG.error("Unrecognized shape: "+tag.getString("shape")); + CarpetSettings.LOG.info("Unrecognized shape: "+tag.getString("shape")); } else { @@ -161,7 +158,7 @@ public class ShapesRenderer @Override public void render(Tessellator tessellator, BufferBuilder bufferBuilder, float cx, float cy, float cz) { - RenderSystem.lineWidth(2.0F); + RenderSystem.lineWidth(shape.lineWidth); bufferBuilder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3 drawBoxWireGLLines(bufferBuilder, shape.x1 - cx-renderEpsilon, shape.y1 - cy-renderEpsilon, shape.z1 - cz-renderEpsilon, @@ -173,12 +170,13 @@ public class ShapesRenderer @Override public void render2pass(Tessellator tessellator, BufferBuilder bufferBuilder, float cx, float cy, float cz) { + if (shape.fa == 0.0) return; RenderSystem.lineWidth(1.0F); bufferBuilder.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR); // 3 drawBox(bufferBuilder, shape.x1-cx-renderEpsilon, shape.y1-cy-renderEpsilon, shape.z1-cz-renderEpsilon, shape.x2-cx+renderEpsilon, shape.y2-cy+renderEpsilon, shape.z2-cz+renderEpsilon, - shape.r, shape.g, shape.b, shape.a/6 + shape.fr, shape.fg, shape.fb, shape.fa ); tessellator.draw(); } @@ -193,7 +191,7 @@ public class ShapesRenderer @Override public void render(Tessellator tessellator, BufferBuilder bufferBuilder, float cx, float cy, float cz) { - RenderSystem.lineWidth(2.0F); + RenderSystem.lineWidth(shape.lineWidth); bufferBuilder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3 drawLine(bufferBuilder, shape.x1-cx-renderEpsilon, shape.y1-cy-renderEpsilon, shape.z1-cz-renderEpsilon,