Merge branch 'master' into 1.17

# Conflicts:
#	src/main/java/carpet/script/api/Entities.java
This commit is contained in:
gnembon committed 2020-12-07 01:14:12 -04:00
commit 02a25c0c99
16 files changed
+189 -33

No files matched your search

+32 -6
View File
@@ -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.
+15 -5
View File
@@ -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')`
+7
View File
@@ -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.
+8
View File
@@ -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),
+2 -1
View File
@@ -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
@@ -52,6 +52,8 @@ inventory_remove
inventory_set
inventory_size
item_category
item_list
item_tags
last_tick_times
light
liquid
@@ -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
+2 -2
View File
@@ -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));
+1 -1
View File
@@ -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";
@@ -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
@@ -217,7 +217,7 @@ public class CarpetScriptHost extends ScriptHost
if (!(ret instanceof MapValue)) return false;
Map<Value, Value> 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)
@@ -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();
+16 -9
View File
@@ -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<? extends Entity> entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate);
ServerCommandSource source = ((CarpetContext)c).s;
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, source.getMinecraftServer());
List<? extends Entity> 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<? extends Entity> entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate);
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, cc.s.getMinecraftServer());
List<? extends Entity> 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<EntityType<? extends Entity>> 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<LazyValue> funArg = FunctionArgument.findIn(c, expression.module, lv, 1, true, false);
CarpetEventServer events = ((CarpetScriptHost)c.host).getScriptServer().events;
if (funArg.function == null)
@@ -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<Item> 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<Item> 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;
@@ -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<EntityType<?>> 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<EntityType<?>> 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)
@@ -267,7 +267,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
}
}
private static Map<String,ItemStackArgument> itemCache = new HashMap<>();
private static final Map<String,ItemStackArgument> itemCache = new HashMap<>();
public static ItemStackArgument parseItem(String itemString)
{