diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index db56482f..3a8c27d4 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -65,7 +65,8 @@ 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. +sending many small packets to draw several shapes at once. The drawback of sending shapes is batches is that they need to address +the same list of players, i.e. if multiple players from the list target different players, all shapes will be sent to all of them. 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: @@ -78,8 +79,8 @@ or simply refresh the shapes periodically in more dynamic applications. Optional shared shape attributes: * `color` - integer value indicating the main color of the shape in the form of red, green, blue and alpha components in the form of `0xRRGGBBAA`, with the default of `-1`, so white opaque, or `0xFFFFFFFF`. - * `player` - name or player entity to send the shape to. If specified, the shapes will appear only for the specified - player, otherwise it will be send to all players in the dimension. + * `player` - name or player entity to send the shape to, or a list of players. If specified, the shapes will appear only for the specified + players (regardless where they are), otherwise it will be send to all players in the current dimension. * `line` - (Deprecated) line thickness, defaults to 2.0pt. Not supported in 1.17's 3.2 core GL renderer. * `fill` - color for the faces, defaults to no fill. Use `color` attribute format * `follow` - entity, or player name. Shape will follow an entity instead of being static. diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index 753cd7e3..96adae02 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -121,9 +121,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; @@ -331,7 +333,7 @@ public class Auxiliary { CarpetContext cc = (CarpetContext)c; ServerWorld world = cc.s.getWorld(); MinecraftServer server = world.getServer(); - ServerPlayerEntity player[] = {null}; + Set playerTargets = new HashSet<>(); List>> shapes = new ArrayList<>(); if (lv.size() == 1) // bulk { @@ -340,16 +342,16 @@ public class Auxiliary { for (Value list : ((ListValue) specLoad).getItems()) { 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(server, world, ((ListValue) list).getItems(), player)); + shapes.add( ShapeDispatcher.fromFunctionArgs(server, world, ((ListValue) list).getItems(), playerTargets)); } } else { - shapes.add(ShapeDispatcher.fromFunctionArgs(server, world, lv, player)); + shapes.add(ShapeDispatcher.fromFunctionArgs(server, world, lv, playerTargets)); } ShapeDispatcher.sendShape( - (player[0]==null)?cc.s.getWorld().getPlayers():Collections.singletonList(player[0]), + (playerTargets.isEmpty())?cc.s.getWorld().getPlayers():playerTargets, shapes ); return Value.TRUE; diff --git a/src/main/java/carpet/script/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java index 2a8b21e7..ce242978 100644 --- a/src/main/java/carpet/script/utils/ShapeDispatcher.java +++ b/src/main/java/carpet/script/utils/ShapeDispatcher.java @@ -47,6 +47,7 @@ import net.minecraft.util.registry.RegistryKey; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -65,7 +66,7 @@ public class ShapeDispatcher public static Pair> fromFunctionArgs( MinecraftServer server, ServerWorld world, List lv, - ServerPlayerEntity[] playerRef + Set playerSet ) { if (lv.size() < 3) throw new InternalExpressionException("'draw_shape' takes at least three parameters, shape name, duration, and its params"); @@ -97,16 +98,25 @@ public class ShapeDispatcher if (params.containsKey("player")) { - ServerPlayerEntity player = EntityValue.getPlayerByValue(server, params.get("player")); - if (player == null) - throw new InternalExpressionException("'player' parameter needs to represent an existing player"); + Value players = params.get("player"); + List playerVals; + if (players instanceof ListValue) + playerVals = ((ListValue) players).getItems(); + else + playerVals = Collections.singletonList(players); + for (Value pVal : playerVals) + { + ServerPlayerEntity player = EntityValue.getPlayerByValue(server, pVal); + if (player == null) + throw new InternalExpressionException("'player' parameter needs to represent an existing player, not "+pVal.getString()); + playerSet.add(player); + } params.remove("player"); - playerRef[0] = player; } return Pair.of(ShapeDispatcher.create(server, shapeType, params), params); } - public static void sendShape(List players, List>> shapes) + public static void sendShape(Collection players, List>> shapes) { List clientPlayers = new ArrayList<>(); List alternativePlayers = new ArrayList<>(); diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index c5b72db7..0867c799 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -103,14 +103,14 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf } public static ListValue of(Value ... list) { - return ListValue.wrap(Arrays.asList(list)); + return new ListValue(new ArrayList<>(Arrays.asList(list))); } public static ListValue ofNums(Number ... list) { List valList = new ArrayList<>(); for (Number i : list) valList.add(new NumericValue(i.doubleValue())); - return ListValue.wrap(valList); + return new ListValue(valList); } public static LazyValue lazyEmpty() @@ -342,11 +342,11 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf int size = items.size(); int from = normalizeIndex(fromDesc, size); if (toDesc == null) - return new ListValue((Collection) getItems().subList(from, size)); + return new ListValue(new ArrayList<>(getItems().subList(from, size))); int to = normalizeIndex(toDesc, size+1); if (from > to) return ListValue.of(); - return new ListValue((Collection) getItems().subList(from, to)); + return new ListValue(new ArrayList<>(getItems().subList(from, to))); } @Override @@ -364,11 +364,11 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf index++; if (val.equals(delimiter)) { - result.items.add(ListValue.wrap(this.items.subList(startIndex, index-1))); + result.items.add(new ListValue(new ArrayList<>(this.items.subList(startIndex, index-1)))); startIndex = index; } } - result.items.add(ListValue.wrap(this.items.subList(startIndex, length()))); + result.items.add(new ListValue(new ArrayList<>(this.items.subList(startIndex, length())))); return result; }