From d9ed35676ff77e95fb60f9178ff7dac285b27d43 Mon Sep 17 00:00:00 2001 From: Ghoulboy Date: Sun, 7 Jun 2020 22:57:04 +0200 Subject: [PATCH 01/10] Added hunger, saturation and exhaustion modification and querying (#286) * Added hunger querying and modification * Cleaning up old code * forgot semicolon * Added saturation querying and modification and exhaustion modification working on exhaustion querying * added if statements to avoid crash * more if statements * o ye boi! Co-authored-by: Ghoulboy78 <53367549+Ghoulboy-78@users.noreply.github.com> --- .../carpet/fakes/HungerManagerInterface.java | 5 +++ ...HungerManagerMixin_scarpetEventsMixin.java | 20 +++++++++ .../java/carpet/script/value/EntityValue.java | 43 ++++++++++++++----- src/main/resources/carpet.mixins.json | 2 +- 4 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 src/main/java/carpet/fakes/HungerManagerInterface.java create mode 100644 src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java diff --git a/src/main/java/carpet/fakes/HungerManagerInterface.java b/src/main/java/carpet/fakes/HungerManagerInterface.java new file mode 100644 index 00000000..028c0bf3 --- /dev/null +++ b/src/main/java/carpet/fakes/HungerManagerInterface.java @@ -0,0 +1,5 @@ +package carpet.fakes; + +public interface HungerManagerInterface { + float getExhaustion(); +} diff --git a/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java b/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java new file mode 100644 index 00000000..c9f341e3 --- /dev/null +++ b/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java @@ -0,0 +1,20 @@ +package carpet.mixins; + +import carpet.fakes.EntityInterface; +import carpet.fakes.HungerManagerInterface; +import carpet.script.EntityEventsGroup; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.player.HungerManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(HungerManager.class) +public class HungerManagerMixin_scarpetEventsMixin implements HungerManagerInterface { + @Shadow private float exhaustion; + public float getExhaustion(){ + return exhaustion; + } +} \ No newline at end of file diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 68c3f14d..faec676c 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -1,17 +1,21 @@ package carpet.script.value; +import carpet.CarpetSettings; import carpet.fakes.EntityInterface; import carpet.fakes.ItemEntityInterface; import carpet.fakes.LivingEntityInterface; import carpet.fakes.MobEntityInterface; +import carpet.fakes.HungerManagerInterface; import carpet.helpers.Tracer; import carpet.patches.EntityPlayerMPFake; import carpet.script.CarpetContext; import carpet.script.EntityEventsGroup; import carpet.script.argument.Vector3Argument; import carpet.script.exception.InternalExpressionException; +import carpet.utils.Messenger; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.entity.player.HungerManager; import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket; import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; @@ -285,6 +289,21 @@ public class EntityValue extends Value put("sneaking", (e, a) -> e.isSneaking()?Value.TRUE:Value.FALSE); put("sprinting", (e, a) -> e.isSprinting()?Value.TRUE:Value.FALSE); put("swimming", (e, a) -> e.isSwimming()?Value.TRUE:Value.FALSE); + put("hunger", (e, a) -> { + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getFoodLevel()); + return Value.NULL; + }); + put("saturation", (e, a) -> { + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getSaturationLevel()); + return Value.NULL; + }); + + put("exhaustion",(e, a)->{ + if(e instanceof PlayerEntity) + return new NumericValue(((HungerManagerInterface)((PlayerEntity) e).getHungerManager()).getExhaustion()); + return Value.NULL; + }); + put("jumping", (e, a) -> { if (e instanceof LivingEntity) { @@ -934,20 +953,24 @@ public class EntityValue extends Value } }); - put("silent",(e,v)->{ - e.setSilent(v.getBoolean()); + put("silent",(e,v)-> e.setSilent(v.getBoolean())); + + put("gravity",(e,v)-> e.setNoGravity(!v.getBoolean())); + + put("invulnerable",(e,v)-> e.setInvulnerable(v.getBoolean())); + + put("fire",(e,v)-> e.setFireTicks((int)NumericValue.asNumber(v).getLong())); + + put("hunger", (e, v)-> { + if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setFoodLevel((int) NumericValue.asNumber(v).getLong()); }); - put("gravity",(e,v)->{ - e.setNoGravity(!v.getBoolean()); + put("exhaustion", (e, v)-> { + if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong()); }); - put("invulnerable",(e,v)->{ - e.setInvulnerable(v.getBoolean()); - }); - - put("fire",(e,v)->{ - e.setFireTicks((int)NumericValue.asNumber(v).getLong()); + put("saturation", (e, v)-> { + if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong()); }); // gamemode [check] diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 4b63eb8e..65ec4c40 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -155,7 +155,7 @@ "StatType_scarpetMixin", "PointOfInterest_scarpetMixin", "ServerWorld_scarpetEventMixin", - + "HungerManagerMixin_scarpetEventsMixin", "PistonBlock_movableTEMixin", "PistonBlockEntity_movableTEMixin", From 73cb9d696821928ad9f73764b4dad6148e5c84e8 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 7 Jun 2020 18:53:17 -0300 Subject: [PATCH 02/10] added hunger, saturation, and exhaustion entity API calls. --- docs/scarpet/api/Entities.md | 18 ++++++++++++++++++ .../carpet/fakes/HungerManagerInterface.java | 3 ++- .../HungerManagerMixin_scarpetEventsMixin.java | 16 ++++++++-------- .../java/carpet/script/value/EntityValue.java | 7 +++++-- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 36365a78..f2d7bb97 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -314,6 +314,13 @@ query(p,'effect','resistance') => null Number indicating remaining entity health, or `null` if not applicable. +### `query(e, 'hunger')` +### `query(e, 'saturation')` +### `query(e, 'exhaustion')` + +Retrieves player hunger related information. For non-players, returns `null`. + + ### `query(e, 'holds', slot?)` Returns triple of short name, stack count, and NBT of item held in `slot`, or `null` if nothing or not applicable. Available options for `slot` are: @@ -513,6 +520,17 @@ Toggles gravity for the entity. Will set entity on fire for `ticks` ticks. Set to 0 to extinguish. +### `modify(e, 'hunger', value)` +### `modify(e, 'saturation', value)` +### `modify(e, 'exhaustion', value)` + +Modifies directly player raw hunger components. Has no effect on non-players + +### `modify(e, 'add_exhaustion', value)` + +adds exhaustion value to the current player exhaustion level - that's the method you probably want to use +to manipulate how much 'food' some action cost. + ## Entity Events There is a number of events that happen to entities that you can attach your own code to in the form of event handlers. diff --git a/src/main/java/carpet/fakes/HungerManagerInterface.java b/src/main/java/carpet/fakes/HungerManagerInterface.java index 028c0bf3..b4df2334 100644 --- a/src/main/java/carpet/fakes/HungerManagerInterface.java +++ b/src/main/java/carpet/fakes/HungerManagerInterface.java @@ -1,5 +1,6 @@ package carpet.fakes; public interface HungerManagerInterface { - float getExhaustion(); + float getExhaustionCM(); + void setExhaustionCM(float exhaust); } diff --git a/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java b/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java index c9f341e3..3e6fcdc2 100644 --- a/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java @@ -1,20 +1,20 @@ package carpet.mixins; -import carpet.fakes.EntityInterface; import carpet.fakes.HungerManagerInterface; -import carpet.script.EntityEventsGroup; -import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.HungerManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(HungerManager.class) public class HungerManagerMixin_scarpetEventsMixin implements HungerManagerInterface { @Shadow private float exhaustion; - public float getExhaustion(){ + public float getExhaustionCM(){ return exhaustion; } -} \ No newline at end of file + + @Override + public void setExhaustionCM(float exhaust) + { + exhaustion = exhaust; + } +} diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index faec676c..0b053f2c 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -299,8 +299,7 @@ public class EntityValue extends Value }); put("exhaustion",(e, a)->{ - if(e instanceof PlayerEntity) - return new NumericValue(((HungerManagerInterface)((PlayerEntity) e).getHungerManager()).getExhaustion()); + if(e instanceof PlayerEntity) return new NumericValue(((HungerManagerInterface)((PlayerEntity) e).getHungerManager()).getExhaustionCM()); return Value.NULL; }); @@ -966,6 +965,10 @@ public class EntityValue extends Value }); put("exhaustion", (e, v)-> { + if(e instanceof PlayerEntity) ((HungerManagerInterface) ((PlayerEntity) e).getHungerManager()).setExhaustionCM(NumericValue.asNumber(v).getFloat()); + }); + + put("add_exhaustion", (e, v)-> { if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong()); }); From 2f08ee8c0149518b79f67c672838779ed62776a0 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 7 Jun 2020 19:32:08 -0300 Subject: [PATCH 03/10] player inventories can be specified as strings player enderchest inventories can also be specified as a single string. --- docs/scarpet/api/Inventories.md | 7 ++++- .../script/value/NBTSerializableValue.java | 30 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/docs/scarpet/api/Inventories.md b/docs/scarpet/api/Inventories.md index 972cf5b5..5ef96c7a 100644 --- a/docs/scarpet/api/Inventories.md +++ b/docs/scarpet/api/Inventories.md @@ -4,7 +4,12 @@ Most functions in this category require inventory as the first argument. Inventory could be specified by an entity, or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory require -two arguments, keyword `'enderchest'`, followed by the player entity argument. If the entity or a block doesn't have +two arguments, keyword `'enderchest'`, followed by the player entity argument, or a single argument as a string of a +form: `'enderchest_steve'`. If your player name starts with enderchest, it can be always accessed by passing a player +entity value. If all else fails, it will try to identify first three arguments as coordinates of a block position of +a block inventory. Player inventories can also be called by their name. + + If the entity or a block doesn't have an inventory, they typically do nothing and return null. Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item. diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 07256909..b4ce70b5 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -27,6 +27,7 @@ import net.minecraft.nbt.StringNbtReader; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; @@ -35,6 +36,7 @@ import net.minecraft.util.math.Box; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Supplier; import java.util.regex.Matcher; @@ -201,16 +203,30 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return null; return new InventoryLocator(pos, pos, inv, offset + 1); } - else if (v1.getString().equalsIgnoreCase("enderchest")) + else if (v1 instanceof StringValue) { - Value v2 = params.get(1 + offset).evalValue(c); - if (!(v2 instanceof EntityValue) || !(((EntityValue) v2).getEntity() instanceof PlayerEntity)) + String strVal = v1.getString().toLowerCase(Locale.ROOT); + if (strVal.equals("enderchest")) { - throw new InternalExpressionException("enderchest inventory requires player argument"); + Value v2 = params.get(1 + offset).evalValue(c); + ServerPlayerEntity player = EntityValue.getPlayerByValue(c.s.getMinecraftServer(), v2); + if (player == null) throw new InternalExpressionException("enderchest inventory requires player argument"); + return new InventoryLocator(player, player.getBlockPos(), player.getEnderChestInventory(), offset + 2, true); + } + else + { + boolean isEnder = strVal.startsWith("enderchest_"); + if (isEnder) strVal = strVal.substring(11); // len("enderchest_") + ServerPlayerEntity player = c.s.getMinecraftServer().getPlayerManager().getPlayer(strVal); + if (player == null) throw new InternalExpressionException("String description of an inventory should either denote a player or player's enderchest"); + return new InventoryLocator( + player, + player.getBlockPos(), + isEnder ? player.getEnderChestInventory() : player.inventory, + offset + 1, + isEnder + ); } - PlayerEntity e = (PlayerEntity)((EntityValue) v2).getEntity(); - inv = e.getEnderChestInventory(); - return new InventoryLocator(e, e.getBlockPos(), inv, offset + 2, true); } BlockPos pos = new BlockPos( NumericValue.asNumber(v1).getDouble(), From 2739ef9d444577eb55de6873172ade3d7aebef27 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 7 Jun 2020 22:44:52 -0300 Subject: [PATCH 04/10] added sync-preferred option to camera path --- .../resources/assets/carpet/scripts/camera.sc | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc index 57bf59f2..993825db 100644 --- a/src/main/resources/assets/carpet/scripts/camera.sc +++ b/src/main/resources/assets/carpet/scripts/camera.sc @@ -49,6 +49,11 @@ __command() -> print(' "/camera show": Show current path particles'); print(' color of particles used is different for different players'); print(' "/camera hide": Hide path display'); + print(' "/camera prefer_smooth_play": '); + print(' Eat CPU spikes and continue as usual'); + print(' "/camera prefer_synced_play": '); + print(' After CPU spikes jump to where you should be'); + print(''); '' ); @@ -483,6 +488,11 @@ hide() -> ); // runs the player on the path +global_prefer_sync = false; + +prefer_smooth_play() -> (global_prefer_sync = false; 'Smooth path play'); +prefer_synced_play() -> (global_prefer_sync = true; 'Synchronized path play'); + play() -> ( if (err = __is_not_valid_for_motion(), exit(err)); @@ -501,6 +511,7 @@ play() -> global_playing_path = true; mspt = 1000 / 60; start_time = time(); + very_start = start_time; point = 0; p = __get_player(); try ( @@ -514,9 +525,14 @@ play() -> modify(p, 'location', v); point += 1; end_time = time(); - took = end_time - start_time; - if (took < mspt, sleep(mspt-took)); - start_time = time() + if (global_prefer_sync, + should_be = very_start + mspt*point; + if (end_time < should_be, sleep(should_be-end_time) ) + , + took = end_time - start_time; + if (took < mspt, sleep(mspt-took)); + start_time = time() + ) ) ); ); From 2f102a77857df961ddbb5978c615114dae0ce475 Mon Sep 17 00:00:00 2001 From: gnembon Date: Mon, 8 Jun 2020 21:24:56 -0300 Subject: [PATCH 05/10] Switched to 64FNV-1a as shapes keying algorithm as collision resistant Allow shapes to be sent in bulk as one / few packets Optimized rendering of shapes that not render all faces Moving shapes render now smoothly with higher framerates --- .../DebugRenderer_scarpetRenderMixin.java | 23 +- .../WorldRenderer_scarpetRenderMixin.java | 32 +++ .../carpet/network/ClientNetworkHandler.java | 7 +- .../java/carpet/script/CarpetExpression.java | 45 +--- .../carpet/script/utils/ShapeDispatcher.java | 249 ++++++++++++++---- .../carpet/script/utils/ShapesRenderer.java | 232 ++++++++++------ .../assets/carpet/scripts/chunk_display.sc | 181 +++++++++++++ .../assets/carpet/scripts/overlay.sc | 16 +- src/main/resources/carpet.mixins.json | 3 +- 9 files changed, 595 insertions(+), 193 deletions(-) create mode 100644 src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java create mode 100644 src/main/resources/assets/carpet/scripts/chunk_display.sc diff --git a/src/main/java/carpet/mixins/DebugRenderer_scarpetRenderMixin.java b/src/main/java/carpet/mixins/DebugRenderer_scarpetRenderMixin.java index a57a3b26..0ed9e836 100644 --- a/src/main/java/carpet/mixins/DebugRenderer_scarpetRenderMixin.java +++ b/src/main/java/carpet/mixins/DebugRenderer_scarpetRenderMixin.java @@ -1,11 +1,7 @@ package carpet.mixins; import carpet.network.CarpetClient; -import carpet.script.utils.ShapesRenderer; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.debug.DebugRenderer; -import net.minecraft.client.util.math.MatrixStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -14,25 +10,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(DebugRenderer.class) public class DebugRenderer_scarpetRenderMixin { - private ShapesRenderer shapes; - - @Inject(method = "", at = @At("RETURN")) - private void addRenderers(MinecraftClient client, CallbackInfo ci) - { - shapes = new ShapesRenderer(client); - CarpetClient.shapes = shapes; - } - @Inject(method = "reset", at = @At("HEAD")) private void resetScarpetRenderes(CallbackInfo ci) { - shapes.reset(); + if (CarpetClient.shapes != null) + CarpetClient.shapes.reset(); } - - @Inject(method = "render", at = @At("HEAD")) - private void renderScarpetThings(MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) - { - shapes.render(matrices, vertexConsumers, cameraX, cameraY, cameraZ); - } - } diff --git a/src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java b/src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java new file mode 100644 index 00000000..df777e69 --- /dev/null +++ b/src/main/java/carpet/mixins/WorldRenderer_scarpetRenderMixin.java @@ -0,0 +1,32 @@ +package carpet.mixins; + +import carpet.network.CarpetClient; +import carpet.script.utils.ShapesRenderer; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.BufferBuilderStorage; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.util.math.Matrix4f; +import net.minecraft.client.util.math.MatrixStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(WorldRenderer.class) +public class WorldRenderer_scarpetRenderMixin +{ + @Inject(method = "", at = @At("RETURN")) + private void addRenderers(MinecraftClient client, BufferBuilderStorage bufferBuilders, CallbackInfo ci) + { + CarpetClient.shapes = new ShapesRenderer(client); + } + + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/debug/DebugRenderer;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;DDD)V")) + private void renderScarpetThings(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) + { + if (CarpetClient.shapes != null) CarpetClient.shapes.render(camera, tickDelta); + } +} diff --git a/src/main/java/carpet/network/ClientNetworkHandler.java b/src/main/java/carpet/network/ClientNetworkHandler.java index 07ec99df..c65e2ac6 100644 --- a/src/main/java/carpet/network/ClientNetworkHandler.java +++ b/src/main/java/carpet/network/ClientNetworkHandler.java @@ -8,6 +8,7 @@ import io.netty.buffer.Unpooled; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.nbt.AbstractNumberTag; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket; import net.minecraft.util.PacketByteBuf; @@ -35,10 +36,14 @@ public class ClientNetworkHandler } }); put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false)); - put("scShape", (p, t) -> { + put("scShape", (p, t) -> { // deprecated if (CarpetClient.shapes != null) CarpetClient.shapes.addShape((CompoundTag)t); }); + put("scShapes", (p, t) -> { + if (CarpetClient.shapes != null) + CarpetClient.shapes.addShapes((ListTag) t); + }); }}; diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 06d324b3..711d07d6 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -2662,47 +2662,28 @@ public class CarpetExpression this.expr.addLazyFunction("draw_shape", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; - if (lv.size() < 3) throw new InternalExpressionException("'draw_shape' takes at least three parameters, shape name, duration, and its params"); - String shapeType = lv.get(0).evalValue(c).getString(); - Value duration = NumericValue.asNumber(lv.get(1).evalValue(c)); - Map params; - if (lv.size() == 3) + ServerPlayerEntity player[] = {null}; + List>> shapes = new ArrayList<>(); + if (lv.size() == 1) // bulk { - Value paramValue = lv.get(2).evalValue(c); - if (paramValue instanceof MapValue) + Value specLoad = lv.get(0).evalValue(c); + if (!(specLoad instanceof ListValue)) throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs"); + for (Value list : ((ListValue) specLoad).getItems()) { - params = new HashMap<>(); - ((MapValue) paramValue).getMap().entrySet().forEach(e -> params.put(e.getKey().getString(),e.getValue())); + if (!(list instanceof ListValue)) throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs"); + shapes.add( ShapeDispatcher.fromFunctionArgs(cc, ((ListValue) list).getItems(), player)); } - else if (paramValue instanceof ListValue) - { - params = ShapeDispatcher.parseParams(((ListValue) paramValue).getItems()); - } - else throw new InternalExpressionException("Parameters for 'draw_shape' need to be defined either in a list or a map"); } else { - List paramList = new ArrayList<>(); - for (int i=2; i < lv.size(); i++) paramList.add(lv.get(i).evalValue(c)); - params = ShapeDispatcher.parseParams(paramList); + List params = new ArrayList<>(); + for (LazyValue v : lv) params.add(v.evalValue(c)); + shapes.add(ShapeDispatcher.fromFunctionArgs(cc, params, player)); } - params.putIfAbsent("dim", new StringValue(cc.s.getWorld().getDimension().getType().toString())); - params.putIfAbsent("duration", duration); - ServerPlayerEntity player = null; - if (params.containsKey("player")) - { - player = EntityValue.getPlayerByValue(cc.s.getMinecraftServer(), params.get("player")); - if (player == null) - throw new InternalExpressionException("'player' parameter needs to represent an existing player"); - params.remove("player"); - } - - ShapeDispatcher.ExpiringShape shape = ShapeDispatcher.create(cc, shapeType, params); ShapeDispatcher.sendShape( - (player==null)?cc.s.getWorld().getPlayers():Collections.singletonList(player), - shape, - params + (player[0]==null)?cc.s.getWorld().getPlayers():Collections.singletonList(player[0]), + shapes ); return LazyValue.TRUE; }); diff --git a/src/main/java/carpet/script/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java index 5a66f723..770d7639 100644 --- a/src/main/java/carpet/script/utils/ShapeDispatcher.java +++ b/src/main/java/carpet/script/utils/ShapeDispatcher.java @@ -8,6 +8,7 @@ import carpet.script.exception.InternalExpressionException; import carpet.script.value.BlockValue; import carpet.script.value.EntityValue; import carpet.script.value.ListValue; +import carpet.script.value.MapValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; @@ -19,8 +20,10 @@ 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.DoubleTag; import net.minecraft.nbt.FloatTag; import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.particle.ParticleEffect; @@ -32,7 +35,9 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.registry.Registry; import net.minecraft.world.dimension.DimensionType; +import org.apache.commons.lang3.tuple.Pair; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -49,23 +54,90 @@ public class ShapeDispatcher { private static final Map particleCache = new HashMap<>(); - public static void sendShape(List players, ExpiringShape shape, Map params) + public static Pair> fromFunctionArgs( + CarpetContext cc, + List lv, + ServerPlayerEntity[] playerRef + ) { - Consumer alternative = null; - CompoundTag tag = null; + if (lv.size() < 3) throw new InternalExpressionException("'draw_shape' takes at least three parameters, shape name, duration, and its params"); + String shapeType = lv.get(0).getString(); + Value duration = NumericValue.asNumber(lv.get(1)); + Map params; + if (lv.size() == 3) + { + Value paramValue = lv.get(2); + if (paramValue instanceof MapValue) + { + params = new HashMap<>(); + ((MapValue) paramValue).getMap().entrySet().forEach(e -> params.put(e.getKey().getString(),e.getValue())); + } + else if (paramValue instanceof ListValue) + { + params = parseParams(((ListValue) paramValue).getItems()); + } + else throw new InternalExpressionException("Parameters for 'draw_shape' need to be defined either in a list or a map"); + } + else + { + List paramList = new ArrayList<>(); + for (int i=2; i < lv.size(); i++) paramList.add(lv.get(i)); + params = ShapeDispatcher.parseParams(paramList); + } + params.putIfAbsent("dim", new StringValue(cc.s.getWorld().getDimension().getType().toString())); + params.putIfAbsent("duration", duration); + + if (params.containsKey("player")) + { + ServerPlayerEntity player = EntityValue.getPlayerByValue(cc.s.getMinecraftServer(), params.get("player")); + if (player == null) + throw new InternalExpressionException("'player' parameter needs to represent an existing player"); + params.remove("player"); + playerRef[0] = player; + } + return Pair.of(ShapeDispatcher.create(cc, shapeType, params), params); + } + + public static void sendShape(List players, List>> shapes) + { + List clientPlayers = new ArrayList<>(); + List alternativePlayers = new ArrayList<>(); for (ServerPlayerEntity player : players) { if (ServerNetworkHandler.validCarpetPlayers.contains(player)) { - if (tag == null) tag = ExpiringShape.toTag(params); - ServerNetworkHandler.sendCustomCommand(player,"scShape", tag); + clientPlayers.add(player); } else { - if (alternative == null) alternative = shape.alternative(); - alternative.accept(player); + alternativePlayers.add(player); } } + if (!clientPlayers.isEmpty()) + { + + ListTag tag = new ListTag(); + int tagcount = 0; + for (Pair> s : shapes) + { + tag.add(ExpiringShape.toTag(s.getRight())); // 4000 shapes limit boxes + if (tagcount++>1000) + { + tagcount = 0; + Tag finalTag = tag; + clientPlayers.forEach( p -> ServerNetworkHandler.sendCustomCommand(p, "scShapes", finalTag)); + tag = new ListTag(); + } + } + Tag finalTag = tag; + if (tag.size() > 0) clientPlayers.forEach( p -> ServerNetworkHandler.sendCustomCommand(p, "scShapes", finalTag)); + } + if (!alternativePlayers.isEmpty()) + { + List> alternatives = new ArrayList<>(); + shapes.forEach(s -> alternatives.add(s.getLeft().alternative())); + alternativePlayers.forEach(p -> alternatives.forEach( a -> a.accept(p))); + } } public static ParticleEffect getParticleData(String name) @@ -174,7 +246,7 @@ public class ShapeDispatcher protected float fr, fg, fb, fa; protected int fillColor; protected int duration = 0; - private int key; + private long key; protected int followEntity; protected DimensionType entityDimension; @@ -214,7 +286,7 @@ public class ShapeDispatcher duration = NumericValue.asNumber(options.get("duration")).getInt(); - lineWidth = NumericValue.asNumber(options.getOrDefault("width", optional.get("width"))).getFloat(); + lineWidth = NumericValue.asNumber(options.getOrDefault("line", optional.get("line"))).getFloat(); fillColor = NumericValue.asNumber(options.getOrDefault("fill", optional.get("fill"))).getInt(); this.fr = (float)(fillColor >> 24 & 0xFF) / 255.0F; @@ -254,19 +326,19 @@ public class ShapeDispatcher } public abstract Consumer alternative(); - public int key() + public long key() { if (key!=0) return key; key = calcKey(); return key; } - protected int calcKey() - { - int hash = 17; - hash = 31*hash + color; - hash = 31*hash + followEntity; - hash = 31*hash + Float.hashCode(lineWidth); - if (fa != 0.0) hash = 31*hash + fillColor; + protected long calcKey() + { // using FNV-1a algorithm + long hash = -3750763034362895579L; + hash ^= color; hash *= 1099511628211L; + hash ^= followEntity; hash *= 1099511628211L; + hash ^= Float.hashCode(lineWidth); hash *= 1099511628211L; + if (fa != 0.0) { hash = 31*hash + fillColor; hash *= 1099511628211L; } return hash; } // list of params that need to be there @@ -274,7 +346,7 @@ public class ShapeDispatcher private final Map optional = ImmutableMap.of( "color", new NumericValue(-1), "follow", new NumericValue(-1), - "width", new NumericValue(2.0), + "line", new NumericValue(2.0), "fill", new NumericValue(0xffffff00) ); protected Set requiredParams() {return required;} @@ -306,6 +378,7 @@ public class ShapeDispatcher super.init(options); from = vecFromValue(options.get("from")); to = vecFromValue(options.get("to")); + if (from.equals(to)) throw new InternalExpressionException("Box dimensions are invalid - cannot draw a zero-size boxes"); } @Override @@ -330,12 +403,12 @@ public class ShapeDispatcher } @Override - public int calcKey() + public long calcKey() { - int hash = super.calcKey(); - hash = 31*hash + 1; - hash = 31*hash + from.hashCode(); - hash = 31*hash + to.hashCode(); + long hash = super.calcKey(); + hash ^= 1; hash *= 1099511628211L; + hash ^= from.hashCode(); hash *= 1099511628211L; + hash ^= to.hashCode(); hash *= 1099511628211L; return hash; } @@ -389,6 +462,7 @@ public class ShapeDispatcher super.init(options); from = vecFromValue(options.get("from")); to = vecFromValue(options.get("to")); + if (from.equals(to)) throw new InternalExpressionException("Line dimensions are invalid - cannot draw a zero-length lines"); } @Override @@ -414,12 +488,12 @@ public class ShapeDispatcher } @Override - public int calcKey() + public long calcKey() { - int hash = super.calcKey(); - hash = 31*hash + 2; - hash = 31*hash + from.hashCode(); - hash = 31*hash + to.hashCode(); + long hash = super.calcKey(); + hash ^= 2; hash *= 1099511628211L; + hash ^= from.hashCode(); hash *= 1099511628211L; + hash ^= to.hashCode(); hash *= 1099511628211L; return hash; } } @@ -494,13 +568,13 @@ public class ShapeDispatcher };} @Override - public int calcKey() + public long calcKey() { - int hash = super.calcKey(); - hash = 31*hash + 3; - hash = 31*hash + center.hashCode(); - hash = 31*hash + Double.hashCode(radius); - hash = 31*hash + level; + long hash = super.calcKey(); + hash ^= 3; hash *= 1099511628211L; + hash ^= center.hashCode(); hash *= 1099511628211L; + hash ^= Double.hashCode(radius); hash *= 1099511628211L; + hash ^= level; hash *= 1099511628211L; return hash; } } @@ -514,7 +588,7 @@ public class ShapeDispatcher put("duration", new PositiveIntParam("duration")); put("color", new ColorParam("color")); put("follow", new EntityParam("follow")); - put("width", new PositiveFloatParam("width")); + put("line", new PositiveFloatParam("line")); put("fill", new ColorParam("fill")); put("from", new Vec3Param("from", false)); @@ -522,6 +596,9 @@ public class ShapeDispatcher put("center", new Vec3Param("center", false)); put("radius", new PositiveFloatParam("radius")); put("level", new PositiveIntParam("level")); + put("height", new PositiveFloatParam("height")); + put("axis", new StringChoiceParam("axis", "x", "y", "z")); + put("points", new PointsParam("points")); }}; protected String id; protected Param(String id) @@ -543,6 +620,24 @@ public class ShapeDispatcher public Value decode(Tag tag) { return new StringValue(tag.asString()); } } + public static class StringChoiceParam extends StringParam + { + private Set options; + public StringChoiceParam(String id, String ... options) + { + super(id); + this.options = Sets.newHashSet(options); + } + + + @Override + public Value validate(Map options, CarpetContext cc, Value value) + { + if (this.options.contains(value.getString())) return value; + return null; + } + } + public static class DimensionParam extends StringParam { protected DimensionParam() { super("dim"); } @@ -620,13 +715,17 @@ public class ShapeDispatcher } @Override public Value validate(Map options, CarpetContext cc, Value value) + { + return validate(this, options, cc, value, roundsUpForBlocks); + } + public static Value validate(Param p, Map options, CarpetContext cc, Value value, boolean roundsUp) { if (value instanceof BlockValue) { if (options.containsKey("follow")) - throw new InternalExpressionException(id+" parameter cannot use blocks as positions for relative positioning due to 'follow' attribute being present"); + throw new InternalExpressionException(p.id+" parameter cannot use blocks as positions for relative positioning due to 'follow' attribute being present"); BlockPos pos = ((BlockValue) value).getPos(); - int offset = roundsUpForBlocks?1:0; + int offset = roundsUp?1:0; return ListValue.of( new NumericValue(pos.getX()+offset), new NumericValue(pos.getY()+offset), @@ -636,18 +735,18 @@ public class ShapeDispatcher if (value instanceof ListValue) { List values = ((ListValue) value).getItems(); - if (values.size()!=3) throw new InternalExpressionException("'"+id+"' requires 3 numerical values"); + if (values.size()!=3) throw new InternalExpressionException("'"+p.id+"' requires 3 numerical values"); for (Value component : values) { if (!(component instanceof NumericValue)) - throw new InternalExpressionException("'"+id+"' requires 3 numerical values"); + throw new InternalExpressionException("'"+p.id+"' requires 3 numerical values"); } return value; } if (value instanceof EntityValue) { if (options.containsKey("follow")) - throw new InternalExpressionException(id+" parameter cannot use entity as positions for relative positioning due to 'follow' attribute being present"); + throw new InternalExpressionException(p.id+" parameter cannot use entity as positions for relative positioning due to 'follow' attribute being present"); Entity e = ((EntityValue) value).getEntity(); return ListValue.of( new NumericValue(e.getX()), @@ -656,29 +755,81 @@ public class ShapeDispatcher ); } CarpetSettings.LOG.error("Value: "+value.getString()); - throw new InternalExpressionException("'"+id+"' requires a triple, block or entity to indicate position"); + throw new InternalExpressionException("'"+p.id+"' requires a triple, block or entity to indicate position"); } public Value decode(Tag tag) { - CompoundTag ctag = (CompoundTag)tag; + ListTag ctag = (ListTag)tag; return ListValue.of( - new NumericValue(ctag.getDouble("x")), - new NumericValue(ctag.getDouble("y")), - new NumericValue(ctag.getDouble("z")) + new NumericValue(ctag.getDouble(0)), + new NumericValue(ctag.getDouble(1)), + new NumericValue(ctag.getDouble(2)) ); } @Override public Tag toTag(Value value) { List lv = ((ListValue)value).getItems(); - CompoundTag tag = new CompoundTag(); - tag.putDouble("x", NumericValue.asNumber(lv.get(0)).getDouble()); - tag.putDouble("y", NumericValue.asNumber(lv.get(1)).getDouble()); - tag.putDouble("z", NumericValue.asNumber(lv.get(2)).getDouble()); + ListTag tag = new ListTag(); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(0)).getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(1)).getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(2)).getDouble())); return tag; } } + + public static class PointsParam extends Param + { + public PointsParam(String id) + { + super(id); + } + @Override + public Value validate(Map options, CarpetContext cc, Value value) + { + if (!(value instanceof ListValue)) + throw new InternalExpressionException(id+ " parameter should be a list"); + List points = new ArrayList<>(); + for (Value point: ((ListValue) value).getItems()) + points.add(Vec3Param.validate(this, options, cc, point, false)); + return ListValue.wrap(points); + } + + public Value decode(Tag tag) + { + ListTag ltag = (ListTag)tag; + List points = new ArrayList<>(); + for (int i=0, ll = ltag.size(); i lv = ((ListValue)pointsValue).getItems(); + ListTag ltag = new ListTag(); + for (Value value : lv) + { + List coords = ((ListValue)value).getItems(); + ListTag tag = new ListTag(); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(0)).getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(1)).getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(2)).getDouble())); + ltag.add(tag); + } + return ltag; + } + } + + public static class ColorParam extends NumericParam { protected ColorParam(String id) diff --git a/src/main/java/carpet/script/utils/ShapesRenderer.java b/src/main/java/carpet/script/utils/ShapesRenderer.java index e259a219..1819885b 100644 --- a/src/main/java/carpet/script/utils/ShapesRenderer.java +++ b/src/main/java/carpet/script/utils/ShapesRenderer.java @@ -2,15 +2,15 @@ package carpet.script.utils; import carpet.CarpetSettings; import com.mojang.blaze3d.systems.RenderSystem; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Camera; import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexFormats; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.Entity; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -25,7 +25,7 @@ import java.util.function.BiFunction; public class ShapesRenderer { - private final Map>> shapes; + private final Map>> shapes; private MinecraftClient client; private Map>> renderedShapes @@ -40,12 +40,12 @@ public class ShapesRenderer { this.client = minecraftClient; shapes = new HashMap<>(); - shapes.put(DimensionType.OVERWORLD, new Int2ObjectOpenHashMap<>()); - shapes.put(DimensionType.THE_NETHER, new Int2ObjectOpenHashMap<>()); - shapes.put(DimensionType.THE_END, new Int2ObjectOpenHashMap<>()); + shapes.put(DimensionType.OVERWORLD, new Long2ObjectOpenHashMap<>()); + shapes.put(DimensionType.THE_NETHER, new Long2ObjectOpenHashMap<>()); + shapes.put(DimensionType.THE_END, new Long2ObjectOpenHashMap<>()); } - public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, double cameraX, double cameraY, double cameraZ) + public void render(Camera camera, float partialTick) { IWorld iWorld = this.client.world; DimensionType dimensionType = iWorld.getDimension().getType(); @@ -68,20 +68,25 @@ public class ShapesRenderer //RenderSystem.polygonOffset(-3f, -3f); //RenderSystem.enablePolygonOffset(); //Entity entity = this.client.gameRenderer.getCamera().getFocusedEntity(); + Tessellator tessellator = Tessellator.getInstance(); BufferBuilder bufferBuilder = tessellator.getBuffer(); // render + double cameraX = camera.getPos().x; + double cameraY = camera.getPos().y; + double cameraZ = camera.getPos().z; + synchronized (shapes) { - shapes.get(dimensionType).int2ObjectEntrySet().removeIf( + shapes.get(dimensionType).long2ObjectEntrySet().removeIf( entry -> entry.getValue().isExpired(currentTime) ); shapes.get(dimensionType).values().forEach( s -> { if ( s.shouldRender(dimensionType)) - s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ); + s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); } ); //lines @@ -89,7 +94,7 @@ public class ShapesRenderer s -> { if ( s.shouldRender(dimensionType)) - s.renderLines(tessellator, bufferBuilder, cameraX, cameraY, cameraZ); + s.renderLines(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); } ); } @@ -102,6 +107,14 @@ public class ShapesRenderer RenderSystem.shadeModel(7424); } + public void addShapes(ListTag tag) + { + for (int i=0, count = tag.size(); i < count; i++) + { + addShape(tag.getCompound(i)); + } + } + public void addShape(CompoundTag tag) { ShapeDispatcher.ExpiringShape shape = ShapeDispatcher.fromTag(tag); @@ -116,7 +129,7 @@ public class ShapesRenderer { RenderedShape rshape = shapeFactory.apply(client, shape); DimensionType dim = Registry.DIMENSION_TYPE.get(new Identifier(tag.getString("dim"))); - int key = rshape.key(); + long key = rshape.key(); synchronized (shapes) { RenderedShape existing = shapes.get(dim).get(key); @@ -135,7 +148,7 @@ public class ShapesRenderer { synchronized (shapes) { - shapes.values().forEach(Int2ObjectOpenHashMap::clear); + shapes.values().forEach(Long2ObjectOpenHashMap::clear); } } @@ -146,13 +159,13 @@ public class ShapesRenderer protected MinecraftClient client; long expiryTick; double renderEpsilon = 0; - public abstract void renderLines(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz ); - public void renderFaces(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz ) {} + public abstract void renderLines(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz, float partialTick ); + public void renderFaces(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz, float partialTick ) {} protected RenderedShape(MinecraftClient client, T shape) { this.shape = shape; expiryTick = client.world.getTime()+shape.getExpiry(); - renderEpsilon = (2+((double)shape.key())/Integer.MAX_VALUE)/1000; + renderEpsilon = (3+((double)shape.key())/Long.MAX_VALUE)/1000; this.client = client; } @@ -160,7 +173,7 @@ public class ShapesRenderer { return expiryTick < currentTick; } - public int key() + public long key() { return shape.key(); }; @@ -172,11 +185,15 @@ public class ShapesRenderer if (client.world.getEntityById(shape.followEntity) == null) return false; return true; } - protected Vec3d relativiseRender(Vec3d vec) + protected Vec3d relativiseRender(Vec3d vec, float partialTick) { if (shape.followEntity <= 0) return vec; Entity e = client.world.getEntityById(shape.followEntity); - return shape.toAbsolute(e, vec); + return vec.add( + MathHelper.lerp(partialTick, e.prevX, e.getX()), + MathHelper.lerp(partialTick, e.prevY, e.getY()), + MathHelper.lerp(partialTick, e.prevZ, e.getZ()) + ); } } @@ -189,28 +206,30 @@ public class ShapesRenderer } @Override - public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz) + public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.a == 0.0) return; - Vec3d v1 = relativiseRender(shape.from); - Vec3d v2 = relativiseRender(shape.to); + Vec3d v1 = relativiseRender(shape.from, partialTick); + Vec3d v2 = relativiseRender(shape.to, partialTick); RenderSystem.lineWidth(shape.lineWidth); drawBoxWireGLLines(tessellator, bufferBuilder, (float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), (float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon), + v1.x!=v2.x, v1.y!=v2.y, v1.z!=v2.z, shape.r, shape.g, shape.b, shape.a, shape.r, shape.g, shape.b ); } @Override - public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz) + public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.fa == 0.0) return; - Vec3d v1 = relativiseRender(shape.from); - Vec3d v2 = relativiseRender(shape.to); + Vec3d v1 = relativiseRender(shape.from, partialTick); + Vec3d v2 = relativiseRender(shape.to, partialTick); RenderSystem.lineWidth(1.0F); drawBoxFaces(tessellator, bufferBuilder, (float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), (float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon), + v1.x!=v2.x, v1.y!=v2.y, v1.z!=v2.z, shape.fr, shape.fg, shape.fb, shape.fa ); } @@ -224,10 +243,10 @@ public class ShapesRenderer super(client, (ShapeDispatcher.Line)shape); } @Override - public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz) + public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { - Vec3d v1 = relativiseRender(shape.from); - Vec3d v2 = relativiseRender(shape.to); + Vec3d v1 = relativiseRender(shape.from, partialTick); + Vec3d v2 = relativiseRender(shape.to, partialTick); RenderSystem.lineWidth(shape.lineWidth); drawLine(tessellator, bufferBuilder, (float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), @@ -244,10 +263,10 @@ public class ShapesRenderer super(client, (ShapeDispatcher.Sphere)shape); } @Override - public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz) + public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.a == 0.0) return; - Vec3d vc = relativiseRender(shape.center); + Vec3d vc = relativiseRender(shape.center, partialTick); RenderSystem.lineWidth(shape.lineWidth); drawSphereWireframe(tessellator, bufferBuilder, (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), @@ -255,10 +274,10 @@ public class ShapesRenderer shape.r, shape.g, shape.b, shape.a); } @Override - public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz) + public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.fa == 0.0) return; - Vec3d vc = relativiseRender(shape.center); + Vec3d vc = relativiseRender(shape.center, partialTick); RenderSystem.lineWidth(1.0f); drawSphereFaces(tessellator, bufferBuilder, (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), @@ -276,68 +295,117 @@ public class ShapesRenderer tessellator.draw(); } - public static void drawBoxWireGLLines(Tessellator tessellator, BufferBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2, float red1, float grn1, float blu1, float alpha, float red2, float grn2, float blu2) + public static void drawBoxWireGLLines( + Tessellator tessellator, BufferBuilder builder, + float x1, float y1, float z1, + float x2, float y2, float z2, + boolean xthick, boolean ythick, boolean zthick, + float red1, float grn1, float blu1, float alpha, float red2, float grn2, float blu2) { builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3 - builder.vertex(x1, y1, z1).color(red1, grn2, blu2, alpha).next(); - builder.vertex(x2, y1, z1).color(red1, grn2, blu2, alpha).next(); - builder.vertex(x1, y1, z1).color(red2, grn1, blu2, alpha).next(); - builder.vertex(x1, y2, z1).color(red2, grn1, blu2, alpha).next(); - builder.vertex(x1, y1, z1).color(red2, grn2, blu1, alpha).next(); - builder.vertex(x1, y1, z2).color(red2, grn2, blu1, alpha).next(); - builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); + if (xthick) + { + builder.vertex(x1, y1, z1).color(red1, grn2, blu2, alpha).next(); + builder.vertex(x2, y1, z1).color(red1, grn2, blu2, alpha).next(); + + builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); + + builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); + + builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); + } + if (ythick) + { + builder.vertex(x1, y1, z1).color(red2, grn1, blu2, alpha).next(); + builder.vertex(x1, y2, z1).color(red2, grn1, blu2, alpha).next(); + + builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); + + builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); + + builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); + } + if (zthick) + { + builder.vertex(x1, y1, z1).color(red2, grn2, blu1, alpha).next(); + builder.vertex(x1, y1, z2).color(red2, grn2, blu1, alpha).next(); + + builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); + + builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); + + builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); + } tessellator.draw(); } - public static void drawBoxFaces(Tessellator tessellator, BufferBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2, float red1, float grn1, float blu1, float alpha) + public static void drawBoxFaces( + Tessellator tessellator, BufferBuilder builder, + float x1, float y1, float z1, + float x2, float y2, float z2, + boolean xthick, boolean ythick, boolean zthick, + float red1, float grn1, float blu1, float alpha) { builder.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR); - builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); + if (xthick && ythick) + { + builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); + if (zthick) + { + builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); + } + } - builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); + if (zthick && ythick) + { + builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); + if (xthick) + { + builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); + } + } - builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); - builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); + // now at least drawing one + if (zthick && xthick) + { + builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next(); + + + if (ythick) + { + builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); + builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); + } + } tessellator.draw(); } diff --git a/src/main/resources/assets/carpet/scripts/chunk_display.sc b/src/main/resources/assets/carpet/scripts/chunk_display.sc new file mode 100644 index 00000000..1a9edf21 --- /dev/null +++ b/src/main/resources/assets/carpet/scripts/chunk_display.sc @@ -0,0 +1,181 @@ +global_chunk_colors = m( + // loaded ticking + l(3, l(0xBBFF0000, 0x88bb0000)), // lime, green + l(2, l(0xFF000000, 0x88000000)), // red + l(1, l(0xFFFF0000, 0xbbbb0000)), // yellow + // 'unloaded' (in memory, so rather 'inaccessible') + l(0, l(0xcccccc00, 0x11111100)), // gray / hi constast + // stable full generation (for '0's, all 1,2,3 are always 'full') + l('full', l(0xbbbbbb00, 0x88888800)), // gray + // unstable final bits + l('heightmaps', l(0xFFFFFF00, 0x00000000)), // check + l('spawn', l(0xFFFFFF00, 0x00000000)), // check + l('light_gen', l(0xFFFFFF00, 0x00000000)), // check + // stable features + l('features', l(0x88bb2200, 0x66882200)), // green muddled + //stable terrain + l('liquid_carvers', l(0x65432100, 0x54321000)), // browns + //unstable terrain - not generated yet + l('carvers', l(0xFFFFFF00, 0x00000000)), // check + l('surface', l(0xFFFFFF00, 0x00000000)), // check + l('noise', l(0xFFFFFF00, 0x00000000)), // check + l('biomes', l(0xFFFFFF00, 0x00000000)), // check + l('structure_references', l(0xFFFFFF00, 0x00000000)), // check + // stable + l('structure_starts', l(0x66666600, 0x22222200)), // darkgrey + // not stated yet + l('empty', l(0xFF88888800, 0xDD444400)), // pink + // proper not in memory + l(null , l(0xFFFFFF00, 0xFFFFFF00)), // hwite + + // ticket types + l('player', l(0x0000DD00, 0x00008800)), // blue + l('portal', l(0xAA00AA00, 0xAA00AA00)), // purple + l('dragon', l(0xAA008800, 0xAA008800)), // purple + l('forced', l(0xAABBFF00, 0x8899AA00)), // blue + l('light', l(0xFFFF0000, 0xBBBB0000)), // yellow + l('post_teleport', l(0xAA00AA00, 0xAA00AA00)), // purple + l('start', l(0xDDFF0000, 0xDDFF0000)), // lime, green + // recent chunk requests + l('unknown', l(0xFFFFFF00, 0x00000000)) // check +); + +// map representing current displayed chunkloading setup +global_current_setup = null; + +// which blocks in the inventory represent which dimension +global_block_markers = m( + l('netherrack', 'the_nether'), + l('grass_block', 'overworld'), + l('end_stone', 'the_end') +); + +__on_player_uses_item(player, item_tuple, hand) -> +( + if (hand != 'mainhand', return()); + if (!has(global_block_markers, item_tuple:0), return()); + if (item_tuple:1%16 != 0, return()); + new_setup = true; + if (global_current_setup, + new_setup = __remove_previous_setup(player) + ); + if (!new_setup, return()); + tag = nbt('{}'); + put(tag:'Enchantments','[]'); + put(tag:'Enchantments', '{lvl:1s,id:"minecraft:protection"}', 0); + inventory_set(player, player~'selected_slot', item_tuple:1, item_tuple:0, tag); + __setup_tracker(player, item_tuple); +); + +//__on_tick() -> null; + +__remove_previous_setup(new_player) -> +( + if (!global_current_setup, return()); + global_running = false; + p = player(); + if (p && + global_block_markers:((item = inventory_get(p, global_current_setup:'inventory_slot')):0)==global_current_setup:'source_dimension' && + item:1 == global_current_setup:'radius' && + item:2, + inventory_set(p, global_current_setup:'inventory_slot', item:1, item:0, null); // disenchant + if( new_player == p && new_player~'selected_slot' == global_current_setup:'inventory_slot', + disabled = true + ); + ); + global_current_setup = null; + !disabled +); + +__setup_tracker(player, item) -> +( + setup = m(); + setup:'player_name' = str(player); + setup:'inventory_slot' = player ~ 'selected_slot'; + setup:'plot_dimension' = player ~ 'dimension'; + setup:'plot_center' = map(pos(player), floor(_))-l(0,1,0); + setup:'radius' = item:1; + setup:'source_dimension' = global_block_markers:(item:0); + multiplier = 1; + if (setup:'source_dimension' != setup:'plot_dimension', + multiplier = if ( + setup:'source_dimension' == 'the_nether', 1/8, + setup:'source_dimension' == 'the_end', 0, + 8); + ); + setup:'source_center' = setup:'plot_center' * multiplier; + print(setup:'source_dimension'+' around '+setup:'source_center'+' with '+setup:'radius'+' radius ('+ (2*setup:'radius'+1)^2 +' chunks)'); + + l(sx, sy, sz) = setup:'source_center'; + global_status_cache = m(); + loop( 2*setup:'radius'+1, dx = _ - setup:'radius'; + loop( 2*setup:'radius'+1, dz = _ - setup:'radius'; + source_pos = l(sx+16*dx,sy,sz+16*dz); + global_status_cache:source_pos = null + ) + ); + global_current_setup = setup; + global_running = true; + schedule(0, '__chunk_visualizer_tick', player); +); + +global_status_cache = m(); + +__chunk_visualizer_tick(p) -> +( + setup = global_current_setup; + if(!setup, return()); + if (!global_running, return()); + show_activity = (p~'holds':0 == 'redstone_torch'); + in_dimension( setup:'plot_dimension', + l(sx, sy, sz) = setup:'source_center'; + l(px, py, pz) = setup:'plot_center'; + source_center = setup:'source_center'; + radius = setup:'radius'; + source_dimension = setup:'source_dimension'; + + shapes = l(); + + loop( 2*radius+1, dx = _ - radius; + loop( 2*radius+1, dz = _ - radius; + changed = false; + source_pos = l(sx+16*dx,sy,sz+16*dz); + status = in_dimension( source_dimension, + loaded_status = loaded_status(source_pos); + if (loaded_status > 0, loaded_status, generation_status(source_pos)) + ); + // will mix with light ticket + if (status=='light', status = 'light_gen'); + + if ( status != global_status_cache:source_pos, + global_status_cache:source_pos = status; + changed = true; + ); + if (loaded_status > 0, + tickets = in_dimension( source_dimension, chunk_tickets(source_pos)); + for(tickets, + l(type, level) = _; + if (show_activity || type != 'unknown', + global_status_cache:source_pos = type; + changed = true; + ); + ); + ); + //if (changed, + //to = global_status_cache:source_pos; + //if(!has(global_chunk_colors:to), print('problem with '+to)); + //set(px+dx,py,pz+dz, global_chunk_colors:to:((dx+dz)%2)); + //set(px+dx,py,pz+dz, global_chunk_colors:(global_status_cache:source_pos):((dx+dz)%2)); + //bpos = l(px+dx/2, py, pz+dz/2); + bpos = l(dx/2, -10, dz/2); + bcol = global_chunk_colors:(global_status_cache:source_pos):((dx+dz)%2); + shapes += l('box', 10, 'from', bpos, 'to', bpos + l(0.5,0,0.5), 'color', bcol, 'fill', bcol+128, + 'follow', p) + ; + //); + ); + ); + draw_shape(shapes); + ); + schedule(5, '__chunk_visualizer_tick', p) +) \ No newline at end of file diff --git a/src/main/resources/assets/carpet/scripts/overlay.sc b/src/main/resources/assets/carpet/scripts/overlay.sc index 81b42749..8038b9d3 100644 --- a/src/main/resources/assets/carpet/scripts/overlay.sc +++ b/src/main/resources/assets/carpet/scripts/overlay.sc @@ -94,7 +94,7 @@ __structure_renderer(player_name) -> l(from, to) = structure_data; total_size = _euclidean(from, to); density = max(10, total_size/10); - draw_shape('box', 15, 'from', from, 'to', to+1, 'color', 0x00FFFFFF, 'width', 3, 'fill', 0x00FFFF22); + draw_shape('box', 15, 'from', from, 'to', to+1, 'color', 0x00FFFFFF, 'line', 3, 'fill', 0x00FFFF22); structure_pieces = slice(sort_key(structure_pieces, _euclidean_sq((_:2+_:3)/2,ppos)), 0, config:'max_pieces'); for (structure_pieces, l(piece, direction, from, to) = _; r = 255 - floor(128 * _i/config:'max_pieces'); @@ -133,12 +133,14 @@ __chunk_renderer(player_name) -> b = 30; a = max(0, 255-player_distance); color = a+256*(b+256*(g+256*r)); - draw_shape('line', 15, 'from', top_00, 'to', top_10, 'color', color, 'width', 3); - draw_shape('line', 15, 'from', top_10, 'to', top_11, 'color', color, 'width', 3); - draw_shape('line', 15, 'from', top_11, 'to', top_01, 'color', color, 'width', 3); - draw_shape('line', 15, 'from', top_01, 'to', top_00, 'color', color, 'width', 3); - draw_shape('line', 15, 'from', top_00, 'to', top_11, 'color', color, 'width', 3); - draw_shape('line', 15, 'from', top_01, 'to', top_10, 'color', color, 'width', 3); + draw_shape(l( + l('line', 15, 'from', top_00, 'to', top_10, 'color', color, 'line', 3), + l('line', 15, 'from', top_10, 'to', top_11, 'color', color, 'line', 3), + l('line', 15, 'from', top_11, 'to', top_01, 'color', color, 'line', 3), + l('line', 15, 'from', top_01, 'to', top_00, 'color', color, 'line', 3), + l('line', 15, 'from', top_00, 'to', top_11, 'color', color, 'line', 3), + l('line', 15, 'from', top_01, 'to', top_10, 'color', color, 'line', 3) + )); ) ) ) diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 65ec4c40..db91b5fe 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -182,7 +182,8 @@ "WorldRenderer_fogOffMixin", "WorldRenderer_creativeNoClipMixin", "ClientPlayNetworkHandlerMixin", - "DebugRenderer_scarpetRenderMixin" + "DebugRenderer_scarpetRenderMixin", + "WorldRenderer_scarpetRenderMixin" ], "server": [ "MinecraftDedicatedServerMixin" From d7e4b49d56f0e04e9b9ae21c8389882f9891c2be Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 9 Jun 2020 02:58:44 -0300 Subject: [PATCH 06/10] chunk display fixes --- .../assets/carpet/scripts/chunk_display.sc | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/main/resources/assets/carpet/scripts/chunk_display.sc b/src/main/resources/assets/carpet/scripts/chunk_display.sc index 1a9edf21..7f770b0a 100644 --- a/src/main/resources/assets/carpet/scripts/chunk_display.sc +++ b/src/main/resources/assets/carpet/scripts/chunk_display.sc @@ -1,30 +1,30 @@ global_chunk_colors = m( // loaded ticking - l(3, l(0xBBFF0000, 0x88bb0000)), // lime, green + l(3, l(0xAAdd0000, 0x66990000)), // lime, green l(2, l(0xFF000000, 0x88000000)), // red - l(1, l(0xFFFF0000, 0xbbbb0000)), // yellow + l(1, l(0xFFcc0000, 0xbbaa0000)), // yellowish // 'unloaded' (in memory, so rather 'inaccessible') l(0, l(0xcccccc00, 0x11111100)), // gray / hi constast // stable full generation (for '0's, all 1,2,3 are always 'full') l('full', l(0xbbbbbb00, 0x88888800)), // gray // unstable final bits - l('heightmaps', l(0xFFFFFF00, 0x00000000)), // check - l('spawn', l(0xFFFFFF00, 0x00000000)), // check - l('light_gen', l(0xFFFFFF00, 0x00000000)), // check + l('heightmaps', l(0xFFFFFF00, 0x00000000)), // checker + l('spawn', l(0xFFFFFF00, 0x00000000)), // checker + l('light_gen', l(0xFFFFFF00, 0x00000000)), // checker // stable features l('features', l(0x88bb2200, 0x66882200)), // green muddled //stable terrain l('liquid_carvers', l(0x65432100, 0x54321000)), // browns //unstable terrain - not generated yet - l('carvers', l(0xFFFFFF00, 0x00000000)), // check - l('surface', l(0xFFFFFF00, 0x00000000)), // check - l('noise', l(0xFFFFFF00, 0x00000000)), // check - l('biomes', l(0xFFFFFF00, 0x00000000)), // check - l('structure_references', l(0xFFFFFF00, 0x00000000)), // check + l('carvers', l(0xFFFFFF00, 0x00000000)), // checker + l('surface', l(0xFFFFFF00, 0x00000000)), // checker + l('noise', l(0xFFFFFF00, 0x00000000)), // checker + l('biomes', l(0xFFFFFF00, 0x00000000)), // checker + l('structure_references', l(0xFFFFFF00, 0x00000000)), // checker // stable l('structure_starts', l(0x66666600, 0x22222200)), // darkgrey // not stated yet - l('empty', l(0xFF88888800, 0xDD444400)), // pink + l('empty', l(0xFF888800, 0xDD444400)), // pink // proper not in memory l(null , l(0xFFFFFF00, 0xFFFFFF00)), // hwite @@ -111,7 +111,7 @@ __setup_tracker(player, item) -> loop( 2*setup:'radius'+1, dx = _ - setup:'radius'; loop( 2*setup:'radius'+1, dz = _ - setup:'radius'; source_pos = l(sx+16*dx,sy,sz+16*dz); - global_status_cache:source_pos = null + global_status_cache:source_pos = l(null, 0); ) ); global_current_setup = setup; @@ -135,6 +135,7 @@ __chunk_visualizer_tick(p) -> source_dimension = setup:'source_dimension'; shapes = l(); + now = tick_time(); loop( 2*radius+1, dx = _ - radius; loop( 2*radius+1, dz = _ - radius; @@ -147,35 +148,35 @@ __chunk_visualizer_tick(p) -> // will mix with light ticket if (status=='light', status = 'light_gen'); - if ( status != global_status_cache:source_pos, - global_status_cache:source_pos = status; - changed = true; - ); + if (loaded_status > 0, tickets = in_dimension( source_dimension, chunk_tickets(source_pos)); for(tickets, l(type, level) = _; if (show_activity || type != 'unknown', - global_status_cache:source_pos = type; - changed = true; + //global_status_cache:source_pos = type; + //changed = true; + status = type; ); ); ); - //if (changed, - //to = global_status_cache:source_pos; - //if(!has(global_chunk_colors:to), print('problem with '+to)); - //set(px+dx,py,pz+dz, global_chunk_colors:to:((dx+dz)%2)); - //set(px+dx,py,pz+dz, global_chunk_colors:(global_status_cache:source_pos):((dx+dz)%2)); - //bpos = l(px+dx/2, py, pz+dz/2); - bpos = l(dx/2, -10, dz/2); - bcol = global_chunk_colors:(global_status_cache:source_pos):((dx+dz)%2); - shapes += l('box', 10, 'from', bpos, 'to', bpos + l(0.5,0,0.5), 'color', bcol, 'fill', bcol+128, - 'follow', p) - ; - //); + l(cached_status, expiry) = global_status_cache:source_pos; + changed = (status != cached_status); + if ( changed || (expiry < now), + global_status_cache:source_pos = l(status, now+100+floor(rand(20))); + bpos = l(dx/2, 10, dz/2); + bcol = global_chunk_colors:status:((dx+dz)%2); + shapes += l('box', 150, 'from', bpos, 'to', bpos + l(0.5,0,0.5), + 'color', 0xffffff00, 'fill', bcol+128, 'follow', p); + if (changed, + pbcol = global_chunk_colors:cached_status:((dx+dz)%2); + shapes += l('box', 0, 'from', bpos, 'to', bpos + l(0.5,0,0.5), + 'color', 0xffffff00, 'fill', pbcol+128, 'follow', p); + ); + ); ); ); draw_shape(shapes); ); - schedule(5, '__chunk_visualizer_tick', p) + schedule(1, '__chunk_visualizer_tick', p) ) \ No newline at end of file From f5868b24e29bc773e1f8b2f7e564505bddc7d349 Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 9 Jun 2020 02:59:53 -0300 Subject: [PATCH 07/10] Duration can be sent as 0 --- .../carpet/script/utils/ShapeDispatcher.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/carpet/script/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java index 770d7639..6215fe99 100644 --- a/src/main/java/carpet/script/utils/ShapeDispatcher.java +++ b/src/main/java/carpet/script/utils/ShapeDispatcher.java @@ -585,7 +585,7 @@ public class ShapeDispatcher public static Map of = new HashMap(){{ put("shape", new ShapeParam()); put("dim", new DimensionParam()); - put("duration", new PositiveIntParam("duration")); + put("duration", new NonNegativeIntParam("duration")); put("color", new ColorParam("color")); put("follow", new EntityParam("follow")); put("line", new PositiveFloatParam("line")); @@ -704,6 +704,20 @@ public class ShapeDispatcher public Tag toTag(Value value) { return IntTag.of(NumericValue.asNumber(value).getInt()); } } + public static class NonNegativeIntParam extends NumericParam + { + protected NonNegativeIntParam(String id) { super(id); } + @Override + public Value decode(Tag tag) { return new NumericValue(((IntTag)tag).getInt()); } + @Override + public Tag toTag(Value value) { return IntTag.of(NumericValue.asNumber(value).getInt()); } + @Override public Value validate(Map options, CarpetContext cc, Value value) + { + Value ret = super.validate(options, cc, value); + if (((NumericValue)ret).getDouble()<0) throw new InternalExpressionException("'"+id+"' should be non-negative"); + return ret; + } + } public static class Vec3Param extends Param { From eb2e6921c13d9f95e36f031d2035fff8bc4e1b33 Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 9 Jun 2020 12:27:41 -0300 Subject: [PATCH 08/10] docs --- docs/scarpet/api/Entities.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 36365a78..7d8f831f 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -473,7 +473,8 @@ players, since they are controlled client side. ### `modify(e, 'effect', name, duration?, amplifier?, show_particles?, show_icon?)` Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true` -and `true`. If no duration is specified, or if it's null or 0, the effect is removed. +and `true`. If no duration is specified, or if it's null or 0, the effect is removed. If the effect name is specified +as `null` that clears all effects. ### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)` From 0ea463bbedf323eab87dd5e192c5973a056f5962 Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 9 Jun 2020 12:34:03 -0300 Subject: [PATCH 09/10] docs --- docs/scarpet/api/Entities.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index fc80dcb6..a0524460 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -477,11 +477,11 @@ If called with `false` value, it will disable AI in the mob. `true` will enable Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting players, since they are controlled client side. -### `modify(e, 'effect', name, duration?, amplifier?, show_particles?, show_icon?)` +### `modify(e, 'effect', name?, duration?, amplifier?, show_particles?, show_icon?)` Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true` -and `true`. If no duration is specified, or if it's null or 0, the effect is removed. If the effect name is specified -as `null` that clears all effects. +and `true`. If no duration is specified, or if it's null or 0, the effect is removed. If name is not specified, +it clears all effects. ### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)` From 58862fca11a8bb415b6166bdfb94faf0befca9ae Mon Sep 17 00:00:00 2001 From: gnembon Date: Wed, 10 Jun 2020 01:25:51 -0300 Subject: [PATCH 10/10] added snap to shape added bulk shape rendering --- docs/scarpet/api/Auxiliary.md | 13 +- .../carpet/script/CarpetScriptServer.java | 1 + .../carpet/script/utils/ShapeDispatcher.java | 232 ++++++++++++++---- .../carpet/script/utils/ShapesRenderer.java | 148 +++++++++-- .../carpet/script/value/NumericValue.java | 7 + .../assets/carpet/scripts/chunk_display.sc | 31 ++- 6 files changed, 341 insertions(+), 91 deletions(-) diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index d4de9940..8cb0c117 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -36,7 +36,10 @@ 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)` +### `draw_shape(shape, duration, key?, value?, ... )`, +### `draw_shape(shape, duration, l(key?, value?, ... ))`, +### `draw_shape(shape, duration, attribute_map)` +### `draw_shape(shape_list)` 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 @@ -47,6 +50,10 @@ are not required to follow all attributes precisely, but will allow vanilla clie 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 can be send one by one, using either of the first three invocations, or batched as a list of shape descriptors. +Batching has this benefit that they will be send possibly as one packet, limiting network overhead of +sending many small packets to draw several shapes at once. + 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: @@ -65,6 +72,10 @@ Optional shared shape attributes: * `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. + * `snap` - if `follow` is present, indicated on which axis the snapping to entity coordinates occurs, and which axis + will be treated statically, i.e. the coordinate passed in a coord triple is the actual value in the world. Default + value is `'xyz'`, meaning the shape will be drawn relatively to the entity in all three directions. Using `xz` for + instance makes so that the shape follows the entity, but stays at the same, absolute Y coordinate. Available shapes: * `'line'` - draws a straight line between two points diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index a65a7655..2f0e627d 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -49,6 +49,7 @@ public class CarpetScriptServer add(new BundledModule("event_test", false)); add(new BundledModule("stats_test", false)); add(new BundledModule("math", true)); + add(new BundledModule("chunk_display", false)); }}; public CarpetScriptServer(MinecraftServer server) diff --git a/src/main/java/carpet/script/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java index 6215fe99..9007d675 100644 --- a/src/main/java/carpet/script/utils/ShapeDispatcher.java +++ b/src/main/java/carpet/script/utils/ShapeDispatcher.java @@ -31,9 +31,11 @@ import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; import net.minecraft.world.dimension.DimensionType; import org.apache.commons.lang3.tuple.Pair; @@ -62,7 +64,7 @@ public class ShapeDispatcher { if (lv.size() < 3) throw new InternalExpressionException("'draw_shape' takes at least three parameters, shape name, duration, and its params"); String shapeType = lv.get(0).getString(); - Value duration = NumericValue.asNumber(lv.get(1)); + Value duration = NumericValue.asNumber(lv.get(1), "duration"); Map params; if (lv.size() == 3) { @@ -230,6 +232,7 @@ public class ShapeDispatcher put("line", creator(Line::new)); put("box", creator(Box::new)); put("sphere", creator(Sphere::new)); + put("cylinder", creator(Cylinder::new)); }}; private static Function,ExpiringShape> creator(Supplier shapeFactory) { @@ -248,7 +251,9 @@ public class ShapeDispatcher protected int duration = 0; private long key; protected int followEntity; - protected DimensionType entityDimension; + protected String snapTo; + protected boolean snapX, snapY, snapZ; + public DimensionType shapeDimension; protected ExpiringShape() { } @@ -302,18 +307,33 @@ public class ShapeDispatcher key = 0; followEntity = -1; - entityDimension = null; + shapeDimension = Registry.DIMENSION_TYPE.get(new Identifier(options.get("dim").getString())); if (options.containsKey("follow")) { followEntity = NumericValue.asNumber(options.getOrDefault("follow", optional.get("follow"))).getInt(); - entityDimension = Registry.DIMENSION_TYPE.get(new Identifier(options.get("dim").getString())); + snapTo = options.getOrDefault("snap", optional.get("snap")).getString().toLowerCase(Locale.ROOT); + snapX = snapTo.contains("x"); + snapY = snapTo.contains("y"); + snapZ = snapTo.contains("z"); } } public int getExpiry() { return duration; } - public Vec3d toAbsolute(Entity e, Vec3d vec) + public Vec3d toAbsolute(Entity e, Vec3d vec, float partialTick) { - return e.getPos().add(vec); + return vec.add( + snapX?MathHelper.lerp(partialTick, e.prevX, e.getX()):0.0, + snapY?MathHelper.lerp(partialTick, e.prevY, e.getY()):0.0, + snapZ?MathHelper.lerp(partialTick, e.prevZ, e.getZ()):0.0 + ); } + public Vec3d relativiseRender(World world, Vec3d vec, float partialTick) + { + if (followEntity <= 0) return vec; + Entity e = world.getEntityById(followEntity); + if (e == null) return vec; + return toAbsolute(e, vec, partialTick); + } + public Vec3d vecFromValue(Value value) { if (!(value instanceof ListValue)) throw new InternalExpressionException("decoded value of "+value.getPrettyString()+" is not a triple"); @@ -335,6 +355,7 @@ public class ShapeDispatcher protected long calcKey() { // using FNV-1a algorithm long hash = -3750763034362895579L; + hash ^= shapeDimension.hashCode(); hash *= 1099511628211L; hash ^= color; hash *= 1099511628211L; hash ^= followEntity; hash *= 1099511628211L; hash ^= Float.hashCode(lineWidth); hash *= 1099511628211L; @@ -347,7 +368,8 @@ public class ShapeDispatcher "color", new NumericValue(-1), "follow", new NumericValue(-1), "line", new NumericValue(2.0), - "fill", new NumericValue(0xffffff00) + "fill", new NumericValue(0xffffff00), + "snap", new StringValue("xyz") ); protected Set requiredParams() {return required;} // list of params that can be there, with defaults @@ -389,16 +411,16 @@ public class ShapeDispatcher double density = Math.max(2.0, from.distanceTo(to) /50) / (a+0.1); return p -> { - if (followEntity == -1) + if (p.dimension == shapeDimension) { - particleMesh(Collections.singletonList(p), particle, density, from, to); - return; + particleMesh( + Collections.singletonList(p), + particle, + density, + relativiseRender(p.getServerWorld(), from, 0), + relativiseRender(p.getServerWorld(), to, 0) + ); } - Entity e = p.getServer().getWorld(entityDimension).getEntityById(followEntity); - if (e == null) return; - Vec3d rel1 = toAbsolute(e, from); - Vec3d rel2 = toAbsolute(e, to); - particleMesh(Collections.singletonList(p), particle, density, rel1, rel2); }; } @@ -471,19 +493,16 @@ public class ShapeDispatcher String particleName = String.format(Locale.ROOT , "dust %.1f %.1f %.1f 1.0", r, g, b); ParticleEffect particle = getParticleData(particleName); double density = Math.max(2.0, from.distanceTo(to) /50) / (a+0.1); + return p -> { - if (followEntity == -1) - { - drawParticleLine(Collections.singletonList(p), particle, from, to, density); - return; - } - - Entity e = p.getServer().getWorld(entityDimension).getEntityById(followEntity); - if (e == null) return; - Vec3d rel1 = toAbsolute(e, from); - Vec3d rel2 = toAbsolute(e, to); - drawParticleLine(Collections.singletonList(p), particle, rel1, rel2, density); + if (p.dimension == shapeDimension) drawParticleLine( + Collections.singletonList(p), + particle, + relativiseRender(p.getServerWorld(), from, 0), + relativiseRender(p.getServerWorld(), to, 0), + density + ); }; } @@ -534,24 +553,18 @@ public class ShapeDispatcher @Override public Consumer alternative() { return p -> { + if (p.dimension != shapeDimension) return; String particleName = String.format(Locale.ROOT , "dust %.1f %.1f %.1f 1.0", r, g, b); ParticleEffect particle = getParticleData(particleName); int partno = Math.min(1000,20*subdivisions); Random rand = p.world.getRandom(); ServerWorld world = p.getServerWorld(); - double ccx, ccy, ccz; - if (followEntity == -1) - { - ccx = center.x; ccy = center.y; ccz = center.z; - } - else - { - Entity e = p.getServer().getWorld(entityDimension).getEntityById(followEntity); - if (e == null) return; - Vec3d rel1 = toAbsolute(e, center); - ccx = (float)rel1.x; ccy = (float)rel1.y; ccz = (float)rel1.z; - } + Vec3d ccenter = relativiseRender(world, center, 0 ); + + double ccx = ccenter.x; + double ccy = ccenter.y; + double ccz = ccenter.z; for (int i=0; i required = ImmutableSet.of("center", "radius"); + private final Map optional = ImmutableMap.of( + "level", Value.ZERO, + "height", Value.ZERO, + "axis", new StringValue("y") + ); + @Override + protected Set requiredParams() { return Sets.union(super.requiredParams(), required); } + @Override + protected Set optionalParams() { return Sets.union(super.optionalParams(), optional.keySet()); } + + Vec3d center; + float height; + float radius; + int level; + int subdivisions; + Direction.Axis axis; + + private Cylinder() { super(); } + + @Override + protected void init(Map options) + { + super.init(options); + center = vecFromValue(options.get("center")); + radius = NumericValue.asNumber(options.get("radius")).getFloat(); + level = NumericValue.asNumber(options.getOrDefault("level", optional.get("level"))).getInt(); + subdivisions = level; + if (subdivisions <= 0) + { + subdivisions = Math.max(10, (int)(10*Math.sqrt(radius))); + } + height = NumericValue.asNumber(options.getOrDefault("height", optional.get("height"))).getFloat(); + axis = Direction.Axis.fromName(options.getOrDefault("axis", optional.get("axis")).getString()); + } + + + @Override + public Consumer alternative() { return p -> + { + String particleName = String.format(Locale.ROOT , "dust %.1f %.1f %.1f 1.0", r, g, b); + ParticleEffect particle = getParticleData(particleName); + int partno = (int)Math.min(1000,Math.sqrt(20*subdivisions*(1+height))); + Random rand = p.world.getRandom(); + ServerWorld world = p.getServerWorld(); + + Vec3d ccenter = relativiseRender(world, center, 0 ); + + double ccx = ccenter.x; + double ccy = ccenter.y; + double ccz = ccenter.z; + + if (axis == Direction.Axis.Y) + { + for (int i=0; i options, CarpetContext cc, Value value) + { + Value ret = super.validate(options, cc, value); + if (((NumericValue)ret).getDouble()<0) throw new InternalExpressionException("'"+id+"' should be non-negative"); + return ret; + } + } + public static class NonNegativeFloatParam extends NumericParam + { + protected NonNegativeFloatParam(String id) { super(id); } + @Override + public Value decode(Tag tag) { return new NumericValue(((FloatTag)tag).getFloat()); } + @Override + public Tag toTag(Value value) { return FloatTag.of(NumericValue.asNumber(value, id).getFloat()); } @Override public Value validate(Map options, CarpetContext cc, Value value) { Value ret = super.validate(options, cc, value); @@ -786,9 +918,9 @@ public class ShapeDispatcher { List lv = ((ListValue)value).getItems(); ListTag tag = new ListTag(); - tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(0)).getDouble())); - tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(1)).getDouble())); - tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(2)).getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(0), "x").getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(1), "y").getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(2), "z").getDouble())); return tag; } } @@ -834,9 +966,9 @@ public class ShapeDispatcher { List coords = ((ListValue)value).getItems(); ListTag tag = new ListTag(); - tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(0)).getDouble())); - tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(1)).getDouble())); - tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(2)).getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(0), "x").getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(1), "y").getDouble())); + tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(2), "z").getDouble())); ltag.add(tag); } return ltag; @@ -853,7 +985,7 @@ public class ShapeDispatcher public Value decode(Tag tag) { return new NumericValue(((IntTag)tag).getInt()); } @Override - public Tag toTag(Value value) { return IntTag.of(NumericValue.asNumber(value).getInt()); } + public Tag toTag(Value value) { return IntTag.of(NumericValue.asNumber(value, id).getInt()); } } public static class EntityParam extends Param @@ -864,7 +996,7 @@ public class ShapeDispatcher @Override public Tag toTag(Value value) { - return IntTag.of(NumericValue.asNumber(value).getInt()); + return IntTag.of(NumericValue.asNumber(value, id).getInt()); } @Override diff --git a/src/main/java/carpet/script/utils/ShapesRenderer.java b/src/main/java/carpet/script/utils/ShapesRenderer.java index 1819885b..d4bb1e9f 100644 --- a/src/main/java/carpet/script/utils/ShapesRenderer.java +++ b/src/main/java/carpet/script/utils/ShapesRenderer.java @@ -8,13 +8,11 @@ import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexFormats; -import net.minecraft.entity.Entity; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.util.Identifier; +import net.minecraft.util.math.Direction; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.registry.Registry; import net.minecraft.world.IWorld; import net.minecraft.world.dimension.DimensionType; import org.lwjgl.opengl.GL11; @@ -34,6 +32,7 @@ public class ShapesRenderer put("line", RenderedLine::new); put("box", RenderedBox::new); put("sphere", RenderedSphere::new); + put("cylinder", RenderedCylinder::new); }}; public ShapesRenderer(MinecraftClient minecraftClient) @@ -128,7 +127,7 @@ public class ShapesRenderer else { RenderedShape rshape = shapeFactory.apply(client, shape); - DimensionType dim = Registry.DIMENSION_TYPE.get(new Identifier(tag.getString("dim"))); + DimensionType dim = shape.shapeDimension; long key = rshape.key(); synchronized (shapes) { @@ -181,20 +180,9 @@ public class ShapesRenderer { if (shape.followEntity <=0 ) return true; if (client.world == null) return false; - if (client.world.dimension.getType() != dim) return false; if (client.world.getEntityById(shape.followEntity) == null) return false; return true; } - protected Vec3d relativiseRender(Vec3d vec, float partialTick) - { - if (shape.followEntity <= 0) return vec; - Entity e = client.world.getEntityById(shape.followEntity); - return vec.add( - MathHelper.lerp(partialTick, e.prevX, e.getX()), - MathHelper.lerp(partialTick, e.prevY, e.getY()), - MathHelper.lerp(partialTick, e.prevZ, e.getZ()) - ); - } } public static class RenderedBox extends RenderedShape @@ -209,8 +197,8 @@ public class ShapesRenderer public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.a == 0.0) return; - Vec3d v1 = relativiseRender(shape.from, partialTick); - Vec3d v2 = relativiseRender(shape.to, partialTick); + Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick); + Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick); RenderSystem.lineWidth(shape.lineWidth); drawBoxWireGLLines(tessellator, bufferBuilder, (float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), @@ -223,8 +211,8 @@ public class ShapesRenderer public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.fa == 0.0) return; - Vec3d v1 = relativiseRender(shape.from, partialTick); - Vec3d v2 = relativiseRender(shape.to, partialTick); + Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick); + Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick); RenderSystem.lineWidth(1.0F); drawBoxFaces(tessellator, bufferBuilder, (float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), @@ -245,8 +233,8 @@ public class ShapesRenderer @Override public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { - Vec3d v1 = relativiseRender(shape.from, partialTick); - Vec3d v2 = relativiseRender(shape.to, partialTick); + Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick); + Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick); RenderSystem.lineWidth(shape.lineWidth); drawLine(tessellator, bufferBuilder, (float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), @@ -266,7 +254,7 @@ public class ShapesRenderer public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.a == 0.0) return; - Vec3d vc = relativiseRender(shape.center, partialTick); + Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick); RenderSystem.lineWidth(shape.lineWidth); drawSphereWireframe(tessellator, bufferBuilder, (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), @@ -277,7 +265,7 @@ public class ShapesRenderer public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) { if (shape.fa == 0.0) return; - Vec3d vc = relativiseRender(shape.center, partialTick); + Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick); RenderSystem.lineWidth(1.0f); drawSphereFaces(tessellator, bufferBuilder, (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), @@ -286,6 +274,36 @@ public class ShapesRenderer } } + public static class RenderedCylinder extends RenderedShape + { + public RenderedCylinder(MinecraftClient client, ShapeDispatcher.ExpiringShape shape) + { + super(client, (ShapeDispatcher.Cylinder)shape); + } + @Override + public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) + { + if (shape.a == 0.0) return; + Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick); + RenderSystem.lineWidth(shape.lineWidth); + drawCylinderWireframe(tessellator, bufferBuilder, + (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), + shape.radius, shape.height, shape.axis, shape.subdivisions, + shape.r, shape.g, shape.b, shape.a); + } + @Override + public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) + { + if (shape.fa == 0.0) return; + //Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick); + //RenderSystem.lineWidth(1.0f); + //drawSphereFaces(tessellator, bufferBuilder, + // (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), + // shape.radius, shape.subdivisions, + // shape.fr, shape.fg, shape.fb, shape.fa); + } + } + // some raw shit public static void drawLine(Tessellator tessellator, BufferBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2, float red1, float grn1, float blu1, float alpha) { @@ -409,6 +427,90 @@ public class ShapesRenderer tessellator.draw(); } + public static void drawCylinderWireframe(Tessellator tessellator, BufferBuilder builder, + float cx, float cy, float cz, + float r, float h, Direction.Axis axis, int subd, + float red, float grn, float blu, float alpha) + { + float step = (float)Math.PI / (subd/2); + int num_steps180 = (int)(Math.PI / step)+1; + int num_steps360 = (int)(2*Math.PI / step); + int hsteps = (h>0) ? ((int)Math.ceil(h / (step*r))+1) : 1; + float hstep = (h>0) ? (h / (hsteps-1)) : 1; + // draw base + + if (axis == Direction.Axis.Y) + { + for (int dh = 0; dh < hsteps; dh++) + { + float hh = dh*hstep; + builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR); + for (int i = 0; i <= num_steps360; i++) + { + float theta = step * i; + float x = r * MathHelper.cos(theta); + float y = hh; + float z = r * MathHelper.sin(theta); + builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next(); + } + tessellator.draw(); + } + + if (h > 0) + { + for (int i = 0; i <= num_steps180; i++) + { + builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR); + float theta = step * i; + float x = r * MathHelper.cos(theta); + + float z = r * MathHelper.sin(theta); + + builder.vertex(cx - x, cy + 0, cz + z).color(red, grn, blu, alpha).next(); + builder.vertex(cx + x, cy + 0, cz - z).color(red, grn, blu, alpha).next(); + builder.vertex(cx + x, cy + h, cz - z).color(red, grn, blu, alpha).next(); + builder.vertex(cx - x, cy + h, cz + z).color(red, grn, blu, alpha).next(); + tessellator.draw(); + } + } + + } + else if (axis == Direction.Axis.X) + { + for (int dh = 0; dh < hsteps; dh++) + { + float hh = dh * hstep; + builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR); + for (int i = 0; i <= num_steps360; i++) + { + float theta = step * i; + float z = r * MathHelper.cos(theta); + float x = hh; + float y = r * MathHelper.sin(theta); + builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next(); + } + tessellator.draw(); + } + } + else if (axis == Direction.Axis.Z) + { + for (int dh = 0; dh < hsteps; dh++) + { + float hh = dh * hstep; + builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR); + for (int i = 0; i <= num_steps360; i++) + { + float theta = step * i; + float y = r * MathHelper.cos(theta); + float z = hh; + float x = r * MathHelper.sin(theta); + builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next(); + } + tessellator.draw(); + } + } + } + public static void drawSphereWireframe(Tessellator tessellator, BufferBuilder builder, float cx, float cy, float cz, float r, int subd, diff --git a/src/main/java/carpet/script/value/NumericValue.java b/src/main/java/carpet/script/value/NumericValue.java index fa6a58c9..2ae575fb 100644 --- a/src/main/java/carpet/script/value/NumericValue.java +++ b/src/main/java/carpet/script/value/NumericValue.java @@ -13,6 +13,13 @@ public class NumericValue extends Value private Double value; final static double epsilon = 1024*Double.MIN_VALUE; + public static NumericValue asNumber(Value v1, String id) + { + if (!(v1 instanceof NumericValue)) + throw new InternalExpressionException("Argument "+id+" has to be of a numeric type"); + return ((NumericValue) v1); + } + public static NumericValue asNumber(Value v1) { if (!(v1 instanceof NumericValue)) diff --git a/src/main/resources/assets/carpet/scripts/chunk_display.sc b/src/main/resources/assets/carpet/scripts/chunk_display.sc index 7f770b0a..1eca80e5 100644 --- a/src/main/resources/assets/carpet/scripts/chunk_display.sc +++ b/src/main/resources/assets/carpet/scripts/chunk_display.sc @@ -8,19 +8,19 @@ global_chunk_colors = m( // stable full generation (for '0's, all 1,2,3 are always 'full') l('full', l(0xbbbbbb00, 0x88888800)), // gray // unstable final bits - l('heightmaps', l(0xFFFFFF00, 0x00000000)), // checker - l('spawn', l(0xFFFFFF00, 0x00000000)), // checker - l('light_gen', l(0xFFFFFF00, 0x00000000)), // checker + l('heightmaps', l(0x33333300, 0x22222200)), // checker + l('spawn', l(0x00333300, 0x00222200)), // checker + l('light_gen', l(0x55550000, 0x44440000)), // checker // stable features l('features', l(0x88bb2200, 0x66882200)), // green muddled //stable terrain l('liquid_carvers', l(0x65432100, 0x54321000)), // browns //unstable terrain - not generated yet - l('carvers', l(0xFFFFFF00, 0x00000000)), // checker - l('surface', l(0xFFFFFF00, 0x00000000)), // checker - l('noise', l(0xFFFFFF00, 0x00000000)), // checker - l('biomes', l(0xFFFFFF00, 0x00000000)), // checker - l('structure_references', l(0xFFFFFF00, 0x00000000)), // checker + l('carvers', l(0x33003300, 0x22002200)), // checker + l('surface', l(0x33330000, 0x22220000)), // checker + l('noise', l(0x33000000, 0x22000000)), // checker + l('biomes', l(0x00330000, 0x00220000)), // checker + l('structure_references', l(0x00003300, 0x00002200)), // checker // stable l('structure_starts', l(0x66666600, 0x22222200)), // darkgrey // not stated yet @@ -37,7 +37,7 @@ global_chunk_colors = m( l('post_teleport', l(0xAA00AA00, 0xAA00AA00)), // purple l('start', l(0xDDFF0000, 0xDDFF0000)), // lime, green // recent chunk requests - l('unknown', l(0xFFFFFF00, 0x00000000)) // check + l('unknown', l(0xFF55FF00, 0xff99ff00)) // pink purple ); // map representing current displayed chunkloading setup @@ -67,8 +67,6 @@ __on_player_uses_item(player, item_tuple, hand) -> __setup_tracker(player, item_tuple); ); -//__on_tick() -> null; - __remove_previous_setup(new_player) -> ( if (!global_current_setup, return()); @@ -127,6 +125,7 @@ __chunk_visualizer_tick(p) -> if(!setup, return()); if (!global_running, return()); show_activity = (p~'holds':0 == 'redstone_torch'); + yval = setup:'plot_center':1+16; in_dimension( setup:'plot_dimension', l(sx, sy, sz) = setup:'source_center'; l(px, py, pz) = setup:'plot_center'; @@ -154,8 +153,6 @@ __chunk_visualizer_tick(p) -> for(tickets, l(type, level) = _; if (show_activity || type != 'unknown', - //global_status_cache:source_pos = type; - //changed = true; status = type; ); ); @@ -163,15 +160,15 @@ __chunk_visualizer_tick(p) -> l(cached_status, expiry) = global_status_cache:source_pos; changed = (status != cached_status); if ( changed || (expiry < now), - global_status_cache:source_pos = l(status, now+100+floor(rand(20))); - bpos = l(dx/2, 10, dz/2); + global_status_cache:source_pos = l(status, now+100+floor(rand(40))); + bpos = l(dx/2, yval, dz/2); bcol = global_chunk_colors:status:((dx+dz)%2); shapes += l('box', 150, 'from', bpos, 'to', bpos + l(0.5,0,0.5), - 'color', 0xffffff00, 'fill', bcol+128, 'follow', p); + 'color', 0xffffff00, 'fill', bcol+128, 'follow', p, 'snap', 'xz'); if (changed, pbcol = global_chunk_colors:cached_status:((dx+dz)%2); shapes += l('box', 0, 'from', bpos, 'to', bpos + l(0.5,0,0.5), - 'color', 0xffffff00, 'fill', pbcol+128, 'follow', p); + 'color', 0xffffff00, 'fill', pbcol+128, 'follow', p, 'snap', 'xz'); ); ); );