diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index c21745b7..78b53a0d 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -1757,7 +1757,8 @@ Here is the gist of the Minecraft related functions. Otherwise the CarpetScript ## App structure The main delivery method for scarpet programs into the game is in the form of apps in `*.sc` files located in the world `scripts` -folder. In singleplayer, you can also save apps in `.minecraft/config/carpet/scripts` for them to be available in any world. +folder, flat. In singleplayer, you can also save apps in `.minecraft/config/carpet/scripts` for them to be available in any world, +and here you can actually organize them in folders. When loaded (via `/script load` command, etc.), the game will run the content of the app once, regardless of its scope (more about the app scopes below), without executing of any functions, unless called directly, and with the exception of the `__config()` function, if present, which will be executed once. Loading the app will also bind specific @@ -2919,7 +2920,9 @@ to the `regular` group. * `monster`, `creature`, `ambient`, `water_creature`, `water_ambient`, `misc` - another categorization of living entities based on their spawn group. Negative descriptor resolves to all living types that don't belong to that category. -* Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: +* All entity tags including those provided with datapacks. Built-in entity tags include: `skeletons`, `raiders`, +`beehive_inhabitors` (bee, duh), `arrows` and `impact_projectiles`. +* Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: `area_effect_cloud`, `armor_stand`, `arrow`, `bat`, `bee`, `blaze`, `boat`, `cat`, `cave_spider`, `chest_minecart`, `chicken`, `cod`, `command_block_minecart`, `cow`, `creeper`, `dolphin`, `donkey`, `dragon_fireball`, `drowned`, `egg`, `elder_guardian`, `end_crystal`, `ender_dragon`, `ender_pearl`, `enderman`, `endermite`, `evoker`, @@ -3076,13 +3079,21 @@ List of entities riding the entity. Entity that `e` rides. -### `query(e, 'tags')` +### `query(e, 'scoreboard_tags')`, `query(e, 'tags')`(deprecated) -List of entity's tags. +List of entity's scoreboard tags. -### `query(e, 'has_tag',tag)` +### `query(e, 'has_scoreboard_tag',tag)`, `query(e, 'has_tag',tag)`(deprecated) -Boolean, true if the entity is marked with `tag`. +Boolean, true if the entity is marked with a `tag` scoreboad tag. + +### `query(e, 'entity_tags')` + +List of entity tags assigned to the type this entity represents. + +### `query(e, 'has_entity_tag', tag)` + +Returns `true` if the entity matches that entity tag, `false` if it doesn't, and `null` if the tag is not valid. ### `query(e, 'is_burning')` @@ -3744,6 +3755,14 @@ an inventory, all API functions 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. +### item_list(tag?) + +With no arguments, returns a list of all items in the game. With an item tag provided, list items matching the tag, or `null` if tag is not valid. + +### item_tags(item, tag?) + +Returns list of tags the item belongs to, or, if tag is provided, `true` if an item maches the tag, `false` if it doesn't and `null` if that's not a valid tag + ### `stack_limit(item)` Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets), @@ -3940,6 +3959,13 @@ if it accepts required number of parameters. Global events will be handled once per app that is with `'global'` scope. With `player` scoped apps, each player instance is responsible independently from handling their events, so a global event may be executed multiple times for each player. +### `__on_server_starts()` +Event triggers after world is loaded and after all startup apps have started. It won't be triggered with `/reload`. + +### `__on_server_shuts_down()` +Event triggers when the server started the shutdown process, before `__on_close()` is executed. Unlike `__on_close()`, it doesn't +trigger with `/reload`. + ### `__on_tick()` Event triggers at the beginning of each tick, located in the overworld. You can use `in_dimension()` to access other dimensions from there. diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 8ecb1af0..0ebc7a59 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -52,7 +52,9 @@ to the `regular` group. * `monster`, `creature`, `ambient`, `water_creature`, `water_ambient`, `misc` - another categorization of living entities based on their spawn group. Negative descriptor resolves to all living types that don't belong to that category. -* Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: +* All entity tags including those provided with datapacks. Built-in entity tags include: `skeletons`, `raiders`, +`beehive_inhabitors` (bee, duh), `arrows` and `impact_projectiles`. +* Any of the following standard entity types (equivalent to selection from `/summon` vanilla command: `area_effect_cloud`, `armor_stand`, `arrow`, `bat`, `bee`, `blaze`, `boat`, `cat`, `cave_spider`, `chest_minecart`, `chicken`, `cod`, `command_block_minecart`, `cow`, `creeper`, `dolphin`, `donkey`, `dragon_fireball`, `drowned`, `egg`, `elder_guardian`, `end_crystal`, `ender_dragon`, `ender_pearl`, `enderman`, `endermite`, `evoker`, @@ -209,13 +211,21 @@ List of entities riding the entity. Entity that `e` rides. -### `query(e, 'tags')` +### `query(e, 'scoreboard_tags')`, `query(e, 'tags')`(deprecated) -List of entity's tags. +List of entity's scoreboard tags. -### `query(e, 'has_tag',tag)` +### `query(e, 'has_scoreboard_tag',tag)`, `query(e, 'has_tag',tag)`(deprecated) -Boolean, true if the entity is marked with `tag`. +Boolean, true if the entity is marked with a `tag` scoreboad tag. + +### `query(e, 'entity_tags')` + +List of entity tags assigned to the type this entity represents. + +### `query(e, 'has_entity_tag', tag)` + +Returns `true` if the entity matches that entity tag, `false` if it doesn't, and `null` if the tag is not valid. ### `query(e, 'is_burning')` diff --git a/docs/scarpet/api/Events.md b/docs/scarpet/api/Events.md index b337090d..18e3f2ba 100644 --- a/docs/scarpet/api/Events.md +++ b/docs/scarpet/api/Events.md @@ -32,6 +32,13 @@ if it accepts required number of parameters. Global events will be handled once per app that is with `'global'` scope. With `player` scoped apps, each player instance is responsible independently from handling their events, so a global event may be executed multiple times for each player. +### `__on_server_starts()` +Event triggers after world is loaded and after all startup apps have started. It won't be triggered with `/reload`. + +### `__on_server_shuts_down()` +Event triggers when the server started the shutdown process, before `__on_close()` is executed. Unlike `__on_close()`, it doesn't +trigger with `/reload`. + ### `__on_tick()` Event triggers at the beginning of each tick, located in the overworld. You can use `in_dimension()` to access other dimensions from there. diff --git a/docs/scarpet/api/Inventories.md b/docs/scarpet/api/Inventories.md index 1ecc692b..ebe2ed7b 100644 --- a/docs/scarpet/api/Inventories.md +++ b/docs/scarpet/api/Inventories.md @@ -28,6 +28,14 @@ an inventory, all API functions 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. +### item_list(tag?) + +With no arguments, returns a list of all items in the game. With an item tag provided, list items matching the tag, or `null` if tag is not valid. + +### item_tags(item, tag?) + +Returns list of tags the item belongs to, or, if tag is provided, `true` if an item maches the tag, `false` if it doesn't and `null` if that's not a valid tag + ### `stack_limit(item)` Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets), diff --git a/docs/scarpet/api/Overview.md b/docs/scarpet/api/Overview.md index 7c08b451..4f1da73c 100644 --- a/docs/scarpet/api/Overview.md +++ b/docs/scarpet/api/Overview.md @@ -6,7 +6,8 @@ Here is the gist of the Minecraft related functions. Otherwise the CarpetScript ## App structure The main delivery method for scarpet programs into the game is in the form of apps in `*.sc` files located in the world `scripts` -folder. In singleplayer, you can also save apps in `.minecraft/config/carpet/scripts` for them to be available in any world. +folder, flat. In singleplayer, you can also save apps in `.minecraft/config/carpet/scripts` for them to be available in any world, +and here you can actually organize them in folders. When loaded (via `/script load` command, etc.), the game will run the content of the app once, regardless of its scope (more about the app scopes below), without executing of any functions, unless called directly, and with the exception of the `__config()` function, if present, which will be executed once. Loading the app will also bind specific diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index e7f1f8f6..397c1760 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -52,6 +52,8 @@ inventory_remove inventory_set inventory_size item_category +item_list +item_tags last_tick_times light liquid diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 46f3a139..759e69d0 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -68,7 +68,7 @@ player spawn entity_id entity_list entity_area entity_selector query modify entity_event entity_load_handler entity_types - stack_limit item_category recipe_data crafting_remaining_item + item_list item_tags stack_limit item_category recipe_data crafting_remaining_item inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item diff --git a/src/main/java/carpet/CarpetServer.java b/src/main/java/carpet/CarpetServer.java index d069fe81..773b61e3 100644 --- a/src/main/java/carpet/CarpetServer.java +++ b/src/main/java/carpet/CarpetServer.java @@ -102,9 +102,9 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit public static void onServerLoadedWorlds(MinecraftServer minecraftServer) { + HopperCounter.resetAll(minecraftServer); extensions.forEach(e -> e.onServerLoadedWorlds(minecraftServer)); scriptServer.initializeForWorld(); - HopperCounter.resetAll(minecraftServer); } public static void tick(MinecraftServer server) @@ -169,9 +169,9 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit public static void onServerClosed(MinecraftServer server) { + if (scriptServer != null) scriptServer.onClose(); ServerNetworkHandler.close(); currentCommandDispatcher = null; - if (scriptServer != null) scriptServer.onClose(); LoggerRegistry.stopLoggers(); extensions.forEach(e -> e.onServerClosed(server)); diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java index 87706659..e675b952 100644 --- a/src/main/java/carpet/CarpetSettings.java +++ b/src/main/java/carpet/CarpetSettings.java @@ -431,7 +431,7 @@ public class CarpetSettings extra = "if /script is enabled", category = SCARPET ) - public static boolean scriptsAutoload = false; + public static boolean scriptsAutoload = true; @Rule(desc = "Enables /player command to control/spawn players", category = COMMAND) public static String commandPlayer = "ops"; diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index 04599e8d..11e89c03 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -309,6 +309,30 @@ public class CarpetEventServer return events; } + public static final Event START = new Event("server_starts", 0, true) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, () -> + CarpetServer.minecraft_server.getCommandSource(). + withWorld(CarpetServer.minecraft_server.getWorld(World.OVERWORLD)) + ); + } + }; + + public static final Event SHUTDOWN = new Event("server_shuts_down", 0, true) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, () -> + CarpetServer.minecraft_server.getCommandSource(). + withWorld(CarpetServer.minecraft_server.getWorld(World.OVERWORLD)) + ); + } + }; + public static final Event TICK = new Event("tick", 0, true) { @Override diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index d0b4e84e..4aeafe37 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -217,7 +217,7 @@ public class CarpetScriptHost extends ScriptHost if (!(ret instanceof MapValue)) return false; Map config = ((MapValue) ret).getMap(); setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player")); - persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.FALSE).getBoolean(); + persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.TRUE).getBoolean(); // read custom arguments Value arguments = config.get(StringValue.of("arguments")); if (arguments != null) diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index d7090999..41b2fc34 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -122,13 +122,12 @@ public class CarpetScriptServer }); if (CarpetSettings.scriptsAutoload) { - Messenger.m(server.getCommandSource(), "Auto-loading world scarpet apps"); for (String moduleName: listAvailableModules(false)) { addScriptHost(server.getCommandSource(), moduleName, null, true, true, false); } } - + CarpetEventServer.Event.START.onTick(); } public Module getModule(String name, boolean allowLibraries) @@ -149,7 +148,7 @@ public class CarpetScriptServer { Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts"); Files.createDirectories(globalFolder); - scriptPath = Files.list(globalFolder) + scriptPath = Files.walk(globalFolder) .filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") || (allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl"))) .findFirst(); @@ -194,7 +193,7 @@ public class CarpetScriptServer try { Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts"); Files.createDirectories(worldScripts); - Files.walk(worldScripts) + Files.list(worldScripts) .filter(f -> f.toString().endsWith(".sc")) .forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT))); @@ -490,6 +489,7 @@ public class CarpetScriptServer public void onClose() { + CarpetEventServer.Event.SHUTDOWN.onTick(); for (ScriptHost host : modules.values()) { host.onClose(); diff --git a/src/main/java/carpet/script/api/Entities.java b/src/main/java/carpet/script/api/Entities.java index ead7f75f..0c413e3a 100644 --- a/src/main/java/carpet/script/api/Entities.java +++ b/src/main/java/carpet/script/api/Entities.java @@ -23,6 +23,7 @@ import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.CompoundTag; import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; @@ -34,6 +35,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -183,8 +185,9 @@ public class Entities { expression.addLazyFunction("entity_list", 1, (c, t, lv) -> { String who = lv.get(0).evalValue(c).getString(); - EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who); - List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate); + ServerCommandSource source = ((CarpetContext)c).s; + EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, source.getMinecraftServer()); + List entityList = source.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate); Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList())); return (_c, _t ) -> retval; }); @@ -212,8 +215,8 @@ public class Entities { throw new InternalExpressionException("Range of 'entity_area' cannot come from a block argument"); Vec3d range = rangeLocator.vec; Box area = centerBox.expand(range.x, range.y, range.z); - EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who); - List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate); + EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, cc.s.getMinecraftServer()); + List entityList = cc.s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate); Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList())); return (_c, _t ) -> retval; }); @@ -238,7 +241,9 @@ public class Entities { Value v = lv.get(0).evalValue(c); if (!(v instanceof EntityValue)) throw new InternalExpressionException("First argument to query should be an entity"); - String what = lv.get(1).evalValue(c).getString(); + String what = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT); + if (what.equals("tags")) + c.host.issueDeprecation("'tags' for entity querying"); Value retval; if (lv.size()==2) retval = ((EntityValue) v).get(what, null); @@ -269,11 +274,13 @@ public class Entities { return (cc, tt) -> v; }); - expression.addFunction("entity_types", (lv) -> + expression.addLazyFunction("entity_types", -1, (c, t, lv) -> { + if (lv.size() > 1) throw new InternalExpressionException("'entity_types' requires one or no arguments"); - String desc = (lv.size() == 1)?lv.get(0).getString():"*"; - return EntityValue.getEntityDescriptor(desc).listValue; + String desc = (lv.size() == 1)?lv.get(0).evalValue(c).getString():"*"; + Value ret = EntityValue.getEntityDescriptor(desc, ((CarpetContext) c).s.getMinecraftServer()).listValue; + return (_c, _t) -> ret; }); expression.addLazyFunction("entity_load_handler", -1, (c, t, lv) -> @@ -285,7 +292,7 @@ public class Entities { ? ((ListValue) entityValue).getItems().stream().map(Value::getString).collect(Collectors.toList()) : Collections.singletonList(entityValue.getString()); Set> types = new HashSet<>(); - descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s).typeList)); + descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s, ((CarpetContext) c).s.getMinecraftServer()).typeList)); FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 1, true, false); CarpetEventServer events = ((CarpetScriptHost)c.host).getScriptServer().events; if (funArg.function == null) diff --git a/src/main/java/carpet/script/api/Inventories.java b/src/main/java/carpet/script/api/Inventories.java index b5f6bfb4..fb4983ae 100644 --- a/src/main/java/carpet/script/api/Inventories.java +++ b/src/main/java/carpet/script/api/Inventories.java @@ -12,6 +12,7 @@ import carpet.script.value.NullValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; +import carpet.script.value.ValueConversions; import com.google.common.collect.Sets; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.command.argument.ItemStackArgument; @@ -32,6 +33,7 @@ import net.minecraft.recipe.ShapedRecipe; import net.minecraft.recipe.ShapelessRecipe; import net.minecraft.recipe.SpecialCraftingRecipe; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.tag.TagManager; import net.minecraft.util.Identifier; import net.minecraft.util.InvalidIdentifierException; import net.minecraft.util.math.Vec3d; @@ -40,6 +42,7 @@ import net.minecraft.util.registry.Registry; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; public class Inventories { public static void apply(Expression expression) @@ -60,6 +63,43 @@ public class Inventories { return (_c, _t) -> res; }); + expression.addLazyFunction("item_list", -1, (c, t, lv) -> + { + if (lv.size() == 0) + { + Value res = ListValue.wrap(Registry.ITEM.getIds().stream().map(ValueConversions::of).collect(Collectors.toList())); + return (cc, tt) -> res; + } + CarpetContext cc = (CarpetContext)c; + TagManager tagManager = cc.s.getMinecraftServer().getTagManager(); + String tag = lv.get(0).evalValue(c).getString(); + net.minecraft.tag.Tag itemTag = tagManager.getItems().getTag(new Identifier(tag)); + if (itemTag == null) return LazyValue.NULL; + Value ret = ListValue.wrap(itemTag.values().stream().map(b -> ValueConversions.of(Registry.ITEM.getId(b))).collect(Collectors.toList())); + return (_c, _t) -> ret; + }); + + expression.addLazyFunction("item_tags", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext)c; + TagManager tagManager = cc.s.getMinecraftServer().getTagManager(); + if (lv.size() == 0) + { + Value ret = ListValue.wrap(tagManager.getItems().getTagIds().stream().map(ValueConversions::of).collect(Collectors.toList())); + return (_c, _t) -> ret; + } + Item item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString()).getItem(); + if (lv.size() == 1) + { + Value ret = ListValue.wrap(tagManager.getItems().getTagsFor(item).stream().map(ValueConversions::of).collect(Collectors.toList())); + return (_c, _t) -> ret; + } + String tag = lv.get(1).evalValue(c).getString(); + net.minecraft.tag.Tag itemTag = tagManager.getItems().getTag(new Identifier(tag)); + if (itemTag == null) return LazyValue.NULL; + return item.isIn(itemTag)?LazyValue.TRUE:LazyValue.FALSE; + }); + expression.addLazyFunction("recipe_data", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 0ab267f9..7796ba70 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -224,10 +224,28 @@ public class EntityValue extends Value return entity.hashCode(); } - public static EntityClassDescriptor getEntityDescriptor(String who) + public static EntityClassDescriptor getEntityDescriptor(String who, MinecraftServer server) { EntityClassDescriptor eDesc = EntityClassDescriptor.byName.get(who); - if (eDesc == null) throw new InternalExpressionException(who+" is not a valid entity descriptor"); + if (eDesc == null) + { + boolean positive = true; + if (who.startsWith("!")) + { + positive = false; + who = who.substring(1); + } + net.minecraft.tag.Tag> eTag = server.getTagManager().getEntityTypes().getTag(new Identifier(who)); + if (eTag == null) throw new InternalExpressionException(who+" is not a valid entity descriptor"); + if (positive) + { + return new EntityClassDescriptor(null, e -> eTag.contains(e.getType()) && e.isAlive(), eTag.values().stream()); + } + else + { + return new EntityClassDescriptor(null, e -> !eTag.contains(e.getType()) && e.isAlive(), Registry.ENTITY_TYPE.stream().filter(et -> !eTag.contains(et))); + } + } return eDesc; //TODO add more here like search by tags, or type //if (who.startsWith('tag:')) @@ -416,8 +434,21 @@ public class EntityValue extends Value put("passengers", (e, a) -> ListValue.wrap(e.getPassengerList().stream().map(EntityValue::new).collect(Collectors.toList()))); put("mount", (e, a) -> (e.getVehicle()!=null)?new EntityValue(e.getVehicle()):Value.NULL); put("unmountable", (e, a) -> new NumericValue(((EntityInterface)e).isPermanentVehicle())); + // deprecated put("tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList()))); + + put("scoreboard_tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList()))); + put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTagsFor(e.getType()).stream().map(ValueConversions::of).collect(Collectors.toList()))); + // deprecated put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString()))); + + put("has_scoreboard_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString()))); + put("has_entity_tag", (e, a) -> { + net.minecraft.tag.Tag> tag = e.getServer().getTagManager().getEntityTypes().getTag(new Identifier(a.getString())); + if (tag == null) return Value.NULL; + return new NumericValue(e.getType().isIn(tag)); + }); + put("yaw", (e, a)-> new NumericValue(e.yaw)); put("head_yaw", (e, a)-> { if (e instanceof LivingEntity) diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 48fa8b71..2f6e41bd 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -267,7 +267,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } } - private static Map itemCache = new HashMap<>(); + private static final Map itemCache = new HashMap<>(); public static ItemStackArgument parseItem(String itemString) {