Merge branch 'master' into 1.17

# Conflicts:
#	src/main/java/carpet/script/value/NBTSerializableValue.java
This commit is contained in:
gnembon committed 2020-11-26 03:03:03 -04:00
commit f4f289b0ec
14 files changed
+830 -188

No files matched your search

+111 -18
View File
@@ -1913,6 +1913,11 @@ Here is a list of built-in types, with their return value formats, as well as a
* `'players'`: returning a list of valid player name string, logged in or not. If configured with `'single'` returns only one player or `null`.
* `'intrange'`: same as `'floatrange'`, but requiring integers.
* `'enchantment'`: name of an enchantment
* `'slot'`: provides a list of inventory type and slot. Can be configured with `'restrict'` requiring
`'player'`, `'enderchest'`, `'equipment'`, `'armor'`, `'weapon'`, `'container'`, `'villager'` or `'horse'` restricting selection of
available slots. Scarpet supports all vanilla slots, except for `horse.chest` - chest item, not items themselves. This you would
need to manage yourself via nbt directly. Also, for entities that change their capacity, like llamas, you need to check yourself if
the specified container slot is valid for your entity.
* `'item'`: triple of item type, count of 1, and nbt.
* `'message'`: text with expanded embedded player names
* `'effect'`: string representing a status effect
@@ -3586,29 +3591,30 @@ health back to the villager after being harmed.
## Manipulating inventories of blocks and entities
Most functions in this category require inventory as the first argument. Inventory could be specified by an entity,
or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory require
two arguments, keyword `'enderchest'`, followed by the player entity argument, 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
or a block, or position (three coordinates) of a potential block with inventory, or can be preceded with inventory
type.
Inventory type can be `null` (default), `'enderchest'` denoting player enderchest storage, or `'equipment'` applying to
entities hand and armour pieces. Then the type can be followed by entity, block or position coordinates.
For instance, player enderchest inventory requires
two arguments, keyword `'enderchest'`, followed by the player entity argument, (or a single argument as a string of a
form: `'enderchest_steve'` for legacy support). If your player name starts with `'enderchest_'`, first of all, tough luck,
but then 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.
a block inventory. Player inventories can also be called by their name.
You can also use `'equipment'` as a keyword preceeding an entity indicating the need of access that entity as a mob.
Equipment inventory
consists of its armour and hand items, and a few living entites can have both, their regular inventory, and their equipment inventory. For some
entities (players) their regular inventory already contains the equipment. For entity types that only have
their equipment inventory, the equipment is returned by default.
A few living entities can have both: their regular inventory, and their equipment inventory.
Player's regular inventory already contains the equipment, but you can access the equipment part as well, as well as
their enderchest separately. For entity types that only have
their equipment inventory, the equipment is returned by default (`null` type).
If that's confusing see examples under `inventory_size` on how to access inventories. All other `inventory_...()` functions
use the same scheme.
<pre>
inventory_size(player()) => 41 // items
inventory_size('enderchest', player()) => 27 // equipment
inventory_size('equipment', player()) => 6 // equipment
</pre>
If the entity or a block doesn't have
an inventory, they typically do nothing and return null.
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.
Manipulating of the nbt data can be costly, but retrieving them from the tuple to match other aspects is cheap
### `stack_limit(item)`
@@ -3681,14 +3687,26 @@ item name that serves as a replacement after crafting is done. Currently it can
### `inventory_size(inventory)`
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
have an inventory
have an inventory.
<pre>
inventory_size(player()) => 41
inventory_size('enderchest', player()) => 27 // enderchest
inventory_size('equipment', player()) => 6 // equipment
inventory_size(null, player()) => 41 // default inventory for players
inventory_size(x,y,z) => 27 // chest
inventory_size(block(pos)) => 5 // hopper
horse = spawn('horse', x, y, z);
inventory_size(horse); => 2 // default horse inventory
inventory_size('equipment', horse); => 6 // unused horse equipment inventory
inventory_size(null, horse); => 2 // default horse
creeper = spawn('creeper', x, y, z);
inventory_size(creeper); => 6 // default creeper inventory is equipment since it has no other
inventory_size('equipment', creeper); => 6 // unused horse equipment inventory
inventory_size(null, creeper); => 6 // creeper default is its equipment
</pre>
### `inventory_has_items(inventory)`
@@ -4157,8 +4175,83 @@ for the objective.
### `scoreboard_display(place, objective)`
sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
Sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
returns `null`.
# Team
### `team_list()`, `team_list(team)`
Returns all available teams as a list with no arguments.
When a `team` is specified, it returns all the players inside that team. If the `team` is invalid, returns `null`.
### `team_add(team)`, `team_add(team,player)`
With one argument, creates a new `team` and returns its name if successful, or `null` if team already exists.
`team_add('admin')` -> Create a team with the name 'admin'
`team_add('admin','Steve')` -> Joing the player 'Steve' into the team 'admin'
If a `player` is specified, the player will join the given `team`. Returns `true` if player joined the team, or `false` if nothing changed since the player was already in this team. If the team is invalid, returns `null`
### `team_remove(team)`
Removes a `team`. Returns `true` if the team was deleted, or `null` if the team is invalid.
### `team_leave(player)`
Removes the `player` from the team he is in. Returns `true` if the player left a team, otherwise `false`.
`team_leave('Steve')` -> Removes Steve from the team he is currently in
`for(team_list('admin'), team_leave('admin', _))` -> Remove all players from team 'admin'
### `team_property(team,property,value?)`
Reads the `property` of the `team` if no `value` is specified. If a `value` is added as a third argument, it sets the `property` to that `value`.
* `collisionRule`
* Type: String
* Options: always, never, pushOtherTeams, pushOwnTeam
* `color`
* Type: String
* Options: See [team command](https://minecraft.gamepedia.com/Commands/team#Arguments) (same strings as `'teamcolor'` [command argument](https://github.com/gnembon/fabric-carpet/blob/master/docs/scarpet/Full.md#command-argument-types] options))
* `displayName`
* Type: String or FormattedText, when querying returns FormattedText
* `prefix`
* Type: String or FormattedText, when querying returns FormattedText
* `suffix`
* Type: String or FormattedText, when querying returns FormattedText
* `friendlyFire`
* Type: boolean
* `seeFriendlyInvisibles`
* Type: boolean
* `nametagVisibility`
* Type: String
* Options: always, never, hideForOtherTeams, hideForOwnTeam
* `deathMessageVisibility`
* Type: String
* Options: always, never, hideForOtherTeams, hideForOwnTeam
Examples:
```
team_property('admin','color','dark_red') Make the team color for team 'admin' dark red
team_property('admin','prefix',format('r Admin | ')) Set prefix of all players in 'admin'
team_property('admin','display_name','Administrators') Set display name for team 'admin'
team_property('admin','seeFriendlyInvisibles',true) Make all players in 'admin' see other admins even when invisible
team_property('admin','deathMessageVisibility','hideForOtherTeams') Make all players in 'admin' see other admins even when invisible
```
# Auxiliary aspects
Collection of other methods that control smaller, yet still important aspects of the game
+30 -17
View File
@@ -3,29 +3,30 @@
## Manipulating inventories of blocks and entities
Most functions in this category require inventory as the first argument. Inventory could be specified by an entity,
or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory require
two arguments, keyword `'enderchest'`, followed by the player entity argument, 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
or a block, or position (three coordinates) of a potential block with inventory, or can be preceded with inventory
type.
Inventory type can be `null` (default), `'enderchest'` denoting player enderchest storage, or `'equipment'` applying to
entities hand and armour pieces. Then the type can be followed by entity, block or position coordinates.
For instance, player enderchest inventory requires
two arguments, keyword `'enderchest'`, followed by the player entity argument, (or a single argument as a string of a
form: `'enderchest_steve'` for legacy support). If your player name starts with `'enderchest_'`, first of all, tough luck,
but then 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.
a block inventory. Player inventories can also be called by their name.
You can also use `'equipment'` as a keyword preceeding an entity indicating the need of access that entity as a mob.
Equipment inventory
consists of its armour and hand items, and a few living entites can have both, their regular inventory, and their equipment inventory. For some
entities (players) their regular inventory already contains the equipment. For entity types that only have
their equipment inventory, the equipment is returned by default.
A few living entities can have both: their regular inventory, and their equipment inventory.
Player's regular inventory already contains the equipment, but you can access the equipment part as well, as well as
their enderchest separately. For entity types that only have
their equipment inventory, the equipment is returned by default (`null` type).
If that's confusing see examples under `inventory_size` on how to access inventories. All other `inventory_...()` functions
use the same scheme.
<pre>
inventory_size(player()) => 41 // items
inventory_size('enderchest', player()) => 27 // equipment
inventory_size('equipment', player()) => 6 // equipment
</pre>
If the entity or a block doesn't have
an inventory, they typically do nothing and return null.
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.
Manipulating of the nbt data can be costly, but retrieving them from the tuple to match other aspects is cheap
### `stack_limit(item)`
@@ -98,14 +99,26 @@ item name that serves as a replacement after crafting is done. Currently it can
### `inventory_size(inventory)`
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
have an inventory
have an inventory.
<pre>
inventory_size(player()) => 41
inventory_size('enderchest', player()) => 27 // enderchest
inventory_size('equipment', player()) => 6 // equipment
inventory_size(null, player()) => 41 // default inventory for players
inventory_size(x,y,z) => 27 // chest
inventory_size(block(pos)) => 5 // hopper
horse = spawn('horse', x, y, z);
inventory_size(horse); => 2 // default horse inventory
inventory_size('equipment', horse); => 6 // unused horse equipment inventory
inventory_size(null, horse); => 2 // default horse
creeper = spawn('creeper', x, y, z);
inventory_size(creeper); => 6 // default creeper inventory is equipment since it has no other
inventory_size('equipment', creeper); => 6 // unused horse equipment inventory
inventory_size(null, creeper); => 6 // creeper default is its equipment
</pre>
### `inventory_has_items(inventory)`
+5
View File
@@ -214,6 +214,11 @@ Here is a list of built-in types, with their return value formats, as well as a
* `'players'`: returning a list of valid player name string, logged in or not. If configured with `'single'` returns only one player or `null`.
* `'intrange'`: same as `'floatrange'`, but requiring integers.
* `'enchantment'`: name of an enchantment
* `'slot'`: provides a list of inventory type and slot. Can be configured with `'restrict'` requiring
`'player'`, `'enderchest'`, `'equipment'`, `'armor'`, `'weapon'`, `'container'`, `'villager'` or `'horse'` restricting selection of
available slots. Scarpet supports all vanilla slots, except for `horse.chest` - chest item, not items themselves. This you would
need to manage yourself via nbt directly. Also, for entities that change their capacity, like llamas, you need to check yourself if
the specified container slot is valid for your entity.
* `'item'`: triple of item type, count of 1, and nbt.
* `'message'`: text with expanded embedded player names
* `'effect'`: string representing a status effect
+76 -1
View File
@@ -27,5 +27,80 @@ for the objective.
### `scoreboard_display(place, objective)`
sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
Sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
returns `null`.
# Team
### `team_list()`, `team_list(team)`
Returns all available teams as a list with no arguments.
When a `team` is specified, it returns all the players inside that team. If the `team` is invalid, returns `null`.
### `team_add(team)`, `team_add(team,player)`
With one argument, creates a new `team` and returns its name if successful, or `null` if team already exists.
`team_add('admin')` -> Create a team with the name 'admin'
`team_add('admin','Steve')` -> Joing the player 'Steve' into the team 'admin'
If a `player` is specified, the player will join the given `team`. Returns `true` if player joined the team, or `false` if nothing changed since the player was already in this team. If the team is invalid, returns `null`
### `team_remove(team)`
Removes a `team`. Returns `true` if the team was deleted, or `null` if the team is invalid.
### `team_leave(player)`
Removes the `player` from the team he is in. Returns `true` if the player left a team, otherwise `false`.
`team_leave('Steve')` -> Removes Steve from the team he is currently in
`for(team_list('admin'), team_leave('admin', _))` -> Remove all players from team 'admin'
### `team_property(team,property,value?)`
Reads the `property` of the `team` if no `value` is specified. If a `value` is added as a third argument, it sets the `property` to that `value`.
* `collisionRule`
* Type: String
* Options: always, never, pushOtherTeams, pushOwnTeam
* `color`
* Type: String
* Options: See [team command](https://minecraft.gamepedia.com/Commands/team#Arguments) (same strings as `'teamcolor'` [command argument](https://github.com/gnembon/fabric-carpet/blob/master/docs/scarpet/Full.md#command-argument-types] options))
* `displayName`
* Type: String or FormattedText, when querying returns FormattedText
* `prefix`
* Type: String or FormattedText, when querying returns FormattedText
* `suffix`
* Type: String or FormattedText, when querying returns FormattedText
* `friendlyFire`
* Type: boolean
* `seeFriendlyInvisibles`
* Type: boolean
* `nametagVisibility`
* Type: String
* Options: always, never, hideForOtherTeams, hideForOwnTeam
* `deathMessageVisibility`
* Type: String
* Options: always, never, hideForOtherTeams, hideForOwnTeam
Examples:
```
team_property('admin','color','dark_red') Make the team color for team 'admin' dark red
team_property('admin','prefix',format('r Admin | ')) Set prefix of all players in 'admin'
team_property('admin','display_name','Administrators') Set display name for team 'admin'
team_property('admin','seeFriendlyInvisibles',true) Make all players in 'admin' see other admins even when invisible
team_property('admin','deathMessageVisibility','hideForOtherTeams') Make all players in 'admin' see other admins even when invisible
```
@@ -84,6 +84,11 @@ scoreboard
scoreboard_add
scoreboard_remove
scoreboard_display
team_add
team_list
team_remove
team_leave
team_property
see_sky
set
set_biome
@@ -60,6 +60,8 @@
scoreboard scoreboard_add scoreboard_remove scoreboard_display
team_add team_list team_remove team_leave team_property
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
@@ -60,6 +60,8 @@
scoreboard scoreboard_add scoreboard_remove scoreboard_display
team_add team_list team_remove team_leave team_property
player spawn entity_id entity_list entity_area entity_selector query modify entity_event
stack_limit item_category recipe_data crafting_remaining_item
@@ -9,14 +9,20 @@ import carpet.script.value.ListValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.FormattedTextValue;
import carpet.script.value.Value;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.ServerScoreboard;
import net.minecraft.scoreboard.ScoreboardCriterion;
import net.minecraft.scoreboard.ScoreboardObjective;
import net.minecraft.scoreboard.ScoreboardPlayerScore;
import net.minecraft.scoreboard.Team;
import net.minecraft.scoreboard.AbstractTeam;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.stream.Collectors;
@@ -147,5 +153,249 @@ public class Scoreboards {
scoreboard.setObjectiveSlot(slot, objective);
return (_c, _t) -> new NumericValue(slot);
});
expression.addLazyFunction("team_list", -1, (c, t, lv) ->
{
if(lv.size() > 1) throw new InternalExpressionException("'team_list' requires zero or one parameters");
CarpetContext cc = (CarpetContext)c;
ServerScoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
if(lv.size() == 0) {
Value ret = ListValue.wrap(scoreboard.getTeamNames().stream().map(StringValue::of).collect(Collectors.toList()));
return (_c, _t) -> ret;
} else if(lv.size() == 1) {
Team team = scoreboard.getTeam(lv.get(0).evalValue(c).getString());
if(team == null) return LazyValue.NULL;
Value ret = ListValue.wrap(team.getPlayerList().stream().map(StringValue::of).collect(Collectors.toList()));
return (_c, _t) -> ret;
} else {
return LazyValue.NULL;
}
});
expression.addLazyFunction("team_add", -1, (c, t, lv) ->
{
if(!(lv.size() < 3 && lv.size() > 0)) throw new InternalExpressionException("'team_add' requires one or two parameters");
CarpetContext cc = (CarpetContext)c;
ServerScoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
String teamName = lv.get(0).evalValue(c).getString();
if(lv.size() == 1) {
if(scoreboard.getTeam(teamName) == null) {
scoreboard.addTeam(teamName);
Value ret = new StringValue(teamName);
return (_c, _t) -> ret;
} else {
return LazyValue.NULL;
}
} else if(lv.size() == 2) {
Value playerVal = lv.get(1).evalValue(c);
String player = EntityValue.getPlayerNameByValue(playerVal);
if(player == null) return LazyValue.NULL;
Team team = scoreboard.getTeam(teamName);
if(team == null) return LazyValue.NULL;
if(team.isEqual(scoreboard.getPlayerTeam(player))) {
return LazyValue.FALSE;
} else {
scoreboard.addPlayerToTeam(player,scoreboard.getTeam(teamName));
return LazyValue.TRUE;
}
} else {
return LazyValue.NULL;
}
});
expression.addLazyFunction("team_remove", 1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerScoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
Value teamVal = lv.get(0).evalValue(c);
String team = teamVal.getString();
if(scoreboard.getTeam(team) != null) {
scoreboard.removeTeam(scoreboard.getTeam(team));
return LazyValue.TRUE;
} else {
return LazyValue.NULL;
}
});
expression.addLazyFunction("team_leave", 1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerScoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
Value playerVal = lv.get(0).evalValue(c);
String player = EntityValue.getPlayerNameByValue(playerVal);
if(player == null) return LazyValue.NULL;
Value ret = new NumericValue(scoreboard.clearPlayerTeam(player));
return (_c, _t) -> ret;
});
expression.addLazyFunction("team_property", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerScoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
if(lv.size() < 2 || lv.size() > 3) throw new InternalExpressionException("'team_property' requires two or three arguments");
Value teamVal = lv.get(0).evalValue(c);
Value propertyVal = lv.get(1).evalValue(c);
Value settingVal = null;
boolean modifying = false;
if(lv.size() == 3) {
modifying = true;
settingVal = lv.get(2).evalValue(c);
}
Team team = scoreboard.getTeam(teamVal.getString());
if(team == null) return LazyValue.NULL;
if(!(propertyVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the second argument");
switch (propertyVal.getString()) {
case "collision_rule":
if(!modifying) {
Value ret = new StringValue(team.getCollisionRule().name);
return (_c, _t) -> ret;
}
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
AbstractTeam.CollisionRule collisionRule = AbstractTeam.CollisionRule.getRule(settingVal.getString());
if(collisionRule == null) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
team.setCollisionRule(collisionRule);
break;
case "color":
if(!modifying) {
Value ret = new StringValue(team.getColor().getName());
return (_c, _t) -> ret;
}
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
Formatting color = Formatting.byName(settingVal.getString().toUpperCase());
if(color == null || !color.isColor()) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
team.setColor(color);
break;
case "death_message_visibility":
if(!modifying) {
Value ret = new StringValue(team.getDeathMessageVisibilityRule().name);
return (_c, _t) -> ret;
}
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
AbstractTeam.VisibilityRule deathMessageVisibility = AbstractTeam.VisibilityRule.getRule(settingVal.getString());
if(deathMessageVisibility == null) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
team.setDeathMessageVisibilityRule(deathMessageVisibility);
break;
case "display_name":
if(!modifying) {
Value ret = new FormattedTextValue(team.getDisplayName());
return (_c, _t) -> ret;
}
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
Text displayName;
if(settingVal instanceof FormattedTextValue) {
displayName = ((FormattedTextValue) settingVal).getText();
} else {
displayName = new LiteralText(settingVal.getString());
}
team.setDisplayName(displayName);
break;
case "friendly_fire":
if(!modifying) {
Value ret = new NumericValue(team.isFriendlyFireAllowed());
return (_c, _t) -> ret;
}
if(!(settingVal instanceof NumericValue)) throw new InternalExpressionException("'team_property' requires a boolean as the third argument for the property " + propertyVal.getString());
boolean friendlyFire = settingVal.getBoolean();
team.setFriendlyFireAllowed(friendlyFire);
break;
case "nametag_visibility":
if(!modifying) {
Value ret = new StringValue(team.getNameTagVisibilityRule().name);
return (_c, _t) -> ret;
}
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
AbstractTeam.VisibilityRule nametagVisibility = AbstractTeam.VisibilityRule.getRule(settingVal.getString());
if(nametagVisibility == null) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
team.setNameTagVisibilityRule(nametagVisibility);
break;
case "prefix":
if(!modifying) {
Value ret = new FormattedTextValue(team.getPrefix());
return (_c, _t) -> ret;
}
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property ' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
Text prefix;
if(settingVal instanceof FormattedTextValue) {
prefix = ((FormattedTextValue) settingVal).getText();
} else {
prefix = new LiteralText(settingVal.getString());
}
team.setPrefix(prefix);
break;
case "see_friendly_invisibles":
if(!modifying) {
Value ret = new NumericValue(team.shouldShowFriendlyInvisibles());
return (_c, _t) -> ret;
}
if(!(settingVal instanceof NumericValue)) throw new InternalExpressionException("'team_property' requires a boolean as the third argument for the property " + propertyVal.getString());
boolean seeFriendlyInvisibles = settingVal.getBoolean();
team.setShowFriendlyInvisibles(seeFriendlyInvisibles);
break;
case "suffix":
if(!modifying) {
Value ret = new FormattedTextValue(team.getSuffix());
return (_c, _t) -> ret;
}
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
Text suffix;
if(settingVal instanceof FormattedTextValue) {
suffix = ((FormattedTextValue) settingVal).getText();
} else {
suffix = new LiteralText(settingVal.getString());
}
team.setSuffix(suffix);
break;
default:
throw new InternalExpressionException("team property " + propertyVal.getString() + " is not a valid property");
}
return LazyValue.TRUE;
});
}
}
@@ -47,6 +47,7 @@ import net.minecraft.command.argument.EntitySummonArgumentType;
import net.minecraft.command.argument.GameProfileArgumentType;
import net.minecraft.command.argument.IdentifierArgumentType;
import net.minecraft.command.argument.ItemEnchantmentArgumentType;
import net.minecraft.command.argument.ItemSlotArgumentType;
import net.minecraft.command.argument.ItemStackArgumentType;
import net.minecraft.command.argument.MessageArgumentType;
import net.minecraft.command.argument.MobEffectArgumentType;
@@ -78,11 +79,14 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.registry.Registry;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -158,6 +162,7 @@ public abstract class CommandArgument
),
// item_predicate ?? //same as item but accepts tags, not sure right now
// slot // item_slot
new SlotArgument(),
new VanillaUnconfigurableArgument("item", ItemStackArgumentType::itemStack,
(c, p) -> ValueConversions.of(ItemStackArgumentType.getItemStackArgument(c, p).createStack(1, false)), false
),
@@ -849,6 +854,100 @@ public abstract class CommandArgument
return IntArgument::new;
}
}
private static class SlotArgument extends CommandArgument
{
private String restrict;
private static final Map<String, Pair<Set<Integer>, Set<String>>> RESTRICTED_CONTAINERS = new HashMap<String, Pair<Set<Integer>, Set<String>>>(){{
int i;
for (String source : Arrays.asList("player", "enderchest", "equipment", "armor", "weapon", "container", "villager", "horse"))
put(source, Pair.of(new HashSet<>(), new HashSet<>()));
for (i = 0; i < 41; i++) get("player").getLeft().add(i);
for(i = 0; i < 41; i++) get("player").getRight().add("container." + i);
for(i = 0; i < 9; i++) get("player").getRight().add("hotbar." + i);
for(i = 0; i < 27; i++) get("player").getRight().add("inventory." + i);
for (String place : Arrays.asList("weapon", "weapon.mainhand", "weapon.offhand"))
{
get("player").getRight().add(place);
get("equipment").getRight().add(place);
get("weapon").getRight().add(place);
}
for (String place : Arrays.asList("armor.feet","armor.legs", "armor.chest","armor.head"))
{
get("player").getRight().add(place);
get("equipment").getRight().add(place);
get("armor").getRight().add(place);
}
for (i = 0; i < 27; i++) get("enderchest").getLeft().add(200+i);
for(i = 0; i < 27; i++) get("enderchest").getRight().add("enderchest." + i);
for (i = 0; i < 6; i++) get("equipment").getLeft().add(98+i);
for (i = 0; i < 4; i++) get("armor").getLeft().add(100+i);
for (i = 0; i < 2; i++) get("weapon").getLeft().add(98+i);
for (i = 0; i < 54; i++) get("container").getLeft().add(i);
for(i = 0; i < 41; i++) get("container").getRight().add("container." + i);
for (i = 0; i < 8; i++) get("villager").getLeft().add(i);
for(i = 0; i < 8; i++) get("villager").getRight().add("villager." + i);
for (i = 0; i < 15; i++) get("horse").getLeft().add(500+i);
for(i = 0; i < 15; i++) get("horse").getRight().add("horse." + i);
get("horse").getLeft().add(400);
get("horse").getRight().add("horse.saddle");
get("horse").getLeft().add(401);
get("horse").getRight().add("horse.armor");
}};
protected SlotArgument()
{
super("slot", ItemSlotArgumentType.itemSlot().getExamples(), false);
}
@Override
protected ArgumentType<?> getArgumentType() throws CommandSyntaxException
{
return ItemSlotArgumentType.itemSlot();
}
@Override
protected Value getValueFromContext(CommandContext<ServerCommandSource> context, String param) throws CommandSyntaxException
{
int slot = ItemSlotArgumentType.getItemSlot(context, param);
if (restrict != null && !RESTRICTED_CONTAINERS.get(restrict).getLeft().contains(slot))
{
throw new SimpleCommandExceptionType(new LiteralText("Incorrect slot restricted to "+restrict)).create();
}
return ValueConversions.ofVanillaSlotResult(slot);
}
@Override
protected void configure(Map<String, Value> config, CarpetScriptHost host)
{
super.configure(config, host);
if (config.containsKey("restrict"))
{
restrict = config.get("restrict").getString().toLowerCase(Locale.ROOT);
needsMatching = true;
if (!RESTRICTED_CONTAINERS.containsKey(restrict))
throw new InternalExpressionException("Incorrect slot restriction: "+restrict);
}
}
@Override
protected Collection<String> getOptions(CommandContext<ServerCommandSource> context, CarpetScriptHost host) throws CommandSyntaxException
{
return restrict==null?super.getOptions(context, host):RESTRICTED_CONTAINERS.get(restrict).getRight();
}
@Override
protected Supplier<CommandArgument> factory()
{
return SlotArgument::new;
}
}
@FunctionalInterface
private interface ValueExtractor
@@ -0,0 +1,126 @@
package carpet.script.utils;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import java.util.Arrays;
import java.util.List;
public class EquipmentInventory implements Inventory
{
private static final List<EquipmentSlot> slotToSlot = Arrays.asList(
EquipmentSlot.MAINHAND,
EquipmentSlot.FEET, EquipmentSlot.LEGS, EquipmentSlot.CHEST, EquipmentSlot.HEAD,
EquipmentSlot.OFFHAND
);
LivingEntity mob;
public EquipmentInventory(LivingEntity mob)
{
this.mob = mob;
}
@Override
public int size()
{
return 6;
}
@Override
public boolean isEmpty()
{
for (EquipmentSlot slot: slotToSlot)
if (!mob.getEquippedStack(slot).isEmpty()) return false;
return true;
}
@Override
public ItemStack getStack(int slot)
{
EquipmentSlot slotSlot;
try
{
slotSlot = slotToSlot.get(slot);
}
catch (IndexOutOfBoundsException ignored)
{
//going out of the index should be really exceptional
return null;
}
return mob.getEquippedStack(slotSlot);
}
@Override
public ItemStack removeStack(int slot, int amount)
{
EquipmentSlot slotSlot;
try
{
slotSlot = slotToSlot.get(slot);
}
catch (IndexOutOfBoundsException ignored)
{
//going out of the index should be really exceptional
return null;
}
ItemStack stack = mob.getEquippedStack(slotSlot);
stack.decrement(amount);
return stack;
}
@Override
public ItemStack removeStack(int slot)
{
EquipmentSlot slotSlot;
try
{
slotSlot = slotToSlot.get(slot);
}
catch (IndexOutOfBoundsException ignored)
{
//going out of the index should be really exceptional
return null;
}
ItemStack previous = mob.getEquippedStack(slotSlot);
mob.equipStack(slotSlot, ItemStack.EMPTY);
return previous;
}
@Override
public void setStack(int slot, ItemStack stack)
{
EquipmentSlot slotSlot;
try
{
slotSlot = slotToSlot.get(slot);
}
catch (IndexOutOfBoundsException ignored)
{
//going out of the index should be really exceptional
return;
}
mob.equipStack(slotSlot, stack);
}
@Override
public void markDirty()
{
}
@Override
public boolean canPlayerUse(PlayerEntity player)
{
return false;
}
@Override
public void clear()
{
for (EquipmentSlot slot: slotToSlot)
mob.equipStack(slot, ItemStack.EMPTY);
}
}
@@ -1,96 +0,0 @@
package carpet.script.utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
public class LivingInventory implements Inventory
{
private static final Int2ObjectMap<EquipmentSlot> slotToSlot = new Int2ObjectOpenHashMap<EquipmentSlot>(){{
put(0, EquipmentSlot.MAINHAND);
put(1, EquipmentSlot.FEET);
put(2, EquipmentSlot.LEGS);
put(3, EquipmentSlot.CHEST);
put(4, EquipmentSlot.HEAD);
put(5, EquipmentSlot.OFFHAND);
}};
LivingEntity mob;
public LivingInventory(LivingEntity mob)
{
this.mob = mob;
}
@Override
public int size()
{
return 6;
}
@Override
public boolean isEmpty()
{
for (EquipmentSlot slot: EquipmentSlot.values())
if (!mob.getEquippedStack(slot).isEmpty()) return false;
return true;
}
@Override
public ItemStack getStack(int slot)
{
EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null);
if (slotSlot == null) return null;
return mob.getEquippedStack(slotToSlot.get(slot));
}
@Override
public ItemStack removeStack(int slot, int amount)
{
EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null);
if (slotSlot == null) return null;
ItemStack stack = mob.getEquippedStack(slotSlot);
stack.decrement(amount);
return stack;
}
@Override
public ItemStack removeStack(int slot)
{
EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null);
if (slotSlot == null) return null;
ItemStack previous = mob.getEquippedStack(slotSlot);
mob.equipStack(slotSlot, ItemStack.EMPTY);
return previous;
}
@Override
public void setStack(int slot, ItemStack stack)
{
EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null);
if (slotSlot == null) return;
mob.equipStack(slotSlot, stack);
}
@Override
public void markDirty()
{
}
@Override
public boolean canPlayerUse(PlayerEntity player)
{
return false;
}
@Override
public void clear()
{
for (EquipmentSlot slot: EquipmentSlot.values())
mob.equipStack(slot, ItemStack.EMPTY);
}
}
@@ -144,6 +144,28 @@ public class EntityValue extends Value
return player;
}
public static String getPlayerNameByValue(Value value)
{
String playerName = null;
if (value instanceof EntityValue)
{
Entity e = ((EntityValue) value).getEntity();
if (e instanceof ServerPlayerEntity)
{
playerName = e.getEntityName();
}
}
else if (value.isNull())
{
return null;
}
else
{
playerName = value.getString();
}
return playerName;
}
@Override
public String getString()
{
@@ -4,7 +4,7 @@ import carpet.fakes.InventoryBearerInterface;
import carpet.script.CarpetContext;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.utils.LivingInventory;
import carpet.script.utils.EquipmentInventory;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.Block;
@@ -175,47 +175,15 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
return inventory;
}
public static InventoryLocator locateInventory(CarpetContext c, List<LazyValue> params, int offset)
{
try
{
Inventory inv = null;
Value v1 = params.get(0 + offset).evalValue(c);
if (v1 instanceof EntityValue)
Value v1 = params.get(offset).evalValue(c);
if (v1.isNull())
{
Entity e = ((EntityValue) v1).getEntity();
if (e instanceof PlayerEntity) inv = ((PlayerEntity) e).getInventory();
else if (e instanceof Inventory) inv = (Inventory) e;
else if (e instanceof VillagerEntity) inv = ((VillagerEntity) e).getInventory();
else if (e instanceof InventoryBearerInterface) inv = ((InventoryBearerInterface)e).getCMInventory();
else if (e instanceof LivingEntity) inv = new LivingInventory((MobEntity) e);
if (inv == null)
return null;
return new InventoryLocator(e, e.getBlockPos(), inv, offset + 1);
}
else if (v1 instanceof BlockValue)
{
BlockPos pos = ((BlockValue) v1).getPos();
if (pos == null)
throw new InternalExpressionException("Block to access inventory needs to be positioned in the world");
inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset + 1);
}
else if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
BlockPos pos = new BlockPos(
NumericValue.asNumber(args.get(0)).getDouble(),
NumericValue.asNumber(args.get(1)).getDouble(),
NumericValue.asNumber(args.get(2)).getDouble());
inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset + 1);
offset ++;
v1 = params.get(offset).evalValue(c);
}
else if (v1 instanceof StringValue)
{
@@ -227,34 +195,67 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
if (player == null) throw new InternalExpressionException("enderchest inventory requires player argument");
return new InventoryLocator(player, player.getBlockPos(), player.getEnderChestInventory(), offset + 2, true);
}
else if (strVal.equals("equipment"))
if (strVal.equals("equipment"))
{
Value v2 = params.get(1 + offset).evalValue(c);
if (!(v2 instanceof EntityValue)) throw new InternalExpressionException("mob inventory requires a mob argument");
if (!(v2 instanceof EntityValue)) throw new InternalExpressionException("Equipment inventory requires a living entity argument");
Entity e = ((EntityValue) v2).getEntity();
if (!(e instanceof LivingEntity)) throw new InternalExpressionException("mob inventory requires a mob argument");
return new InventoryLocator(e, e.getBlockPos(), new LivingInventory((LivingEntity) e), offset + 2, false);
}
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.getInventory(),
offset + 1,
isEnder
);
if (!(e instanceof LivingEntity)) throw new InternalExpressionException("Equipment inventory requires a living entity argument");
return new InventoryLocator(e, e.getBlockPos(), new EquipmentInventory((LivingEntity) e), offset + 2);
}
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
);
}
if (v1 instanceof EntityValue)
{
Inventory inv = null;
Entity e = ((EntityValue) v1).getEntity();
if (e instanceof PlayerEntity) inv = ((PlayerEntity) e).getInventory();
else if (e instanceof Inventory) inv = (Inventory) e;
else if (e instanceof VillagerEntity) inv = ((VillagerEntity) e).getInventory();
else if (e instanceof InventoryBearerInterface) inv = ((InventoryBearerInterface)e).getCMInventory();
else if (e instanceof LivingEntity) return new InventoryLocator(e, e.getBlockPos(), new EquipmentInventory((MobEntity) e), offset+1);
if (inv == null)
return null;
return new InventoryLocator(e, e.getBlockPos(), inv, offset+1);
}
if (v1 instanceof BlockValue)
{
BlockPos pos = ((BlockValue) v1).getPos();
if (pos == null)
throw new InternalExpressionException("Block to access inventory needs to be positioned in the world");
Inventory inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset+1);
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
BlockPos pos = new BlockPos(
NumericValue.asNumber(args.get(0)).getDouble(),
NumericValue.asNumber(args.get(1)).getDouble(),
NumericValue.asNumber(args.get(2)).getDouble());
Inventory inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset+1);
}
BlockPos pos = new BlockPos(
NumericValue.asNumber(v1).getDouble(),
NumericValue.asNumber(params.get(1 + offset).evalValue(c)).getDouble(),
NumericValue.asNumber(params.get(2 + offset).evalValue(c)).getDouble());
inv = getInventoryAt(c.s.getWorld(), pos);
Inventory inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset + 3);
@@ -330,4 +330,49 @@ public class ValueConversions
}
throw new InternalExpressionException("Unknown property type: "+p.getName());
}
private static final Map<Integer, ListValue> slotIdsToSlotParams = new HashMap<Integer, ListValue>() {{
int n;
//covers blocks, player hotbar and inventory, and all default inventories
for(n = 0; n < 54; ++n) {
put(n, ListValue.of(Value.NULL, NumericValue.of(n)));
}
for(n = 0; n < 27; ++n) {
put(200+n, ListValue.of(StringValue.of("enderchest"), NumericValue.of(n)));
}
// villager
for(n = 0; n < 8; ++n) {
put(300+n, ListValue.of(Value.NULL, NumericValue.of(n)));
}
// horse, llamas, donkeys, etc.
// two first slots are for saddle and armour
for(n = 0; n < 15; ++n) {
put(500+n, ListValue.of(Value.NULL, NumericValue.of(n+2)));
}
Value equipment = StringValue.of("equipment");
// weapon main hand
put(98, ListValue.of(equipment, NumericValue.of(0)));
// offhand
put(99, ListValue.of(equipment, NumericValue.of(5)));
// feet, legs, chest, head
for(n = 0; n < 4; ++n) {
put(100+n, ListValue.of(equipment, NumericValue.of(n+1)));
}
//horse defaults saddle
put(400, ListValue.of(Value.NULL, NumericValue.of(0)));
// armor
put(401, ListValue.of(Value.NULL, NumericValue.of(1)));
// chest itself on the donkey is wierd - use NBT to alter that.
//hashMap.put("horse.chest", 499);
}};
public static Value ofVanillaSlotResult(int itemSlot)
{
Value ret = slotIdsToSlotParams.get(itemSlot);
if (ret == null) return ListValue.of(Value.NULL, NumericValue.of(itemSlot));
return ret;
}
}