Merge branch 'master' into 1.16

# Conflicts:
#	src/main/java/carpet/script/CarpetExpression.java
#	src/main/java/carpet/script/utils/ShapeDispatcher.java
#	src/main/java/carpet/script/utils/ShapesRenderer.java
This commit is contained in:
gnembon committed 2020-06-10 01:36:11 -03:00
commit 9aa580cfa5
19 files changed
+1042 -265

No files matched your search

+12 -1
View File
@@ -36,7 +36,10 @@ supplied, only that player will receive particles.
## Markers ## 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 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 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 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. 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 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: 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` - 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 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. 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: Available shapes:
* `'line'` - draws a straight line between two points * `'line'` - draws a straight line between two points
+21 -2
View File
@@ -314,6 +314,13 @@ query(p,'effect','resistance') => null
Number indicating remaining entity health, or `null` if not applicable. 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?)` ### `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: 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:
@@ -470,10 +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 Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting
players, since they are controlled client side. 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` 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 name is not specified,
it clears all effects.
### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)` ### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)`
@@ -513,6 +521,17 @@ Toggles gravity for the entity.
Will set entity on fire for `ticks` ticks. Set to 0 to extinguish. 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 ## 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. There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
+6 -1
View File
@@ -4,7 +4,12 @@
Most functions in this category require inventory as the first argument. Inventory could be specified by an entity, 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 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. 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. Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item.
@@ -0,0 +1,6 @@
package carpet.fakes;
public interface HungerManagerInterface {
float getExhaustionCM();
void setExhaustionCM(float exhaust);
}
@@ -1,11 +1,7 @@
package carpet.mixins; package carpet.mixins;
import carpet.network.CarpetClient; 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.render.debug.DebugRenderer;
import net.minecraft.client.util.math.MatrixStack;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -14,25 +10,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(DebugRenderer.class) @Mixin(DebugRenderer.class)
public class DebugRenderer_scarpetRenderMixin public class DebugRenderer_scarpetRenderMixin
{ {
private ShapesRenderer shapes;
@Inject(method = "<init>", at = @At("RETURN"))
private void addRenderers(MinecraftClient client, CallbackInfo ci)
{
shapes = new ShapesRenderer(client);
CarpetClient.shapes = shapes;
}
@Inject(method = "reset", at = @At("HEAD")) @Inject(method = "reset", at = @At("HEAD"))
private void resetScarpetRenderes(CallbackInfo ci) 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);
}
} }
@@ -0,0 +1,20 @@
package carpet.mixins;
import carpet.fakes.HungerManagerInterface;
import net.minecraft.entity.player.HungerManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(HungerManager.class)
public class HungerManagerMixin_scarpetEventsMixin implements HungerManagerInterface {
@Shadow private float exhaustion;
public float getExhaustionCM(){
return exhaustion;
}
@Override
public void setExhaustionCM(float exhaust)
{
exhaustion = exhaust;
}
}
@@ -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 = "<init>", 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);
}
}
@@ -8,6 +8,7 @@ import io.netty.buffer.Unpooled;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.nbt.AbstractNumberTag; import net.minecraft.nbt.AbstractNumberTag;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket; import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
@@ -35,10 +36,14 @@ public class ClientNetworkHandler
} }
}); });
put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false)); put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false));
put("scShape", (p, t) -> { put("scShape", (p, t) -> { // deprecated
if (CarpetClient.shapes != null) if (CarpetClient.shapes != null)
CarpetClient.shapes.addShape((CompoundTag)t); CarpetClient.shapes.addShape((CompoundTag)t);
}); });
put("scShapes", (p, t) -> {
if (CarpetClient.shapes != null)
CarpetClient.shapes.addShapes((ListTag) t);
});
}}; }};
@@ -2678,47 +2678,28 @@ public class CarpetExpression
this.expr.addLazyFunction("draw_shape", -1, (c, t, lv) -> this.expr.addLazyFunction("draw_shape", -1, (c, t, lv) ->
{ {
CarpetContext cc = (CarpetContext)c; CarpetContext cc = (CarpetContext)c;
if (lv.size() < 3) throw new InternalExpressionException("'draw_shape' takes at least three parameters, shape name, duration, and its params"); ServerPlayerEntity player[] = {null};
String shapeType = lv.get(0).evalValue(c).getString(); List<Pair<ShapeDispatcher.ExpiringShape, Map<String,Value>>> shapes = new ArrayList<>();
Value duration = NumericValue.asNumber(lv.get(1).evalValue(c)); if (lv.size() == 1) // bulk
Map<String, Value> params;
if (lv.size() == 3)
{ {
Value paramValue = lv.get(2).evalValue(c); Value specLoad = lv.get(0).evalValue(c);
if (paramValue instanceof MapValue) 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<>(); if (!(list instanceof ListValue)) throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs");
((MapValue) paramValue).getMap().entrySet().forEach(e -> params.put(e.getKey().getString(),e.getValue())); 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 else
{ {
List<Value> paramList = new ArrayList<>(); List<Value> params = new ArrayList<>();
for (int i=2; i < lv.size(); i++) paramList.add(lv.get(i).evalValue(c)); for (LazyValue v : lv) params.add(v.evalValue(c));
params = ShapeDispatcher.parseParams(paramList); shapes.add(ShapeDispatcher.fromFunctionArgs(cc, params, player));
} }
params.putIfAbsent("dim", new StringValue(cc.s.getWorld().getRegistryKey().getValue().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( ShapeDispatcher.sendShape(
(player==null)?cc.s.getWorld().getPlayers():Collections.singletonList(player), (player[0]==null)?cc.s.getWorld().getPlayers():Collections.singletonList(player[0]),
shape, shapes
params
); );
return LazyValue.TRUE; return LazyValue.TRUE;
}); });
@@ -50,6 +50,7 @@ public class CarpetScriptServer
add(new BundledModule("event_test", false)); add(new BundledModule("event_test", false));
add(new BundledModule("stats_test", false)); add(new BundledModule("stats_test", false));
add(new BundledModule("math", true)); add(new BundledModule("math", true));
add(new BundledModule("chunk_display", false));
}}; }};
public CarpetScriptServer(MinecraftServer server) public CarpetScriptServer(MinecraftServer server)
@@ -8,6 +8,7 @@ import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BlockValue; import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue; import carpet.script.value.EntityValue;
import carpet.script.value.ListValue; import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue; import carpet.script.value.NumericValue;
import carpet.script.value.StringValue; import carpet.script.value.StringValue;
import carpet.script.value.Value; import carpet.script.value.Value;
@@ -19,8 +20,10 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.arguments.ParticleArgumentType; import net.minecraft.command.arguments.ParticleArgumentType;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.FloatTag; import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.IntTag; import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringTag; import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleEffect;
@@ -28,13 +31,17 @@ import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.dimension.DimensionType;
import net.minecraft.util.registry.RegistryKey; import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -51,23 +58,90 @@ public class ShapeDispatcher
{ {
private static final Map<String, ParticleEffect> particleCache = new HashMap<>(); private static final Map<String, ParticleEffect> particleCache = new HashMap<>();
public static void sendShape(List<ServerPlayerEntity> players, ExpiringShape shape, Map<String, Value> params) public static Pair<ExpiringShape,Map<String, Value>> fromFunctionArgs(
CarpetContext cc,
List<Value> lv,
ServerPlayerEntity[] playerRef
)
{ {
Consumer<ServerPlayerEntity> alternative = null; if (lv.size() < 3) throw new InternalExpressionException("'draw_shape' takes at least three parameters, shape name, duration, and its params");
CompoundTag tag = null; String shapeType = lv.get(0).getString();
Value duration = NumericValue.asNumber(lv.get(1), "duration");
Map<String, Value> 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<Value> 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<ServerPlayerEntity> players, List<Pair<ExpiringShape,Map<String, Value>>> shapes)
{
List<ServerPlayerEntity> clientPlayers = new ArrayList<>();
List<ServerPlayerEntity> alternativePlayers = new ArrayList<>();
for (ServerPlayerEntity player : players) for (ServerPlayerEntity player : players)
{ {
if (ServerNetworkHandler.validCarpetPlayers.contains(player)) if (ServerNetworkHandler.validCarpetPlayers.contains(player))
{ {
if (tag == null) tag = ExpiringShape.toTag(params); clientPlayers.add(player);
ServerNetworkHandler.sendCustomCommand(player,"scShape", tag);
} }
else else
{ {
if (alternative == null) alternative = shape.alternative(); alternativePlayers.add(player);
alternative.accept(player);
} }
} }
if (!clientPlayers.isEmpty())
{
ListTag tag = new ListTag();
int tagcount = 0;
for (Pair<ExpiringShape, Map<String, Value>> 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<Consumer<ServerPlayerEntity>> 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) public static ParticleEffect getParticleData(String name)
@@ -160,6 +234,7 @@ public class ShapeDispatcher
put("line", creator(Line::new)); put("line", creator(Line::new));
put("box", creator(Box::new)); put("box", creator(Box::new));
put("sphere", creator(Sphere::new)); put("sphere", creator(Sphere::new));
put("cylinder", creator(Cylinder::new));
}}; }};
private static Function<Map<String, Value>,ExpiringShape> creator(Supplier<ExpiringShape> shapeFactory) private static Function<Map<String, Value>,ExpiringShape> creator(Supplier<ExpiringShape> shapeFactory)
{ {
@@ -176,9 +251,11 @@ public class ShapeDispatcher
protected float fr, fg, fb, fa; protected float fr, fg, fb, fa;
protected int fillColor; protected int fillColor;
protected int duration = 0; protected int duration = 0;
private int key; private long key;
protected int followEntity; protected int followEntity;
protected RegistryKey<World> entityDimension; protected String snapTo;
protected boolean snapX, snapY, snapZ;
protected RegistryKey<World> shapeDimension;
protected ExpiringShape() { } protected ExpiringShape() { }
@@ -216,7 +293,7 @@ public class ShapeDispatcher
duration = NumericValue.asNumber(options.get("duration")).getInt(); 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(); fillColor = NumericValue.asNumber(options.getOrDefault("fill", optional.get("fill"))).getInt();
this.fr = (float)(fillColor >> 24 & 0xFF) / 255.0F; this.fr = (float)(fillColor >> 24 & 0xFF) / 255.0F;
@@ -232,18 +309,33 @@ public class ShapeDispatcher
key = 0; key = 0;
followEntity = -1; followEntity = -1;
entityDimension = null; shapeDimension = RegistryKey.of(Registry.DIMENSION, new Identifier(options.get("dim").getString()));
if (options.containsKey("follow")) if (options.containsKey("follow"))
{ {
followEntity = NumericValue.asNumber(options.getOrDefault("follow", optional.get("follow"))).getInt(); followEntity = NumericValue.asNumber(options.getOrDefault("follow", optional.get("follow"))).getInt();
entityDimension = RegistryKey.of(Registry.DIMENSION, 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 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) public Vec3d vecFromValue(Value value)
{ {
if (!(value instanceof ListValue)) throw new InternalExpressionException("decoded value of "+value.getPrettyString()+" is not a triple"); if (!(value instanceof ListValue)) throw new InternalExpressionException("decoded value of "+value.getPrettyString()+" is not a triple");
@@ -256,19 +348,20 @@ public class ShapeDispatcher
} }
public abstract Consumer<ServerPlayerEntity> alternative(); public abstract Consumer<ServerPlayerEntity> alternative();
public int key() public long key()
{ {
if (key!=0) return key; if (key!=0) return key;
key = calcKey(); key = calcKey();
return key; return key;
} }
protected int calcKey() protected long calcKey()
{ { // using FNV-1a algorithm
int hash = 17; long hash = -3750763034362895579L;
hash = 31*hash + color; hash ^= shapeDimension.hashCode(); hash *= 1099511628211L;
hash = 31*hash + followEntity; hash ^= color; hash *= 1099511628211L;
hash = 31*hash + Float.hashCode(lineWidth); hash ^= followEntity; hash *= 1099511628211L;
if (fa != 0.0) hash = 31*hash + fillColor; hash ^= Float.hashCode(lineWidth); hash *= 1099511628211L;
if (fa != 0.0) { hash = 31*hash + fillColor; hash *= 1099511628211L; }
return hash; return hash;
} }
// list of params that need to be there // list of params that need to be there
@@ -276,8 +369,9 @@ public class ShapeDispatcher
private final Map<String, Value> optional = ImmutableMap.of( private final Map<String, Value> optional = ImmutableMap.of(
"color", new NumericValue(-1), "color", new NumericValue(-1),
"follow", new NumericValue(-1), "follow", new NumericValue(-1),
"width", new NumericValue(2.0), "line", new NumericValue(2.0),
"fill", new NumericValue(0xffffff00) "fill", new NumericValue(0xffffff00),
"snap", new StringValue("xyz")
); );
protected Set<String> requiredParams() {return required;} protected Set<String> requiredParams() {return required;}
// list of params that can be there, with defaults // list of params that can be there, with defaults
@@ -308,6 +402,7 @@ public class ShapeDispatcher
super.init(options); super.init(options);
from = vecFromValue(options.get("from")); from = vecFromValue(options.get("from"));
to = vecFromValue(options.get("to")); to = vecFromValue(options.get("to"));
if (from.equals(to)) throw new InternalExpressionException("Box dimensions are invalid - cannot draw a zero-size boxes");
} }
@Override @Override
@@ -318,26 +413,26 @@ public class ShapeDispatcher
double density = Math.max(2.0, from.distanceTo(to) /50) / (a+0.1); double density = Math.max(2.0, from.distanceTo(to) /50) / (a+0.1);
return p -> return p ->
{ {
if (followEntity == -1) if (p.dimension == shapeDimension)
{ {
particleMesh(Collections.singletonList(p), particle, density, from, to); particleMesh(
return; 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);
}; };
} }
@Override @Override
public int calcKey() public long calcKey()
{ {
int hash = super.calcKey(); long hash = super.calcKey();
hash = 31*hash + 1; hash ^= 1; hash *= 1099511628211L;
hash = 31*hash + from.hashCode(); hash ^= from.hashCode(); hash *= 1099511628211L;
hash = 31*hash + to.hashCode(); hash ^= to.hashCode(); hash *= 1099511628211L;
return hash; return hash;
} }
@@ -391,6 +486,7 @@ public class ShapeDispatcher
super.init(options); super.init(options);
from = vecFromValue(options.get("from")); from = vecFromValue(options.get("from"));
to = vecFromValue(options.get("to")); to = vecFromValue(options.get("to"));
if (from.equals(to)) throw new InternalExpressionException("Line dimensions are invalid - cannot draw a zero-length lines");
} }
@Override @Override
@@ -399,29 +495,26 @@ public class ShapeDispatcher
String particleName = String.format(Locale.ROOT , "dust %.1f %.1f %.1f 1.0", r, g, b); String particleName = String.format(Locale.ROOT , "dust %.1f %.1f %.1f 1.0", r, g, b);
ParticleEffect particle = getParticleData(particleName); ParticleEffect particle = getParticleData(particleName);
double density = Math.max(2.0, from.distanceTo(to) /50) / (a+0.1); double density = Math.max(2.0, from.distanceTo(to) /50) / (a+0.1);
return p -> return p ->
{ {
if (followEntity == -1) if (p.dimension == shapeDimension) drawParticleLine(
{ Collections.singletonList(p),
drawParticleLine(Collections.singletonList(p), particle, from, to, density); particle,
return; relativiseRender(p.getServerWorld(), from, 0),
} relativiseRender(p.getServerWorld(), to, 0),
density
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);
}; };
} }
@Override @Override
public int calcKey() public long calcKey()
{ {
int hash = super.calcKey(); long hash = super.calcKey();
hash = 31*hash + 2; hash ^= 2; hash *= 1099511628211L;
hash = 31*hash + from.hashCode(); hash ^= from.hashCode(); hash *= 1099511628211L;
hash = 31*hash + to.hashCode(); hash ^= to.hashCode(); hash *= 1099511628211L;
return hash; return hash;
} }
} }
@@ -462,24 +555,18 @@ public class ShapeDispatcher
@Override @Override
public Consumer<ServerPlayerEntity> alternative() { return p -> public Consumer<ServerPlayerEntity> alternative() { return p ->
{ {
if (p.dimension != shapeDimension) return;
String particleName = String.format(Locale.ROOT , "dust %.1f %.1f %.1f 1.0", r, g, b); String particleName = String.format(Locale.ROOT , "dust %.1f %.1f %.1f 1.0", r, g, b);
ParticleEffect particle = getParticleData(particleName); ParticleEffect particle = getParticleData(particleName);
int partno = Math.min(1000,20*subdivisions); int partno = Math.min(1000,20*subdivisions);
Random rand = p.world.getRandom(); Random rand = p.world.getRandom();
ServerWorld world = p.getServerWorld(); ServerWorld world = p.getServerWorld();
double ccx, ccy, ccz; Vec3d ccenter = relativiseRender(world, center, 0 );
if (followEntity == -1)
{ double ccx = ccenter.x;
ccx = center.x; ccy = center.y; ccz = center.z; double ccy = ccenter.y;
} double ccz = ccenter.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;
}
for (int i=0; i<partno; i++) for (int i=0; i<partno; i++)
{ {
@@ -496,13 +583,117 @@ public class ShapeDispatcher
};} };}
@Override @Override
public int calcKey() public long calcKey()
{ {
int hash = super.calcKey(); long hash = super.calcKey();
hash = 31*hash + 3; hash ^= 3; hash *= 1099511628211L;
hash = 31*hash + center.hashCode(); hash ^= center.hashCode(); hash *= 1099511628211L;
hash = 31*hash + Double.hashCode(radius); hash ^= Double.hashCode(radius); hash *= 1099511628211L;
hash = 31*hash + level; hash ^= level; hash *= 1099511628211L;
return hash;
}
}
public static class Cylinder extends ExpiringShape
{
private final Set<String> required = ImmutableSet.of("center", "radius");
private final Map<String, Value> optional = ImmutableMap.of(
"level", Value.ZERO,
"height", Value.ZERO,
"axis", new StringValue("y")
);
@Override
protected Set<String> requiredParams() { return Sets.union(super.requiredParams(), required); }
@Override
protected Set<String> 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<String, Value> 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<ServerPlayerEntity> 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<partno; i++)
{
float d = rand.nextFloat()*height;
float phi = (float)(2*Math.PI*rand.nextDouble());
double x = radius * MathHelper.cos(phi);
double y = d;
double z = radius * MathHelper.sin(phi);
world.spawnParticles(p, particle, true, x+ccx, y+ccy, z+ccz, 1, 0.0, 0.0, 0.0, 0.0);
}
}
else if (axis== Direction.Axis.X)
{
for (int i=0; i<partno; i++)
{
float d = rand.nextFloat()*height;
float phi = (float)(2*Math.PI*rand.nextDouble());
double x = d;
double y = radius * MathHelper.cos(phi);
double z = radius * MathHelper.sin(phi);
world.spawnParticles(p, particle, true, x+ccx, y+ccy, z+ccz, 1, 0.0, 0.0, 0.0, 0.0);
}
}
else // Z
{
for (int i=0; i<partno; i++)
{
float d = rand.nextFloat()*height;
float phi = (float)(2*Math.PI*rand.nextDouble());
double x = radius * MathHelper.sin(phi);
double y = radius * MathHelper.cos(phi);
double z = d;
world.spawnParticles(p, particle, true, x+ccx, y+ccy, z+ccz, 1, 0.0, 0.0, 0.0, 0.0);
}
}
};}
@Override
public long calcKey()
{
long hash = super.calcKey();
hash ^= 3; hash *= 1099511628211L;
hash ^= center.hashCode(); hash *= 1099511628211L;
hash ^= Double.hashCode(radius); hash *= 1099511628211L;
hash ^= Double.hashCode(height); hash *= 1099511628211L;
hash ^= level; hash *= 1099511628211L;
return hash; return hash;
} }
} }
@@ -513,10 +704,11 @@ public class ShapeDispatcher
public static Map<String, Param> of = new HashMap<String, Param>(){{ public static Map<String, Param> of = new HashMap<String, Param>(){{
put("shape", new ShapeParam()); put("shape", new ShapeParam());
put("dim", new DimensionParam()); put("dim", new DimensionParam());
put("duration", new PositiveIntParam("duration")); put("duration", new NonNegativeIntParam("duration"));
put("color", new ColorParam("color")); put("color", new ColorParam("color"));
put("follow", new EntityParam("follow")); put("follow", new EntityParam("follow"));
put("width", new PositiveFloatParam("width")); put("snap", new StringChoiceParam("snap", "xyz", "xz", "yz", "xy", "x", "y", "z"));
put("line", new PositiveFloatParam("line"));
put("fill", new ColorParam("fill")); put("fill", new ColorParam("fill"));
put("from", new Vec3Param("from", false)); put("from", new Vec3Param("from", false));
@@ -524,6 +716,9 @@ public class ShapeDispatcher
put("center", new Vec3Param("center", false)); put("center", new Vec3Param("center", false));
put("radius", new PositiveFloatParam("radius")); put("radius", new PositiveFloatParam("radius"));
put("level", new PositiveIntParam("level")); put("level", new PositiveIntParam("level"));
put("height", new NonNegativeFloatParam("height"));
put("axis", new StringChoiceParam("axis", "x", "y", "z"));
put("points", new PointsParam("points"));
}}; }};
protected String id; protected String id;
protected Param(String id) protected Param(String id)
@@ -545,6 +740,24 @@ public class ShapeDispatcher
public Value decode(Tag tag) { return new StringValue(tag.asString()); } public Value decode(Tag tag) { return new StringValue(tag.asString()); }
} }
public static class StringChoiceParam extends StringParam
{
private Set<String> options;
public StringChoiceParam(String id, String ... options)
{
super(id);
this.options = Sets.newHashSet(options);
}
@Override
public Value validate(Map<String, Value> options, CarpetContext cc, Value value)
{
if (this.options.contains(value.getString())) return value;
return null;
}
}
public static class DimensionParam extends StringParam public static class DimensionParam extends StringParam
{ {
protected DimensionParam() { super("dim"); } protected DimensionParam() { super("dim"); }
@@ -593,7 +806,7 @@ public class ShapeDispatcher
@Override @Override
public Value decode(Tag tag) { return new NumericValue(((FloatTag)tag).getFloat()); } public Value decode(Tag tag) { return new NumericValue(((FloatTag)tag).getFloat()); }
@Override @Override
public Tag toTag(Value value) { return FloatTag.of(NumericValue.asNumber(value).getFloat()); } public Tag toTag(Value value) { return FloatTag.of(NumericValue.asNumber(value, id).getFloat()); }
} }
public static class PositiveIntParam extends PositiveParam public static class PositiveIntParam extends PositiveParam
@@ -602,9 +815,37 @@ public class ShapeDispatcher
@Override @Override
public Value decode(Tag tag) { return new NumericValue(((IntTag)tag).getInt()); } public Value decode(Tag tag) { return new NumericValue(((IntTag)tag).getInt()); }
@Override @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 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, id).getInt()); }
@Override public Value validate(Map<String, Value> 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<String, Value> 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 public static class Vec3Param extends Param
{ {
@@ -616,13 +857,17 @@ public class ShapeDispatcher
} }
@Override @Override
public Value validate(Map<String, Value> options, CarpetContext cc, Value value) public Value validate(Map<String, Value> options, CarpetContext cc, Value value)
{
return validate(this, options, cc, value, roundsUpForBlocks);
}
public static Value validate(Param p, Map<String, Value> options, CarpetContext cc, Value value, boolean roundsUp)
{ {
if (value instanceof BlockValue) if (value instanceof BlockValue)
{ {
if (options.containsKey("follow")) 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(); BlockPos pos = ((BlockValue) value).getPos();
int offset = roundsUpForBlocks?1:0; int offset = roundsUp?1:0;
return ListValue.of( return ListValue.of(
new NumericValue(pos.getX()+offset), new NumericValue(pos.getX()+offset),
new NumericValue(pos.getY()+offset), new NumericValue(pos.getY()+offset),
@@ -632,18 +877,18 @@ public class ShapeDispatcher
if (value instanceof ListValue) if (value instanceof ListValue)
{ {
List<Value> values = ((ListValue) value).getItems(); List<Value> 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) for (Value component : values)
{ {
if (!(component instanceof NumericValue)) if (!(component instanceof NumericValue))
throw new InternalExpressionException("'"+id+"' requires 3 numerical values"); throw new InternalExpressionException("'"+p.id+"' requires 3 numerical values");
} }
return value; return value;
} }
if (value instanceof EntityValue) if (value instanceof EntityValue)
{ {
if (options.containsKey("follow")) 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(); Entity e = ((EntityValue) value).getEntity();
return ListValue.of( return ListValue.of(
new NumericValue(e.getX()), new NumericValue(e.getX()),
@@ -652,29 +897,81 @@ public class ShapeDispatcher
); );
} }
CarpetSettings.LOG.error("Value: "+value.getString()); 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) public Value decode(Tag tag)
{ {
CompoundTag ctag = (CompoundTag)tag; ListTag ctag = (ListTag)tag;
return ListValue.of( return ListValue.of(
new NumericValue(ctag.getDouble("x")), new NumericValue(ctag.getDouble(0)),
new NumericValue(ctag.getDouble("y")), new NumericValue(ctag.getDouble(1)),
new NumericValue(ctag.getDouble("z")) new NumericValue(ctag.getDouble(2))
); );
} }
@Override @Override
public Tag toTag(Value value) public Tag toTag(Value value)
{ {
List<Value> lv = ((ListValue)value).getItems(); List<Value> lv = ((ListValue)value).getItems();
CompoundTag tag = new CompoundTag(); ListTag tag = new ListTag();
tag.putDouble("x", NumericValue.asNumber(lv.get(0)).getDouble()); tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(0), "x").getDouble()));
tag.putDouble("y", NumericValue.asNumber(lv.get(1)).getDouble()); tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(1), "y").getDouble()));
tag.putDouble("z", NumericValue.asNumber(lv.get(2)).getDouble()); tag.add(DoubleTag.of(NumericValue.asNumber(lv.get(2), "z").getDouble()));
return tag; return tag;
} }
} }
public static class PointsParam extends Param
{
public PointsParam(String id)
{
super(id);
}
@Override
public Value validate(Map<String, Value> options, CarpetContext cc, Value value)
{
if (!(value instanceof ListValue))
throw new InternalExpressionException(id+ " parameter should be a list");
List<Value> 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<Value> points = new ArrayList<>();
for (int i=0, ll = ltag.size(); i<ll; i++)
{
ListTag ptag = ltag.getList(i);
points.add(ListValue.of(
new NumericValue(ptag.getDouble(0)),
new NumericValue(ptag.getDouble(1)),
new NumericValue(ptag.getDouble(2))
));
}
return ListValue.wrap(points);
}
@Override
public Tag toTag(Value pointsValue)
{
List<Value> lv = ((ListValue)pointsValue).getItems();
ListTag ltag = new ListTag();
for (Value value : lv)
{
List<Value> coords = ((ListValue)value).getItems();
ListTag tag = new ListTag();
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;
}
}
public static class ColorParam extends NumericParam public static class ColorParam extends NumericParam
{ {
protected ColorParam(String id) protected ColorParam(String id)
@@ -684,7 +981,7 @@ public class ShapeDispatcher
public Value decode(Tag tag) { return new NumericValue(((IntTag)tag).getInt()); } public Value decode(Tag tag) { return new NumericValue(((IntTag)tag).getInt()); }
@Override @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 public static class EntityParam extends Param
@@ -695,7 +992,7 @@ public class ShapeDispatcher
@Override @Override
public Tag toTag(Value value) public Tag toTag(Value value)
{ {
return IntTag.of(NumericValue.asNumber(value).getInt()); return IntTag.of(NumericValue.asNumber(value, id).getInt());
} }
@Override @Override
@@ -2,20 +2,19 @@ package carpet.script.utils;
import carpet.CarpetSettings; import carpet.CarpetSettings;
import com.mojang.blaze3d.systems.RenderSystem; 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.MinecraftClient;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.client.render.VertexFormats; 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.CompoundTag;
import net.minecraft.util.Identifier; import net.minecraft.nbt.ListTag;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey; import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -26,7 +25,7 @@ import java.util.function.BiFunction;
public class ShapesRenderer public class ShapesRenderer
{ {
private final Map<RegistryKey<World>, Int2ObjectOpenHashMap<RenderedShape<? extends ShapeDispatcher.ExpiringShape>>> shapes; private final Map<RegistryKey<World>, Long2ObjectOpenHashMap<RenderedShape<? extends ShapeDispatcher.ExpiringShape>>> shapes;
private MinecraftClient client; private MinecraftClient client;
private Map<String, BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape >>> renderedShapes private Map<String, BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape >>> renderedShapes
@@ -35,18 +34,19 @@ public class ShapesRenderer
put("line", RenderedLine::new); put("line", RenderedLine::new);
put("box", RenderedBox::new); put("box", RenderedBox::new);
put("sphere", RenderedSphere::new); put("sphere", RenderedSphere::new);
put("cylinder", RenderedCylinder::new);
}}; }};
public ShapesRenderer(MinecraftClient minecraftClient) public ShapesRenderer(MinecraftClient minecraftClient)
{ {
this.client = minecraftClient; this.client = minecraftClient;
shapes = new HashMap<>(); shapes = new HashMap<>();
shapes.put(World.OVERWORLD, new Int2ObjectOpenHashMap<>()); shapes.put(World.OVERWORLD, new Long2ObjectOpenHashMap<>());
shapes.put(World.NETHER, new Int2ObjectOpenHashMap<>()); shapes.put(World.NETHER, new Long2ObjectOpenHashMap<>());
shapes.put(World.END, new Int2ObjectOpenHashMap<>()); shapes.put(World.END, new Long2ObjectOpenHashMap<>());
} }
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, double cameraX, double cameraY, double cameraZ) public void render(Camera camera, float partialTick)
{ {
//Camera camera = this.client.gameRenderer.getCamera(); //Camera camera = this.client.gameRenderer.getCamera();
ClientWorld iWorld = this.client.world; ClientWorld iWorld = this.client.world;
@@ -70,20 +70,25 @@ public class ShapesRenderer
//RenderSystem.polygonOffset(-3f, -3f); //RenderSystem.polygonOffset(-3f, -3f);
//RenderSystem.enablePolygonOffset(); //RenderSystem.enablePolygonOffset();
//Entity entity = this.client.gameRenderer.getCamera().getFocusedEntity(); //Entity entity = this.client.gameRenderer.getCamera().getFocusedEntity();
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer(); BufferBuilder bufferBuilder = tessellator.getBuffer();
// render // render
double cameraX = camera.getPos().x;
double cameraY = camera.getPos().y;
double cameraZ = camera.getPos().z;
synchronized (shapes) synchronized (shapes)
{ {
shapes.get(dimensionType).int2ObjectEntrySet().removeIf( shapes.get(dimensionType).long2ObjectEntrySet().removeIf(
entry -> entry.getValue().isExpired(currentTime) entry -> entry.getValue().isExpired(currentTime)
); );
shapes.get(dimensionType).values().forEach( shapes.get(dimensionType).values().forEach(
s -> s ->
{ {
if ( s.shouldRender(dimensionType)) if ( s.shouldRender(dimensionType))
s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ); s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick);
} }
); );
//lines //lines
@@ -91,7 +96,7 @@ public class ShapesRenderer
s -> { s -> {
if ( s.shouldRender(dimensionType)) if ( s.shouldRender(dimensionType))
s.renderLines(tessellator, bufferBuilder, cameraX, cameraY, cameraZ); s.renderLines(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick);
} }
); );
} }
@@ -104,6 +109,14 @@ public class ShapesRenderer
RenderSystem.shadeModel(7424); 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) public void addShape(CompoundTag tag)
{ {
ShapeDispatcher.ExpiringShape shape = ShapeDispatcher.fromTag(tag); ShapeDispatcher.ExpiringShape shape = ShapeDispatcher.fromTag(tag);
@@ -117,8 +130,8 @@ public class ShapesRenderer
else else
{ {
RenderedShape<?> rshape = shapeFactory.apply(client, shape); RenderedShape<?> rshape = shapeFactory.apply(client, shape);
RegistryKey<World> dim = RegistryKey.of(Registry.DIMENSION, new Identifier(tag.getString("dim"))); RegistryKey<World> dim = shape.shapeDimension;
int key = rshape.key(); long key = rshape.key();
synchronized (shapes) synchronized (shapes)
{ {
RenderedShape<?> existing = shapes.get(dim).get(key); RenderedShape<?> existing = shapes.get(dim).get(key);
@@ -137,7 +150,7 @@ public class ShapesRenderer
{ {
synchronized (shapes) synchronized (shapes)
{ {
shapes.values().forEach(Int2ObjectOpenHashMap::clear); shapes.values().forEach(Long2ObjectOpenHashMap::clear);
} }
} }
@@ -148,13 +161,13 @@ public class ShapesRenderer
protected MinecraftClient client; protected MinecraftClient client;
long expiryTick; long expiryTick;
double renderEpsilon = 0; double renderEpsilon = 0;
public abstract void renderLines(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 ) {} public void renderFaces(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz, float partialTick ) {}
protected RenderedShape(MinecraftClient client, T shape) protected RenderedShape(MinecraftClient client, T shape)
{ {
this.shape = shape; this.shape = shape;
expiryTick = client.world.getTime()+shape.getExpiry(); 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; this.client = client;
} }
@@ -162,7 +175,7 @@ public class ShapesRenderer
{ {
return expiryTick < currentTick; return expiryTick < currentTick;
} }
public int key() public long key()
{ {
return shape.key(); return shape.key();
}; };
@@ -174,12 +187,6 @@ public class ShapesRenderer
if (client.world.getEntityById(shape.followEntity) == null) return false; if (client.world.getEntityById(shape.followEntity) == null) return false;
return true; return true;
} }
protected Vec3d relativiseRender(Vec3d vec)
{
if (shape.followEntity <= 0) return vec;
Entity e = client.world.getEntityById(shape.followEntity);
return shape.toAbsolute(e, vec);
}
} }
public static class RenderedBox extends RenderedShape<ShapeDispatcher.Box> public static class RenderedBox extends RenderedShape<ShapeDispatcher.Box>
@@ -191,28 +198,30 @@ public class ShapesRenderer
} }
@Override @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; if (shape.a == 0.0) return;
Vec3d v1 = relativiseRender(shape.from); Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
Vec3d v2 = relativiseRender(shape.to); Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
RenderSystem.lineWidth(shape.lineWidth); RenderSystem.lineWidth(shape.lineWidth);
drawBoxWireGLLines(tessellator, bufferBuilder, drawBoxWireGLLines(tessellator, bufferBuilder,
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), (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), (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 shape.r, shape.g, shape.b, shape.a, shape.r, shape.g, shape.b
); );
} }
@Override @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; if (shape.fa == 0.0) return;
Vec3d v1 = relativiseRender(shape.from); Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
Vec3d v2 = relativiseRender(shape.to); Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
RenderSystem.lineWidth(1.0F); RenderSystem.lineWidth(1.0F);
drawBoxFaces(tessellator, bufferBuilder, drawBoxFaces(tessellator, bufferBuilder,
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), (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), (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 shape.fr, shape.fg, shape.fb, shape.fa
); );
} }
@@ -226,10 +235,10 @@ public class ShapesRenderer
super(client, (ShapeDispatcher.Line)shape); super(client, (ShapeDispatcher.Line)shape);
} }
@Override @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 v1 = shape.relativiseRender(client.world, shape.from, partialTick);
Vec3d v2 = relativiseRender(shape.to); Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
RenderSystem.lineWidth(shape.lineWidth); RenderSystem.lineWidth(shape.lineWidth);
drawLine(tessellator, bufferBuilder, drawLine(tessellator, bufferBuilder,
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon), (float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon),
@@ -246,10 +255,10 @@ public class ShapesRenderer
super(client, (ShapeDispatcher.Sphere)shape); super(client, (ShapeDispatcher.Sphere)shape);
} }
@Override @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; if (shape.a == 0.0) return;
Vec3d vc = relativiseRender(shape.center); Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
RenderSystem.lineWidth(shape.lineWidth); RenderSystem.lineWidth(shape.lineWidth);
drawSphereWireframe(tessellator, bufferBuilder, drawSphereWireframe(tessellator, bufferBuilder,
(float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon),
@@ -257,10 +266,10 @@ public class ShapesRenderer
shape.r, shape.g, shape.b, shape.a); shape.r, shape.g, shape.b, shape.a);
} }
@Override @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; if (shape.fa == 0.0) return;
Vec3d vc = relativiseRender(shape.center); Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
RenderSystem.lineWidth(1.0f); RenderSystem.lineWidth(1.0f);
drawSphereFaces(tessellator, bufferBuilder, drawSphereFaces(tessellator, bufferBuilder,
(float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon), (float)(vc.x-cx-renderEpsilon), (float)(vc.y-cy-renderEpsilon), (float)(vc.z-cz-renderEpsilon),
@@ -269,6 +278,36 @@ public class ShapesRenderer
} }
} }
public static class RenderedCylinder extends RenderedShape<ShapeDispatcher.Cylinder>
{
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 // 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) { 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) {
@@ -278,71 +317,204 @@ public class ShapesRenderer
tessellator.draw(); 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.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3
builder.vertex(x1, y1, z1).color(red1, grn2, blu2, alpha).next(); if (xthick)
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, y1, z1).color(red1, grn2, blu2, alpha).next();
builder.vertex(x1, y2, z1).color(red2, grn1, blu2, alpha).next(); builder.vertex(x2, y1, z1).color(red1, grn2, 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, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, 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, z1).color(red1, grn1, blu1, alpha).next(); builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next(); builder.vertex(x2, y1, z2).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, y2, z2).color(red1, grn1, blu1, alpha).next(); builder.vertex(x2, 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(); if (ythick)
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); {
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); builder.vertex(x1, y1, z1).color(red2, grn1, blu2, alpha).next();
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next(); builder.vertex(x1, y2, z1).color(red2, grn1, blu2, 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, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, 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();
builder.vertex(x2, y2, 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(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(); 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.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(); if (xthick && ythick)
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); {
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); builder.vertex(x1, y1, z1).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(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(); if (zthick && ythick)
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next(); {
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); builder.vertex(x1, 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, 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(); if (xthick)
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();
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(); // now at least drawing one
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next(); if (zthick && xthick)
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next(); {
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next(); 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(); 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, public static void drawSphereWireframe(Tessellator tessellator, BufferBuilder builder,
float cx, float cy, float cz, float cx, float cy, float cz,
float r, int subd, float r, int subd,
@@ -1,17 +1,21 @@
package carpet.script.value; package carpet.script.value;
import carpet.CarpetSettings;
import carpet.fakes.EntityInterface; import carpet.fakes.EntityInterface;
import carpet.fakes.ItemEntityInterface; import carpet.fakes.ItemEntityInterface;
import carpet.fakes.LivingEntityInterface; import carpet.fakes.LivingEntityInterface;
import carpet.fakes.MobEntityInterface; import carpet.fakes.MobEntityInterface;
import carpet.fakes.HungerManagerInterface;
import carpet.helpers.Tracer; import carpet.helpers.Tracer;
import carpet.patches.EntityPlayerMPFake; import carpet.patches.EntityPlayerMPFake;
import carpet.script.CarpetContext; import carpet.script.CarpetContext;
import carpet.script.EntityEventsGroup; import carpet.script.EntityEventsGroup;
import carpet.script.argument.Vector3Argument; import carpet.script.argument.Vector3Argument;
import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InternalExpressionException;
import carpet.utils.Messenger;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException; 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.EntityPassengersSetS2CPacket;
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket; import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
@@ -285,6 +289,20 @@ public class EntityValue extends Value
put("sneaking", (e, a) -> e.isSneaking()?Value.TRUE:Value.FALSE); put("sneaking", (e, a) -> e.isSneaking()?Value.TRUE:Value.FALSE);
put("sprinting", (e, a) -> e.isSprinting()?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("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()).getExhaustionCM());
return Value.NULL;
});
put("jumping", (e, a) -> { put("jumping", (e, a) -> {
if (e instanceof LivingEntity) if (e instanceof LivingEntity)
{ {
@@ -934,20 +952,28 @@ public class EntityValue extends Value
} }
}); });
put("silent",(e,v)->{ put("silent",(e,v)-> e.setSilent(v.getBoolean()));
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)->{ put("exhaustion", (e, v)-> {
e.setNoGravity(!v.getBoolean()); if(e instanceof PlayerEntity) ((HungerManagerInterface) ((PlayerEntity) e).getHungerManager()).setExhaustionCM(NumericValue.asNumber(v).getFloat());
}); });
put("invulnerable",(e,v)->{ put("add_exhaustion", (e, v)-> {
e.setInvulnerable(v.getBoolean()); if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong());
}); });
put("fire",(e,v)->{ put("saturation", (e, v)-> {
e.setFireTicks((int)NumericValue.asNumber(v).getLong()); if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong());
}); });
// gamemode [check] // gamemode [check]
@@ -27,6 +27,7 @@ import net.minecraft.nbt.StringNbtReader;
import net.minecraft.nbt.StringTag; import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -35,6 +36,7 @@ import net.minecraft.util.math.Box;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@@ -201,16 +203,30 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
return null; return null;
return new InventoryLocator(pos, pos, inv, offset + 1); 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); String strVal = v1.getString().toLowerCase(Locale.ROOT);
if (!(v2 instanceof EntityValue) || !(((EntityValue) v2).getEntity() instanceof PlayerEntity)) 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( BlockPos pos = new BlockPos(
NumericValue.asNumber(v1).getDouble(), NumericValue.asNumber(v1).getDouble(),
@@ -13,6 +13,13 @@ public class NumericValue extends Value
private Double value; private Double value;
final static double epsilon = 1024*Double.MIN_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) public static NumericValue asNumber(Value v1)
{ {
if (!(v1 instanceof NumericValue)) if (!(v1 instanceof NumericValue))
@@ -49,6 +49,11 @@ __command() ->
print(' "/camera show": Show current path particles'); print(' "/camera show": Show current path particles');
print(' color of particles used is different for different players'); print(' color of particles used is different for different players');
print(' "/camera hide": Hide path display'); 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(''); print('');
'' ''
); );
@@ -483,6 +488,11 @@ hide() ->
); );
// runs the player on the path // 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() -> play() ->
( (
if (err = __is_not_valid_for_motion(), exit(err)); if (err = __is_not_valid_for_motion(), exit(err));
@@ -501,6 +511,7 @@ play() ->
global_playing_path = true; global_playing_path = true;
mspt = 1000 / 60; mspt = 1000 / 60;
start_time = time(); start_time = time();
very_start = start_time;
point = 0; point = 0;
p = __get_player(); p = __get_player();
try ( try (
@@ -514,9 +525,14 @@ play() ->
modify(p, 'location', v); modify(p, 'location', v);
point += 1; point += 1;
end_time = time(); end_time = time();
took = end_time - start_time; if (global_prefer_sync,
if (took < mspt, sleep(mspt-took)); should_be = very_start + mspt*point;
start_time = time() if (end_time < should_be, sleep(should_be-end_time) )
,
took = end_time - start_time;
if (took < mspt, sleep(mspt-took));
start_time = time()
)
) )
); );
); );
@@ -0,0 +1,179 @@
global_chunk_colors = m(
// loaded ticking
l(3, l(0xAAdd0000, 0x66990000)), // lime, green
l(2, l(0xFF000000, 0x88000000)), // red
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(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(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
l('empty', l(0xFF888800, 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(0xFF55FF00, 0xff99ff00)) // pink purple
);
// 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);
);
__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 = l(null, 0);
)
);
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');
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';
source_center = setup:'source_center';
radius = setup:'radius';
source_dimension = setup:'source_dimension';
shapes = l();
now = tick_time();
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 (loaded_status > 0,
tickets = in_dimension( source_dimension, chunk_tickets(source_pos));
for(tickets,
l(type, level) = _;
if (show_activity || type != 'unknown',
status = type;
);
);
);
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(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, '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, 'snap', 'xz');
);
);
);
);
draw_shape(shapes);
);
schedule(1, '__chunk_visualizer_tick', p)
)
@@ -98,7 +98,7 @@ __structure_renderer(player_name) ->
l(from, to) = structure_data; l(from, to) = structure_data;
total_size = _euclidean(from, to); total_size = _euclidean(from, to);
density = max(10, total_size/10); 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'); 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) = _; for (structure_pieces, l(piece, direction, from, to) = _;
r = 255 - floor(128 * _i/config:'max_pieces'); r = 255 - floor(128 * _i/config:'max_pieces');
@@ -137,12 +137,14 @@ __chunk_renderer(player_name) ->
b = 30; b = 30;
a = max(0, 255-player_distance); a = max(0, 255-player_distance);
color = a+256*(b+256*(g+256*r)); color = a+256*(b+256*(g+256*r));
draw_shape('line', 15, 'from', top_00, 'to', top_10, 'color', color, 'width', 3); draw_shape(l(
draw_shape('line', 15, 'from', top_10, 'to', top_11, 'color', color, 'width', 3); l('line', 15, 'from', top_00, 'to', top_10, 'color', color, 'line', 3),
draw_shape('line', 15, 'from', top_11, 'to', top_01, 'color', color, 'width', 3); l('line', 15, 'from', top_10, 'to', top_11, 'color', color, 'line', 3),
draw_shape('line', 15, 'from', top_01, 'to', top_00, 'color', color, 'width', 3); l('line', 15, 'from', top_11, 'to', top_01, 'color', color, 'line', 3),
draw_shape('line', 15, 'from', top_00, 'to', top_11, 'color', color, 'width', 3); l('line', 15, 'from', top_01, 'to', top_00, 'color', color, 'line', 3),
draw_shape('line', 15, 'from', top_01, 'to', top_10, 'color', color, 'width', 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)
));
) )
) )
) )
+3 -2
View File
@@ -158,7 +158,7 @@
"StatType_scarpetMixin", "StatType_scarpetMixin",
"PointOfInterest_scarpetMixin", "PointOfInterest_scarpetMixin",
"ServerWorld_scarpetEventMixin", "ServerWorld_scarpetEventMixin",
"HungerManagerMixin_scarpetEventsMixin",
"PistonBlock_movableTEMixin", "PistonBlock_movableTEMixin",
"PistonBlockEntity_movableTEMixin", "PistonBlockEntity_movableTEMixin",
@@ -186,7 +186,8 @@
"WorldRenderer_fogOffMixin", "WorldRenderer_fogOffMixin",
"WorldRenderer_creativeNoClipMixin", "WorldRenderer_creativeNoClipMixin",
"ClientPlayNetworkHandlerMixin", "ClientPlayNetworkHandlerMixin",
"DebugRenderer_scarpetRenderMixin" "DebugRenderer_scarpetRenderMixin",
"WorldRenderer_scarpetRenderMixin"
], ],
"server": [ "server": [
"MinecraftDedicatedServerMixin" "MinecraftDedicatedServerMixin"