Merge pull request #42 from gnembon/inventory
Inventory in scarpet, primarily
This commit is contained in:
15 files changed
+1043
-106
No files matched your search
@@ -5,6 +5,7 @@ build/
|
|||||||
out/
|
out/
|
||||||
classes/
|
classes/
|
||||||
publish/
|
publish/
|
||||||
|
docs/
|
||||||
|
|
||||||
# idea
|
# idea
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import com.mojang.brigadier.CommandDispatcher;
|
|||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.pattern.CachedBlockPosition;
|
import net.minecraft.block.pattern.CachedBlockPosition;
|
||||||
import net.minecraft.command.arguments.BlockPosArgumentType;
|
import net.minecraft.command.arguments.BlockPosArgumentType;
|
||||||
@@ -31,6 +33,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -40,6 +43,29 @@ import static net.minecraft.server.command.CommandSource.suggestMatching;
|
|||||||
|
|
||||||
public class ScriptCommand
|
public class ScriptCommand
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private static CompletableFuture<Suggestions> suggestCode(
|
||||||
|
CommandContext<ServerCommandSource> context,
|
||||||
|
SuggestionsBuilder suggestionsBuilder
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ScriptHost currentHost = getHost(context);
|
||||||
|
String previous = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||||
|
int strlen = previous.length();
|
||||||
|
StringBuilder lastToken = new StringBuilder();
|
||||||
|
for (int idx = strlen-1; idx >=0; idx--)
|
||||||
|
{
|
||||||
|
char ch = previous.charAt(idx);
|
||||||
|
if (Character.isLetterOrDigit(ch) || ch == '_') lastToken.append(ch); else break;
|
||||||
|
}
|
||||||
|
if (lastToken.length() == 0)
|
||||||
|
return suggestionsBuilder.buildFuture();
|
||||||
|
String prefix = lastToken.reverse().toString();
|
||||||
|
String previousString = previous.substring(0,previous.length()-prefix.length()) ;
|
||||||
|
ExpressionInspector.suggestFunctions(currentHost, previousString, prefix).forEach(text -> suggestionsBuilder.suggest(previousString+text));
|
||||||
|
return suggestionsBuilder.buildFuture();
|
||||||
|
}
|
||||||
|
|
||||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||||
{
|
{
|
||||||
LiteralArgumentBuilder<ServerCommandSource> b = literal("globals").
|
LiteralArgumentBuilder<ServerCommandSource> b = literal("globals").
|
||||||
@@ -50,7 +76,7 @@ public class ScriptCommand
|
|||||||
executes( (cc) -> { CarpetServer.scriptServer.stopAll = false; return 1;});
|
executes( (cc) -> { CarpetServer.scriptServer.stopAll = false; return 1;});
|
||||||
LiteralArgumentBuilder<ServerCommandSource> l = literal("run").
|
LiteralArgumentBuilder<ServerCommandSource> l = literal("run").
|
||||||
requires((player) -> player.hasPermissionLevel(2)).
|
requires((player) -> player.hasPermissionLevel(2)).
|
||||||
then(argument("expr", StringArgumentType.greedyString()).
|
then(argument("expr", StringArgumentType.greedyString()).suggests((cc, bb) -> suggestCode(cc, bb)).
|
||||||
executes((cc) -> compute(
|
executes((cc) -> compute(
|
||||||
cc,
|
cc,
|
||||||
StringArgumentType.getString(cc, "expr"))));
|
StringArgumentType.getString(cc, "expr"))));
|
||||||
@@ -113,6 +139,7 @@ public class ScriptCommand
|
|||||||
then(argument("from", BlockPosArgumentType.create()).
|
then(argument("from", BlockPosArgumentType.create()).
|
||||||
then(argument("to", BlockPosArgumentType.create()).
|
then(argument("to", BlockPosArgumentType.create()).
|
||||||
then(argument("expr", StringArgumentType.greedyString()).
|
then(argument("expr", StringArgumentType.greedyString()).
|
||||||
|
suggests((cc, bb) -> suggestCode(cc, bb)).
|
||||||
executes( (cc) -> scriptScan(
|
executes( (cc) -> scriptScan(
|
||||||
cc,
|
cc,
|
||||||
BlockPosArgumentType.getBlockPos(cc, "origin"),
|
BlockPosArgumentType.getBlockPos(cc, "origin"),
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public abstract class ServerChunkManagerMixin
|
|||||||
SpawnReporter.spawn_cap_count.get(key) + mobCount);
|
SpawnReporter.spawn_cap_count.get(key) + mobCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mobCount <= int_3)
|
if (mobCount <= int_3 || SpawnReporter.mock_spawns)
|
||||||
{
|
{
|
||||||
//place 0 to indicate there were spawn attempts for a category
|
//place 0 to indicate there were spawn attempts for a category
|
||||||
//if (entityCategory != EntityCategory.CREATURE || world.getServer().getTicks() % 400 == 0)
|
//if (entityCategory != EntityCategory.CREATURE || world.getServer().getTicks() % 400 == 0)
|
||||||
@@ -136,6 +136,8 @@ public abstract class ServerChunkManagerMixin
|
|||||||
@Inject(method = "tickChunks", at = @At("RETURN"))
|
@Inject(method = "tickChunks", at = @At("RETURN"))
|
||||||
private void onFinishSpawnWorldCycle(CallbackInfo ci)
|
private void onFinishSpawnWorldCycle(CallbackInfo ci)
|
||||||
{
|
{
|
||||||
|
LevelProperties levelProperties_1 = this.world.getLevelProperties();
|
||||||
|
boolean boolean_3 = levelProperties_1.getTime() % 400L == 0L;
|
||||||
if (SpawnReporter.track_spawns > 0L && SpawnReporter.local_spawns != null)
|
if (SpawnReporter.track_spawns > 0L && SpawnReporter.local_spawns != null)
|
||||||
{
|
{
|
||||||
for (EntityCategory cat: EntityCategory.values())
|
for (EntityCategory cat: EntityCategory.values())
|
||||||
@@ -145,9 +147,13 @@ public abstract class ServerChunkManagerMixin
|
|||||||
int spawnTries = SpawnReporter.spawn_tries.get(cat);
|
int spawnTries = SpawnReporter.spawn_tries.get(cat);
|
||||||
if (!SpawnReporter.local_spawns.containsKey(cat))
|
if (!SpawnReporter.local_spawns.containsKey(cat))
|
||||||
{
|
{
|
||||||
// fill mobcaps for that category so spawn got cancelled
|
if (!cat.isAnimal() || boolean_3)
|
||||||
SpawnReporter.spawn_ticks_full.put(key,
|
{
|
||||||
SpawnReporter.spawn_ticks_full.get(key)+ spawnTries);
|
// fill mobcaps for that category so spawn got cancelled
|
||||||
|
SpawnReporter.spawn_ticks_full.put(key,
|
||||||
|
SpawnReporter.spawn_ticks_full.get(key)+ spawnTries);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (SpawnReporter.local_spawns.get(cat) > 0)
|
else if (SpawnReporter.local_spawns.get(cat) > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,58 +1,66 @@
|
|||||||
package carpet.script;
|
package carpet.script;
|
||||||
|
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
import carpet.script.value.NullValue;
|
|
||||||
import carpet.settings.CarpetSettings;
|
|
||||||
import carpet.helpers.FeatureGenerator;
|
|
||||||
import carpet.fakes.MinecraftServerInterface;
|
import carpet.fakes.MinecraftServerInterface;
|
||||||
|
import carpet.helpers.FeatureGenerator;
|
||||||
|
import carpet.script.Fluff.TriFunction;
|
||||||
import carpet.script.exception.CarpetExpressionException;
|
import carpet.script.exception.CarpetExpressionException;
|
||||||
import carpet.script.exception.ExpressionException;
|
import carpet.script.exception.ExpressionException;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import carpet.script.Fluff.TriFunction;
|
|
||||||
import carpet.script.value.BlockValue;
|
import carpet.script.value.BlockValue;
|
||||||
import carpet.script.value.EntityValue;
|
import carpet.script.value.EntityValue;
|
||||||
import carpet.script.value.LazyListValue;
|
import carpet.script.value.LazyListValue;
|
||||||
import carpet.script.value.ListValue;
|
import carpet.script.value.ListValue;
|
||||||
|
import carpet.script.value.NBTSerializableValue;
|
||||||
|
import carpet.script.value.NullValue;
|
||||||
import carpet.script.value.NumericValue;
|
import carpet.script.value.NumericValue;
|
||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
|
import carpet.settings.CarpetSettings;
|
||||||
import carpet.utils.BlockInfo;
|
import carpet.utils.BlockInfo;
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockPlacementEnvironment;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.client.network.packet.PlaySoundIdS2CPacket;
|
||||||
|
import net.minecraft.command.arguments.ItemStackArgument;
|
||||||
|
import net.minecraft.command.arguments.ParticleArgumentType;
|
||||||
import net.minecraft.enchantment.Enchantments;
|
import net.minecraft.enchantment.Enchantments;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.EquipmentSlot;
|
import net.minecraft.entity.EquipmentSlot;
|
||||||
|
import net.minecraft.entity.ItemEntity;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.decoration.ArmorStandEntity;
|
import net.minecraft.entity.decoration.ArmorStandEntity;
|
||||||
import net.minecraft.item.Items;
|
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
|
||||||
import net.minecraft.command.arguments.ParticleArgumentType;
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.item.BlockItem;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.client.network.packet.PlaySoundIdS2CPacket;
|
import net.minecraft.item.Items;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.particle.ParticleEffect;
|
import net.minecraft.particle.ParticleEffect;
|
||||||
import net.minecraft.block.BlockPlacementEnvironment;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.state.property.Property;
|
|
||||||
import net.minecraft.state.StateFactory;
|
|
||||||
import net.minecraft.predicate.entity.EntityPredicates;
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.sound.BlockSoundGroup;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.state.StateFactory;
|
||||||
|
import net.minecraft.state.property.Property;
|
||||||
import net.minecraft.text.LiteralText;
|
import net.minecraft.text.LiteralText;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.sound.SoundCategory;
|
|
||||||
import net.minecraft.util.math.Box;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Box;
|
||||||
import net.minecraft.util.math.EulerRotation;
|
import net.minecraft.util.math.EulerRotation;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
|
import net.minecraft.world.Heightmap;
|
||||||
import net.minecraft.world.LightType;
|
import net.minecraft.world.LightType;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.world.Heightmap;
|
|
||||||
import net.minecraft.world.loot.context.LootContext;
|
import net.minecraft.world.loot.context.LootContext;
|
||||||
import net.minecraft.world.loot.context.LootContextParameters;
|
import net.minecraft.world.loot.context.LootContextParameters;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
@@ -94,6 +102,7 @@ public class CarpetExpression
|
|||||||
private ServerCommandSource source;
|
private ServerCommandSource source;
|
||||||
private BlockPos origin;
|
private BlockPos origin;
|
||||||
private Expression expr;
|
private Expression expr;
|
||||||
|
Expression getExpr() {return expr;}
|
||||||
private static long tickStart = 0L;
|
private static long tickStart = 0L;
|
||||||
|
|
||||||
private static boolean stopAll = false;
|
private static boolean stopAll = false;
|
||||||
@@ -272,12 +281,13 @@ public class CarpetExpression
|
|||||||
* <h2>Specifying blocks</h2>
|
* <h2>Specifying blocks</h2>
|
||||||
* <h3><code>block(x, y, z), block(state)</code></h3>
|
* <h3><code>block(x, y, z), block(state)</code></h3>
|
||||||
* <p>Returns either a block from specified location, or block with a specific state
|
* <p>Returns either a block from specified location, or block with a specific state
|
||||||
* (as used by <code>/setblock</code> command), so allowing for block properties etc.
|
* (as used by <code>/setblock</code> command), so allowing for block properties, block entity data etc.
|
||||||
* Blocks can be referenced everywhere by its simple name, but its only used in its default state</p>
|
* Blocks otherwise can be referenced everywhere by its simple string name, but its only used in its default state</p>
|
||||||
* <pre>
|
* <pre>
|
||||||
* block('air') => air
|
* block('air') => air
|
||||||
* block('iron_trapdoor[half=top]') => iron_trapdoor
|
* block('iron_trapdoor[half=top]') => iron_trapdoor
|
||||||
* block(0,0,0) == block('bedrock') => 1
|
* block(0,0,0) == block('bedrock') => 1
|
||||||
|
* block('hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') => hopper
|
||||||
* </pre>
|
* </pre>
|
||||||
* <h2>World Manipulation</h2>
|
* <h2>World Manipulation</h2>
|
||||||
* <p>All the functions below can be used with block value, queried with coord triple, or 3-long list.
|
* <p>All the functions below can be used with block value, queried with coord triple, or 3-long list.
|
||||||
@@ -306,6 +316,21 @@ public class CarpetExpression
|
|||||||
* set(x,y,z,'iron_trapdoor[half=top]') // Incorrect. sets bottom iron trapdoor - no parsing of properties
|
* set(x,y,z,'iron_trapdoor[half=top]') // Incorrect. sets bottom iron trapdoor - no parsing of properties
|
||||||
* set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
|
* set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
|
||||||
* set(x,y,z,block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
* set(x,y,z,block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
||||||
|
* set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
||||||
|
* </pre>
|
||||||
|
* <h3><code>place_item(item, pos, facing?, sneak?)</code></h3>
|
||||||
|
* <p>Places a given item in the world like it was placed by a player. Item names are default minecraft item name,
|
||||||
|
* less the minecraft prefix. Default facing is 'up', but there are other options: 'down', 'north', 'east',
|
||||||
|
* 'south', 'west', but also there are other secondary directions important for placement of blocks like stairs, doors, etc.
|
||||||
|
* Try experiment with options like 'north-up' which is placed facing north with cursor pointing to the upper part of the block,
|
||||||
|
* or 'up-north', which means a block placed facing up (player looking down) and placed smidge away of the block center
|
||||||
|
* towards north. Optional sneak is a boolean indicating if a player would be sneaking while placing the block -
|
||||||
|
* this option only affects placement of chests and scaffolding at the moment.
|
||||||
|
* Returns true if placement was successful, false otherwise.</p>
|
||||||
|
* <pre>
|
||||||
|
* place_item('stone',x,y,z) // places a stone block on x,y,z block
|
||||||
|
* place_item('piston,x,y,z,'down') // places a piston facing down
|
||||||
|
* place_item('carrot',x,y,z) // attempts to plant a carrot plant. Returns true if could place carrots at that position.
|
||||||
* </pre>
|
* </pre>
|
||||||
* <h3><code>update(pos)</code></h3>
|
* <h3><code>update(pos)</code></h3>
|
||||||
* <p>Causes a block update at position.</p>
|
* <p>Causes a block update at position.</p>
|
||||||
@@ -343,7 +368,12 @@ public class CarpetExpression
|
|||||||
* property(block('iron_trapdoor[half=top]'),'powered') => false
|
* property(block('iron_trapdoor[half=top]'),'powered') => false
|
||||||
* bool(property(block('iron_trapdoor[half=top]'),'powered')) => 0
|
* bool(property(block('iron_trapdoor[half=top]'),'powered')) => 0
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
* <h3><code>block_data(pos)</code></h3>
|
||||||
|
* <p>Return NBT string associated with specific location, or null if the block does not carry block data.
|
||||||
|
* Can be currently used to match specific information from it, or use it to copy to another block</p>
|
||||||
|
* <pre>
|
||||||
|
* block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
|
||||||
|
* </pre>
|
||||||
* <h3><code>solid(pos)</code></h3>
|
* <h3><code>solid(pos)</code></h3>
|
||||||
* <p>Boolean function, true if the block is solid</p>
|
* <p>Boolean function, true if the block is solid</p>
|
||||||
* <h3> <code>air(pos)</code></h3>
|
* <h3> <code>air(pos)</code></h3>
|
||||||
@@ -539,6 +569,20 @@ public class CarpetExpression
|
|||||||
return (c_, t_) -> retval;
|
return (c_, t_) -> retval;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("block_data", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
if (lv.size() == 0)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Block requires at least one parameter");
|
||||||
|
}
|
||||||
|
CompoundTag tag = BlockValue.fromParams(cc, lv, 0, true).block.getData();
|
||||||
|
if (tag.isEmpty())
|
||||||
|
return (c_, t_) -> Value.NULL;
|
||||||
|
Value retval = new NBTSerializableValue(tag);
|
||||||
|
return (c_, t_) -> retval;
|
||||||
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("pos", 1, (c, t, lv) ->
|
this.expr.addLazyFunction("pos", 1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
Value arg = lv.get(0).evalValue(c);
|
Value arg = lv.get(0).evalValue(c);
|
||||||
@@ -556,7 +600,7 @@ public class CarpetExpression
|
|||||||
if (e == null)
|
if (e == null)
|
||||||
throw new InternalExpressionException("Null entity");
|
throw new InternalExpressionException("Null entity");
|
||||||
Value retval = ListValue.of(new NumericValue(e.x), new NumericValue(e.y), new NumericValue(e.z));
|
Value retval = ListValue.of(new NumericValue(e.x), new NumericValue(e.y), new NumericValue(e.z));
|
||||||
return(c_, t_) -> retval;
|
return (c_, t_) -> retval;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -607,7 +651,8 @@ public class CarpetExpression
|
|||||||
genericStateTest(c, "blast_resistance", lv, (s, p, w) -> new NumericValue(s.getBlock().getBlastResistance())));
|
genericStateTest(c, "blast_resistance", lv, (s, p, w) -> new NumericValue(s.getBlock().getBlastResistance())));
|
||||||
|
|
||||||
|
|
||||||
this.expr.addLazyFunction("top", -1, (c, t, lv) -> {
|
this.expr.addLazyFunction("top", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
String type = lv.get(0).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
String type = lv.get(0).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||||
Heightmap.Type htype;
|
Heightmap.Type htype;
|
||||||
switch (type)
|
switch (type)
|
||||||
@@ -709,17 +754,36 @@ public class CarpetExpression
|
|||||||
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
|
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sourceBlockState == targetBlockState)
|
|
||||||
|
CompoundTag data = sourceLocator.block.getData();
|
||||||
|
|
||||||
|
if (sourceBlockState == targetBlockState && data == null)
|
||||||
return (c_, t_) -> Value.FALSE;
|
return (c_, t_) -> Value.FALSE;
|
||||||
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
||||||
cc.s.getWorld().setBlockState(targetLocator.block.getPos(), sourceBlockState, 2);
|
BlockPos targetPos = targetLocator.block.getPos();
|
||||||
|
cc.s.getWorld().setBlockState(targetPos, sourceBlockState, 2);
|
||||||
|
|
||||||
|
if ( data != null)
|
||||||
|
{
|
||||||
|
BlockEntity be = world.getBlockEntity(targetPos);
|
||||||
|
if (be != null)
|
||||||
|
{
|
||||||
|
CompoundTag destTag = data.method_10553();
|
||||||
|
destTag.putInt("x", targetPos.getX());
|
||||||
|
destTag.putInt("y", targetPos.getY());
|
||||||
|
destTag.putInt("z", targetPos.getZ());
|
||||||
|
be.fromTag(destTag);
|
||||||
|
be.markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CarpetSettings.impendingFillSkipUpdates = false;
|
CarpetSettings.impendingFillSkipUpdates = false;
|
||||||
Value retval = new BlockValue(sourceBlockState, world, targetLocator.block.getPos());
|
Value retval = new BlockValue(sourceBlockState, world, targetLocator.block.getPos());
|
||||||
return (c_, t_) -> retval;
|
return (c_, t_) -> retval;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("destroy", -1, (c, t, lv) -> {
|
this.expr.addLazyFunction("destroy", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
ServerWorld world = cc.s.getWorld();
|
ServerWorld world = cc.s.getWorld();
|
||||||
BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0);
|
BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0);
|
||||||
@@ -756,8 +820,10 @@ public class CarpetExpression
|
|||||||
return (c_, t_) -> Value.TRUE;
|
return (c_, t_) -> Value.TRUE;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("harvest", -1, (c, t, lv) -> {
|
this.expr.addLazyFunction("harvest", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
if (lv.size()<2)
|
||||||
|
throw new InternalExpressionException("harvest takes at least 2 parameters: entity and block, or position, to harvest");
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
World world = cc.s.getWorld();
|
World world = cc.s.getWorld();
|
||||||
Value entityValue = lv.get(0).evalValue(cc);
|
Value entityValue = lv.get(0).evalValue(cc);
|
||||||
@@ -776,11 +842,57 @@ public class CarpetExpression
|
|||||||
ItemStack itemstack2 = player.getMainHandStack();
|
ItemStack itemstack2 = player.getMainHandStack();
|
||||||
itemstack2.postMine(player.world, state, where, player);
|
itemstack2.postMine(player.world, state, where, player);
|
||||||
ItemStack itemstack1 = itemstack2.isEmpty() ? ItemStack.EMPTY : itemstack2.copy();
|
ItemStack itemstack1 = itemstack2.isEmpty() ? ItemStack.EMPTY : itemstack2.copy();
|
||||||
state.getBlock().afterBreak(world, player, where,state, be, itemstack1);
|
state.getBlock().afterBreak(world, player, where, state, be, itemstack1);
|
||||||
world.playLevelEvent(null, 2001, where, Block.getRawIdFromState(state));
|
world.playLevelEvent(null, 2001, where, Block.getRawIdFromState(state));
|
||||||
return (c_, t_) -> Value.TRUE;
|
return (c_, t_) -> Value.TRUE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("place_item", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
if (lv.size()<2)
|
||||||
|
throw new InternalExpressionException("place_item takes at least 2 parameters: item and block, or position, to place onto");
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
String itemString = lv.get(0).evalValue(c).getString();
|
||||||
|
BlockValue.VectorLocator locator = BlockValue.locateVec(cc, lv, 1);
|
||||||
|
ItemStackArgument stackArg = NBTSerializableValue.parseItem(itemString);
|
||||||
|
BlockPos where = new BlockPos(locator.vec);
|
||||||
|
String facing = "up";
|
||||||
|
if (lv.size() > locator.offset)
|
||||||
|
facing = lv.get(locator.offset).evalValue(c).getString();
|
||||||
|
boolean sneakPlace = false;
|
||||||
|
if (lv.size() > locator.offset+1)
|
||||||
|
sneakPlace = lv.get(locator.offset+1).evalValue(c).getBoolean();
|
||||||
|
if (stackArg.getItem() instanceof BlockItem)
|
||||||
|
{
|
||||||
|
BlockItem blockItem = (BlockItem) stackArg.getItem();
|
||||||
|
BlockValue.PlacementContext ctx;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ctx = BlockValue.PlacementContext.from(cc.s.getWorld(), where, facing, sneakPlace, stackArg.createStack(1, false));
|
||||||
|
}
|
||||||
|
catch (CommandSyntaxException e)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException(e.getMessage());
|
||||||
|
}
|
||||||
|
if (!ctx.canPlace())
|
||||||
|
return (_c, _t) -> Value.FALSE;
|
||||||
|
BlockState placementState = blockItem.getBlock().getPlacementState(ctx);
|
||||||
|
if (placementState != null)
|
||||||
|
{
|
||||||
|
if (placementState.canPlaceAt(cc.s.getWorld(), where))
|
||||||
|
{
|
||||||
|
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
||||||
|
cc.s.getWorld().setBlockState(where, placementState, 2);
|
||||||
|
CarpetSettings.impendingFillSkipUpdates = false;
|
||||||
|
BlockSoundGroup blockSoundGroup = placementState.getSoundGroup();
|
||||||
|
cc.s.getWorld().playSound(null, where, blockSoundGroup.getPlaceSound(), SoundCategory.BLOCKS, (blockSoundGroup.getVolume() + 1.0F) / 2.0F, blockSoundGroup.getPitch() * 0.8F);
|
||||||
|
return (_c, _t) -> Value.TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (_c, _t) -> Value.FALSE;
|
||||||
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("blocks_movement", -1, (c, t, lv) ->
|
this.expr.addLazyFunction("blocks_movement", -1, (c, t, lv) ->
|
||||||
booleanStateTest(c, "blocks_movement", lv, (s, p) ->
|
booleanStateTest(c, "blocks_movement", lv, (s, p) ->
|
||||||
!s.canPlaceAtSide(((CarpetContext) c).s.getWorld(), p, BlockPlacementEnvironment.LAND)));
|
!s.canPlaceAtSide(((CarpetContext) c).s.getWorld(), p, BlockPlacementEnvironment.LAND)));
|
||||||
@@ -789,11 +901,11 @@ public class CarpetExpression
|
|||||||
stateStringQuery(c, "block_sound", lv, (s, p) ->
|
stateStringQuery(c, "block_sound", lv, (s, p) ->
|
||||||
BlockInfo.soundName.get(s.getSoundGroup())));
|
BlockInfo.soundName.get(s.getSoundGroup())));
|
||||||
|
|
||||||
this.expr.addLazyFunction("material",-1, (c, t, lv) ->
|
this.expr.addLazyFunction("material", -1, (c, t, lv) ->
|
||||||
stateStringQuery(c, "material", lv, (s, p) ->
|
stateStringQuery(c, "material", lv, (s, p) ->
|
||||||
BlockInfo.materialName.get(s.getMaterial())));
|
BlockInfo.materialName.get(s.getMaterial())));
|
||||||
|
|
||||||
this.expr.addLazyFunction("map_colour", -1, (c, t, lv) ->
|
this.expr.addLazyFunction("map_colour", -1, (c, t, lv) ->
|
||||||
stateStringQuery(c, "map_colour", lv, (s, p) ->
|
stateStringQuery(c, "map_colour", lv, (s, p) ->
|
||||||
BlockInfo.mapColourName.get(s.getTopMaterialColor(((CarpetContext)c).s.getWorld(), p))));
|
BlockInfo.mapColourName.get(s.getTopMaterialColor(((CarpetContext)c).s.getWorld(), p))));
|
||||||
|
|
||||||
@@ -807,10 +919,331 @@ public class CarpetExpression
|
|||||||
if (property == null)
|
if (property == null)
|
||||||
return LazyValue.NULL;
|
return LazyValue.NULL;
|
||||||
Value retval = new StringValue(state.get(property).toString());
|
Value retval = new StringValue(state.get(property).toString());
|
||||||
return (_c, _t ) -> retval;
|
return (_c, _t) -> retval;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>Inventory and Items API</h1>
|
||||||
|
* <div style="padding-left: 20px; border-radius: 5px 45px; border:1px solid grey;">
|
||||||
|
* <h2>Manipulating inventories of blocks and entities</h2>
|
||||||
|
* <p>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.
|
||||||
|
* If the entity or a block doesn't
|
||||||
|
* have an inventory, they typically do nothing and return null.</p>
|
||||||
|
* <p>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</p>
|
||||||
|
* <h3><code>stack_limit(item)</code></h3>
|
||||||
|
* <p>Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable),
|
||||||
|
* 16 (like buckets), or 64 - rest. It is recommended to consult this, as other inventory API functions
|
||||||
|
* ignore normal stack limits, and it is up to the programmer to keep it at bay. As of 1.13, game checks for negative
|
||||||
|
* numbers and setting an item to negative is the same as empty.</p>
|
||||||
|
* <pre>
|
||||||
|
* stack_limit('wooden_axe') => 1
|
||||||
|
* stack_limit('ender_pearl') => 16
|
||||||
|
* stack_limit('stone') => 64
|
||||||
|
* </pre>
|
||||||
|
* <h3><code>inventory_size(inventory)</code></h3>
|
||||||
|
* <p>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</p>
|
||||||
|
* <pre>
|
||||||
|
* inventory_size(player()) => 41
|
||||||
|
* inventory_size(x,y,z) => 27 // chest
|
||||||
|
* inventory_size(block(pos)) => 5 // hopper
|
||||||
|
* </pre>
|
||||||
|
* <h3><code>inventory_get(inventory, slot)</code></h3>
|
||||||
|
* <p>Returns the item in the corresponding inventory slot, or null if slot empty or inventory is invalid.
|
||||||
|
* You can use negative numbers to indicate slots counted from 'the back'.</p>
|
||||||
|
* <pre>
|
||||||
|
* inventory_get(player(), 0) => null // nothing in first hotbar slot
|
||||||
|
* inventory_get(x,y,z, 5) => ['stone', 1, {}]
|
||||||
|
* inventory_get(player(), -1) => ['diamond_pickaxe', 1, {Damage:4}] // slightly damaged diamond pick in the offhand
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h3><code>inventory_set(inventory, slot, count, item?, nbt?) </code></h3>
|
||||||
|
* <p>Modifies or sets a stack in inventory. specify count 0 to empty the slot.
|
||||||
|
* If item is not specified, keeps existing item, just modifies the count. If item is provided - replaces current item.
|
||||||
|
* If nbt is provided - adds a tag to the stack at slot. Returns previous stack in that slot.</p>
|
||||||
|
* <pre>
|
||||||
|
* inventory_set(player(), 0, 0) => ['stone', 64, {}] // player had a stack of stone in first hotbar slot
|
||||||
|
* inventory_set(player(), 0, 6) => ['diamond', 64, {}] // changed stack of diamonds in player slot to 6
|
||||||
|
* inventory_set(player(), 0, 1, 'diamond_axe','{Damage:5}') => null //added slightly damaged pick to first player slot
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h3><code>inventory_find(inventory, item, start_slot?, ), inventory_find(inventory, null, start_slot?) </code></h3>
|
||||||
|
* <p>Finds the first slot with a corresponding item in the inventory, or if queried with null: the first empty slot.
|
||||||
|
* Returns slot number if found, or null otherwise. Optional start_slot argument allows to skip all preceeding slots
|
||||||
|
* allowing for efficient (so not slot-by-slot) inventory search for items.</p>
|
||||||
|
* <pre>
|
||||||
|
* inventory_find(player(), 'stone') => 0 // player has stone in first hotbar slot
|
||||||
|
* inventory_find(player(), null) => null // player's inventory has no empty spot
|
||||||
|
* while( (slot = inventory_find(p, 'diamond', slot)) != null, 41, drop_item(p, slot) )
|
||||||
|
* // spits all diamonds from player inventory wherever they are
|
||||||
|
* inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h3><code>inventory_remove(inventory, item, amount?) </code></h3>
|
||||||
|
* <p>Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing
|
||||||
|
* happens, otherwise the given amount of items is removed wherever they are in the inventory. Returns boolean whether
|
||||||
|
* the removal operation was successful. Easiest way to remove a specific item from player inventory without specifying
|
||||||
|
* the slot.</p>
|
||||||
|
* <pre>
|
||||||
|
* inventory_remove(player(), 'diamond') => 1 // removed diamond from player inventory
|
||||||
|
* inventory_remove(player(), 'diamond', 100) => 0 // player doesn't have 100 diamonds, nothing happened
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h3><code>drop_item(inventory, slot, amount?, )</code></h3>
|
||||||
|
* <p>Drops the items from indicated inventory slot, like player that Q's an item or villager, that exchanges food.
|
||||||
|
* You can Q items from block inventories as well. default amount is 0 - which is all from the slot. NOTE: hoppers are quick
|
||||||
|
* enough to pick all the queued items from their inventory anyways.
|
||||||
|
* Returns size of the actual dropped items.</p>
|
||||||
|
* <pre>
|
||||||
|
* inventory_drop(player(), 0, 1) => 1 // Q's one item on the ground
|
||||||
|
* inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
||||||
|
* </pre>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
public void API_InventoryManipulation()
|
||||||
|
{
|
||||||
|
this.expr.addLazyFunction("stack_limit", 1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
ItemStackArgument item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString());
|
||||||
|
Value res = new NumericValue(item.getItem().getMaxCount());
|
||||||
|
return (_c, _t) -> res;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("inventory_size", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||||
|
if (inventoryLocator == null)
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
Value res = new NumericValue(inventoryLocator.inventory.getInvSize());
|
||||||
|
return (_c, _t) -> res;
|
||||||
|
});
|
||||||
|
|
||||||
|
//inventory_get(<b, e>, <n>) -> item_triple
|
||||||
|
this.expr.addLazyFunction("inventory_get", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||||
|
if (inventoryLocator == null)
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
if (lv.size() == inventoryLocator.offset)
|
||||||
|
{
|
||||||
|
List<Value> fullInventory = new ArrayList<>();
|
||||||
|
for (int i = 0, maxi = inventoryLocator.inventory.getInvSize(); i < maxi; i++)
|
||||||
|
{
|
||||||
|
fullInventory.add(ListValue.fromItemStack(inventoryLocator.inventory.getInvStack(i)));
|
||||||
|
}
|
||||||
|
Value res = ListValue.wrap(fullInventory);
|
||||||
|
return (_c, _t) -> res;
|
||||||
|
}
|
||||||
|
int slot = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset).evalValue(c)).getLong();
|
||||||
|
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory);
|
||||||
|
if (slot == inventoryLocator.inventory.getInvSize())
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
Value res = ListValue.fromItemStack(inventoryLocator.inventory.getInvStack(slot));
|
||||||
|
return (_c, _t) -> res;
|
||||||
|
});
|
||||||
|
|
||||||
|
//inventory_set(<b,e>, <n>, <count>, <item>, <nbt>)
|
||||||
|
this.expr.addLazyFunction("inventory_set", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||||
|
if (inventoryLocator == null)
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
if (lv.size() < inventoryLocator.offset+2)
|
||||||
|
throw new InternalExpressionException("inventory_set requires at least slot number and new stack size, and optional new item");
|
||||||
|
int slot = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+0).evalValue(c)).getLong();
|
||||||
|
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory);
|
||||||
|
if (slot == inventoryLocator.inventory.getInvSize())
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
int count = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
// clear slot
|
||||||
|
ItemStack removedStack = inventoryLocator.inventory.removeInvStack(slot);
|
||||||
|
//Value res = ListValue.fromItemStack(removedStack); // that tuple will be read only but cheaper if noone cares
|
||||||
|
return (_c, _t) -> ListValue.fromItemStack(removedStack);
|
||||||
|
}
|
||||||
|
if (lv.size() < inventoryLocator.offset+3)
|
||||||
|
{
|
||||||
|
ItemStack previousStack = inventoryLocator.inventory.getInvStack(slot);
|
||||||
|
ItemStack newStack = previousStack.copy();
|
||||||
|
newStack.setCount(count);
|
||||||
|
inventoryLocator.inventory.setInvStack(slot, newStack);
|
||||||
|
return (_c, _t) -> ListValue.fromItemStack(previousStack);
|
||||||
|
}
|
||||||
|
CompoundTag nbt = null; // skipping one argument
|
||||||
|
if (lv.size() > inventoryLocator.offset+3)
|
||||||
|
{
|
||||||
|
Value nbtValue = lv.get(inventoryLocator.offset+3).evalValue(c);
|
||||||
|
if (nbtValue instanceof NBTSerializableValue)
|
||||||
|
nbt = ((NBTSerializableValue)nbtValue).getTag();
|
||||||
|
else
|
||||||
|
nbt = new NBTSerializableValue(nbtValue.getString()).getTag();
|
||||||
|
}
|
||||||
|
ItemStackArgument newitem = NBTSerializableValue.parseItem(
|
||||||
|
lv.get(inventoryLocator.offset+2).evalValue(c).getString(),
|
||||||
|
nbt
|
||||||
|
);
|
||||||
|
|
||||||
|
ItemStack previousStack = inventoryLocator.inventory.getInvStack(slot);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
inventoryLocator.inventory.setInvStack(slot, newitem.createStack(count, false));
|
||||||
|
}
|
||||||
|
catch (CommandSyntaxException e)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException(e.getMessage());
|
||||||
|
}
|
||||||
|
return (_c, _t) -> ListValue.fromItemStack(previousStack);
|
||||||
|
});
|
||||||
|
|
||||||
|
//inventory_find(<b, e>, <item> or null (first empty slot), <start_from=0> ) -> <N> or null
|
||||||
|
this.expr.addLazyFunction("inventory_find", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||||
|
if (inventoryLocator == null)
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
ItemStackArgument itemArg = null;
|
||||||
|
if (lv.size() > inventoryLocator.offset)
|
||||||
|
{
|
||||||
|
Value secondArg = lv.get(inventoryLocator.offset+0).evalValue(c);
|
||||||
|
if (!(secondArg instanceof NullValue))
|
||||||
|
itemArg = NBTSerializableValue.parseItem(secondArg.getString());
|
||||||
|
}
|
||||||
|
int startIndex = 0;
|
||||||
|
if (lv.size() > inventoryLocator.offset+1)
|
||||||
|
{
|
||||||
|
startIndex = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
|
||||||
|
}
|
||||||
|
startIndex = NBTSerializableValue.validateSlot(startIndex, inventoryLocator.inventory);
|
||||||
|
for (int i = startIndex, maxi = inventoryLocator.inventory.getInvSize(); i < maxi; i++)
|
||||||
|
{
|
||||||
|
ItemStack stack = inventoryLocator.inventory.getInvStack(i);
|
||||||
|
if ( (itemArg == null && stack.isEmpty()) || (itemArg != null && itemArg.getItem().equals(stack.getItem())) )
|
||||||
|
{
|
||||||
|
Value res = new NumericValue(i);
|
||||||
|
return (_c, _t) -> res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//inventory_remove(<b, e>, <item>, <amount=1>) -> bool
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("inventory_remove", -1, (c, t, lv) -> {
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||||
|
if (inventoryLocator == null)
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
if (lv.size() <= inventoryLocator.offset)
|
||||||
|
throw new InternalExpressionException("inventory_set requires at least an item to be removed");
|
||||||
|
ItemStackArgument searchItem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset).evalValue(c).getString());
|
||||||
|
int amount = 1;
|
||||||
|
if (lv.size() > inventoryLocator.offset+1)
|
||||||
|
{
|
||||||
|
amount = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
|
||||||
|
}
|
||||||
|
// not enough
|
||||||
|
if (((amount == 1) && (!inventoryLocator.inventory.containsAnyInInv(Sets.newHashSet(searchItem.getItem()))))
|
||||||
|
|| (inventoryLocator.inventory.countInInv(searchItem.getItem()) < amount))
|
||||||
|
{
|
||||||
|
return (_c, _t) -> Value.FALSE;
|
||||||
|
}
|
||||||
|
for (int i = 0, maxi = inventoryLocator.inventory.getInvSize(); i < maxi; i++)
|
||||||
|
{
|
||||||
|
ItemStack stack = inventoryLocator.inventory.getInvStack(i);
|
||||||
|
if (stack.isEmpty())
|
||||||
|
continue;
|
||||||
|
if (!stack.getItem().equals(searchItem.getItem()))
|
||||||
|
continue;
|
||||||
|
int left = stack.getCount()-amount;
|
||||||
|
if (left > 0)
|
||||||
|
{
|
||||||
|
stack.setCount(left);
|
||||||
|
inventoryLocator.inventory.setInvStack(i, stack);
|
||||||
|
return (_c, _t) -> Value.TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inventoryLocator.inventory.removeInvStack(i);
|
||||||
|
amount -= stack.getCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (amount > 0)
|
||||||
|
throw new InternalExpressionException("Something bad happened - cannot pull all items from inventory");
|
||||||
|
return (_c, _t) -> Value.TRUE;
|
||||||
|
});
|
||||||
|
|
||||||
|
//inventory_drop(<b, e>, <n>, <amount=1, 0-whatever's there>) -> entity_item (and sets slot) or null if cannot
|
||||||
|
this.expr.addLazyFunction("drop_item", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||||
|
if (inventoryLocator == null)
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
if (lv.size() == inventoryLocator.offset)
|
||||||
|
throw new InternalExpressionException("slot number is required for inventory_drop");
|
||||||
|
int slot = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset).evalValue(c)).getLong();
|
||||||
|
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory);
|
||||||
|
if (slot == inventoryLocator.inventory.getInvSize())
|
||||||
|
return (_c, _t) -> Value.NULL;
|
||||||
|
int amount = 0;
|
||||||
|
if (lv.size() > inventoryLocator.offset+1)
|
||||||
|
amount = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
|
||||||
|
if (amount < 0)
|
||||||
|
throw new InternalExpressionException("Cannot throw negative number of items");
|
||||||
|
ItemStack stack = inventoryLocator.inventory.getInvStack(slot);
|
||||||
|
if (stack == null || stack.isEmpty())
|
||||||
|
return (_c, _t) -> Value.ZERO;
|
||||||
|
if (amount == 0)
|
||||||
|
amount = stack.getCount();
|
||||||
|
ItemStack droppedStack = inventoryLocator.inventory.takeInvStack(slot, amount);
|
||||||
|
if (droppedStack.isEmpty())
|
||||||
|
{
|
||||||
|
return (_c, _t) -> Value.ZERO;
|
||||||
|
}
|
||||||
|
Object owner = inventoryLocator.owner;
|
||||||
|
ItemEntity item;
|
||||||
|
if (owner instanceof PlayerEntity)
|
||||||
|
{
|
||||||
|
item = ((PlayerEntity) owner).dropItem(droppedStack, false, true);
|
||||||
|
}
|
||||||
|
else if (owner instanceof LivingEntity)
|
||||||
|
{
|
||||||
|
LivingEntity villager = (LivingEntity)owner;
|
||||||
|
// stolen from LookTargetUtil.give((VillagerEntity)owner, droppedStack, (LivingEntity) owner);
|
||||||
|
double double_1 = villager.y - 0.30000001192092896D + (double)villager.getStandingEyeHeight();
|
||||||
|
item = new ItemEntity(villager.world, villager.x, double_1, villager.z, droppedStack);
|
||||||
|
Vec3d vec3d_1 = villager.getRotationVec(1.0F).normalize().multiply(0.3);// new Vec3d(0, 0.3, 0);
|
||||||
|
item.setVelocity(vec3d_1);
|
||||||
|
item.setToDefaultPickupDelay();
|
||||||
|
cc.s.getWorld().spawnEntity(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Vec3d point = new Vec3d(inventoryLocator.position);
|
||||||
|
item = new ItemEntity(cc.s.getWorld(), point.x+0.5, point.y+0.5, point.z+0.5, droppedStack);
|
||||||
|
item.setToDefaultPickupDelay();
|
||||||
|
cc.s.getWorld().spawnEntity(item);
|
||||||
|
}
|
||||||
|
inventoryLocator.inventory.markDirty();
|
||||||
|
Value res = new NumericValue(item.getStack().getCount());
|
||||||
|
return (_c, _t) -> res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <h1>Entity API</h1>
|
* <h1>Entity API</h1>
|
||||||
* <div style="padding-left: 20px; border-radius: 5px 45px; border:1px solid grey;">
|
* <div style="padding-left: 20px; border-radius: 5px 45px; border:1px solid grey;">
|
||||||
@@ -859,6 +1292,12 @@ public class CarpetExpression
|
|||||||
* <p>Unlike with blocks, that use plethora of vastly different querying functions, entities are queried with
|
* <p>Unlike with blocks, that use plethora of vastly different querying functions, entities are queried with
|
||||||
* <code>query</code> function and altered via <code>modify</code> function. Type of information needed or
|
* <code>query</code> function and altered via <code>modify</code> function. Type of information needed or
|
||||||
* values to be modified are different for each call</p>
|
* values to be modified are different for each call</p>
|
||||||
|
* <p>Using <code>~</code> (in) operator is an alias for <code>query</code>. Especially useful if a statement has
|
||||||
|
* no arguments, which in this case can be radically simplified</p>
|
||||||
|
* <pre>
|
||||||
|
* query(p, 'name') <=> p ~ 'name' // much shorter and cleaner
|
||||||
|
* query(p, 'holds', 'offhand') <=> p ~ l('holds', 'offhand') // not really but can be done
|
||||||
|
* </pre>
|
||||||
* <h3><code>query(e,'removed')</code></h3>
|
* <h3><code>query(e,'removed')</code></h3>
|
||||||
* <p>Boolean. True if the entity is removed</p>
|
* <p>Boolean. True if the entity is removed</p>
|
||||||
* <h3><code>query(e,'id')</code></h3>
|
* <h3><code>query(e,'id')</code></h3>
|
||||||
@@ -969,6 +1408,12 @@ public class CarpetExpression
|
|||||||
* <li><code>feet</code></li>
|
* <li><code>feet</code></li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>If <code>slot</code> is not specified, it defaults to the main hand.</p>
|
* <p>If <code>slot</code> is not specified, it defaults to the main hand.</p>
|
||||||
|
* <h3><code>query(e,'selected_slot')</code></h3>
|
||||||
|
* <p>Number indicating the selected slot of entity inventory. Currently only applicable to players.</p>
|
||||||
|
* <h3><code>query(e,'facing', order?)</code></h3>
|
||||||
|
* <p>Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating
|
||||||
|
* primary directions where entity is looking at. From most prominent (order 0) to opposite (order 5, or -1)</p>
|
||||||
|
*
|
||||||
* <h3><code>query(e,'nbt',path?)</code></h3>
|
* <h3><code>query(e,'nbt',path?)</code></h3>
|
||||||
* <p>Returns full NBT of the entity. If path is specified, it fetches only that portion of the NBT,
|
* <p>Returns full NBT of the entity. If path is specified, it fetches only that portion of the NBT,
|
||||||
* that corresponds to the path. For specification of <code>path</code> attribute, consult
|
* that corresponds to the path. For specification of <code>path</code> attribute, consult
|
||||||
@@ -2051,6 +2496,7 @@ public class CarpetExpression
|
|||||||
|
|
||||||
API_BlockManipulation();
|
API_BlockManipulation();
|
||||||
API_EntityManipulation();
|
API_EntityManipulation();
|
||||||
|
API_InventoryManipulation();
|
||||||
API_IteratingOverAreasOfBlocks();
|
API_IteratingOverAreasOfBlocks();
|
||||||
API_AuxiliaryAspects();
|
API_AuxiliaryAspects();
|
||||||
}
|
}
|
||||||
@@ -2092,6 +2538,7 @@ public class CarpetExpression
|
|||||||
* <p>The last method is the one that world edit is using (part of carpet mod). It turns out that the outline method with <code>32.5</code> radius,
|
* <p>The last method is the one that world edit is using (part of carpet mod). It turns out that the outline method with <code>32.5</code> radius,
|
||||||
* fill method with <code>round</code> function and draw command are equivalent</p>
|
* fill method with <code>round</code> function and draw command are equivalent</p>
|
||||||
* </div>
|
* </div>
|
||||||
|
* @param host .
|
||||||
* @param x .
|
* @param x .
|
||||||
* @param y .
|
* @param y .
|
||||||
* @param z .
|
* @param z .
|
||||||
@@ -2134,12 +2581,14 @@ public class CarpetExpression
|
|||||||
/**
|
/**
|
||||||
* <h1><code>/script run</code> command</h1>
|
* <h1><code>/script run</code> command</h1>
|
||||||
* <div style="padding-left: 20px; border-radius: 5px 45px; border:1px solid grey;">
|
* <div style="padding-left: 20px; border-radius: 5px 45px; border:1px solid grey;">
|
||||||
* <p>primary way to input commands. The command executes in the context, position, and dimension of the executing
|
* <p>Primary way to input commands. The command executes in the context, position, and dimension of the executing
|
||||||
* player, commandblock, etc... The command receives 4 variables, <code>x</code>, <code>y</code>, <code>z</code>
|
* player, commandblock, etc... The command receives 4 variables, <code>x</code>, <code>y</code>, <code>z</code>
|
||||||
* and <code>p</code> indicating position and the executing entity of the command.
|
* and <code>p</code> indicating position and the executing entity of the command.
|
||||||
|
* You will receive tab completion suggestions as you type your code suggesting functions and global variables.
|
||||||
* It is advisable to use <code>/execute in ... at ... as ... run script run ...</code> or similar, to simulate running
|
* It is advisable to use <code>/execute in ... at ... as ... run script run ...</code> or similar, to simulate running
|
||||||
* commands in a different scope</p>
|
* commands in a different scope</p>
|
||||||
* </div>
|
* </div>
|
||||||
|
* @param host .
|
||||||
* @param pos .
|
* @param pos .
|
||||||
* @return .
|
* @return .
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import carpet.script.value.ListValue;
|
|||||||
import carpet.script.value.NumericValue;
|
import carpet.script.value.NumericValue;
|
||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
|
import carpet.settings.CarpetSettings;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -31,6 +32,7 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -349,6 +351,7 @@ public class Expression implements Cloneable
|
|||||||
boolean isAnOperator(String opname) { return operators.containsKey(opname) || operators.containsKey(opname+"u");}
|
boolean isAnOperator(String opname) { return operators.containsKey(opname) || operators.containsKey(opname+"u");}
|
||||||
|
|
||||||
private Map<String, ILazyFunction> functions = new HashMap<>();
|
private Map<String, ILazyFunction> functions = new HashMap<>();
|
||||||
|
Set<String> getFunctionNames() {return functions.keySet();}
|
||||||
|
|
||||||
//static final Map<String, UserDefinedFunction> globalFunctions = new HashMap<>();
|
//static final Map<String, UserDefinedFunction> globalFunctions = new HashMap<>();
|
||||||
|
|
||||||
@@ -856,9 +859,10 @@ public class Expression implements Cloneable
|
|||||||
* <p>Ability to combine more statements into one expression, with functions, passing parameters, and global and outer
|
* <p>Ability to combine more statements into one expression, with functions, passing parameters, and global and outer
|
||||||
* scoping allow to organize even larger scripts</p>
|
* scoping allow to organize even larger scripts</p>
|
||||||
* <h2>Control flow</h2>
|
* <h2>Control flow</h2>
|
||||||
* <h3><code>return(expr)</code></h3>
|
* <h3><code>return(expr?)</code></h3>
|
||||||
* <p>Sometimes its convenient to break the organized control flow, or it is not practical to pass
|
* <p>Sometimes its convenient to break the organized control flow, or it is not practical to pass
|
||||||
* the final result value of a function to the last statement, in this case a return statement can be used</p>
|
* the final result value of a function to the last statement, in this case a return statement can be used</p>
|
||||||
|
* <p>If no argument is provided - returns null value.</p>
|
||||||
* <pre>
|
* <pre>
|
||||||
* def() -> (
|
* def() -> (
|
||||||
* expr1;
|
* expr1;
|
||||||
@@ -870,12 +874,14 @@ public class Expression implements Cloneable
|
|||||||
* </pre>
|
* </pre>
|
||||||
* <p>In general its cheaper to leave the last expression as a return value, rather than calling returns everywhere,
|
* <p>In general its cheaper to leave the last expression as a return value, rather than calling returns everywhere,
|
||||||
* but it would often lead to a messy code.</p>
|
* but it would often lead to a messy code.</p>
|
||||||
* <h3><code>exit(expr)</code></h3>
|
* <h3><code>exit(expr?)</code></h3>
|
||||||
* <p>It terminates entire program passing <code>expr</code> as the result of the program execution.</p>
|
* <p>It terminates entire program passing <code>expr</code> as the result of the program execution, or null if omitted.</p>
|
||||||
* <h3><code>try(expr, catch_expr(_)) ... throw(value)</code></h3>
|
* <h3><code>try(expr, catch_expr(_)?) ... throw(value?)</code></h3>
|
||||||
* <p><code>try</code> function evaluates expression, and continues further unless <code>throw</code> function is called
|
* <p><code>try</code> function evaluates expression, and continues further unless <code>throw</code> function is called
|
||||||
* anywhere inside <code>expr</code>. In that case the <code>catch_expr</code> is evaluates with <code>_</code> set
|
* anywhere inside <code>expr</code>. In that case the <code>catch_expr</code> is evaluates with <code>_</code> set
|
||||||
* to the argument <code>throw</code> was called with. This mechanism allows to terminate large portion of a convoluted
|
* to the argument <code>throw</code> was called with.
|
||||||
|
* This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns null as well
|
||||||
|
* This mechanism allows to terminate large portion of a convoluted
|
||||||
* call stack and continue program execution. There is only one level of exceptions currently in carpet, so if the inner
|
* call stack and continue program execution. There is only one level of exceptions currently in carpet, so if the inner
|
||||||
* function also defines the <code>try</code> catchment area, it will received the exception first, but it can technically
|
* function also defines the <code>try</code> catchment area, it will received the exception first, but it can technically
|
||||||
* rethrow the value its getting for the outer scope. Unhandled throw acts like an exit statement.</p>
|
* rethrow the value its getting for the outer scope. Unhandled throw acts like an exit statement.</p>
|
||||||
@@ -961,12 +967,14 @@ public class Expression implements Cloneable
|
|||||||
return (cc, tt) -> new StringValue("OK");
|
return (cc, tt) -> new StringValue("OK");
|
||||||
});
|
});
|
||||||
|
|
||||||
addUnaryFunction("exit", (v) -> { throw new ExitStatement(v); });
|
addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||||
addUnaryFunction("return", (v) -> { throw new ReturnStatement(v); });
|
addFunction("return", (lv) -> { throw new ReturnStatement(lv.size()==0?Value.NULL:lv.get(0));} );
|
||||||
addUnaryFunction("throw", (v)-> {throw new ThrowStatement(v); });
|
addFunction("throw", (lv)-> {throw new ThrowStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||||
|
|
||||||
addLazyFunction("try", 2, (c, t, lv) ->
|
addLazyFunction("try", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
|
if (lv.size()==0)
|
||||||
|
throw new InternalExpressionException("try needs at least an expression block");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Value retval = lv.get(0).evalValue(c, t);
|
Value retval = lv.get(0).evalValue(c, t);
|
||||||
@@ -974,6 +982,8 @@ public class Expression implements Cloneable
|
|||||||
}
|
}
|
||||||
catch (ThrowStatement ret)
|
catch (ThrowStatement ret)
|
||||||
{
|
{
|
||||||
|
if (lv.size() == 1)
|
||||||
|
return (c_, t_) -> Value.NULL;
|
||||||
LazyValue __ = c.getVariable("_");
|
LazyValue __ = c.getVariable("_");
|
||||||
c.setVariable("_", (__c, __t) -> ret.retval.reboundedTo("_"));
|
c.setVariable("_", (__c, __t) -> ret.retval.reboundedTo("_"));
|
||||||
Value val = lv.get(1).evalValue(c, t);
|
Value val = lv.get(1).evalValue(c, t);
|
||||||
@@ -1613,15 +1623,15 @@ public class Expression implements Cloneable
|
|||||||
* range(20, 10, -2) => [20, 18, 16, 14, 12]
|
* range(20, 10, -2) => [20, 18, 16, 14, 12]
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <h3><code>element(list, index)</code></h3>
|
* <h3><code>get(list, index), element(list, index)(deprecated)</code></h3>
|
||||||
* <p>Returns the value at <code>index</code> element from the <code>list</code>.
|
* <p>Returns the value at <code>index</code> element from the <code>list</code>.
|
||||||
* use negative numbers to reach elements from the end of the list. <code>element</code>
|
* use negative numbers to reach elements from the end of the list. <code>get</code>
|
||||||
* call will always be able to find the index. In case there is few items, it will loop over </p>
|
* call will always be able to find the index. In case there is few items, it will loop over </p>
|
||||||
* <pre>
|
* <pre>
|
||||||
* element(l(range(10)), 5) => 5
|
* get(l(range(10)), 5) => 5
|
||||||
* element(l(range(10)), -1) => 9
|
* get(l(range(10)), -1) => 9
|
||||||
* element(l(range(10)), 10) => 0
|
* get(l(range(10)), 10) => 0
|
||||||
* element(l(range(10)), 93) => 3
|
* get(l(range(10)), 93) => 3
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <h3><code>put(list, index, values ...), put(list, null, values ...)</code></h3>
|
* <h3><code>put(list, index, values ...), put(list, null, values ...)</code></h3>
|
||||||
@@ -1833,11 +1843,10 @@ public class Expression implements Cloneable
|
|||||||
return LazyListValue.range(from, to, step);
|
return LazyListValue.range(from, to, step);
|
||||||
});
|
});
|
||||||
|
|
||||||
addBinaryFunction("element", (v1, v2) -> {
|
addBinaryFunction("get", (v1, v2) ->
|
||||||
|
{
|
||||||
if (v1 instanceof LazyListValue || !(v1 instanceof ListValue))
|
if (v1 instanceof LazyListValue || !(v1 instanceof ListValue))
|
||||||
{
|
|
||||||
throw new InternalExpressionException("First argument of element should be a list");
|
throw new InternalExpressionException("First argument of element should be a list");
|
||||||
}
|
|
||||||
List<Value> items = ((ListValue)v1).getItems();
|
List<Value> items = ((ListValue)v1).getItems();
|
||||||
long index = NumericValue.asNumber(v2).getLong();
|
long index = NumericValue.asNumber(v2).getLong();
|
||||||
int numitems = items.size();
|
int numitems = items.size();
|
||||||
@@ -1846,6 +1855,21 @@ public class Expression implements Cloneable
|
|||||||
index = index % numitems;
|
index = index % numitems;
|
||||||
return items.get((int)index);
|
return items.get((int)index);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Deprecated, use "get" instead
|
||||||
|
addBinaryFunction("element", (v1, v2) ->
|
||||||
|
{
|
||||||
|
if (v1 instanceof LazyListValue || !(v1 instanceof ListValue))
|
||||||
|
throw new InternalExpressionException("First argument of element should be a list");
|
||||||
|
List<Value> items = ((ListValue)v1).getItems();
|
||||||
|
long index = NumericValue.asNumber(v2).getLong();
|
||||||
|
int numitems = items.size();
|
||||||
|
long range = abs(index)/numitems;
|
||||||
|
index += (range+2)*numitems;
|
||||||
|
index = index % numitems;
|
||||||
|
return items.get((int)index);
|
||||||
|
});
|
||||||
|
|
||||||
addFunction("put", (lv) ->
|
addFunction("put", (lv) ->
|
||||||
{
|
{
|
||||||
if(lv.size()<3)
|
if(lv.size()<3)
|
||||||
@@ -2224,7 +2248,7 @@ public class Expression implements Cloneable
|
|||||||
* number('2')+number('2') => 4
|
* number('2')+number('2') => 4
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <h3><code>str(expr, params? ... )</code></h3>
|
* <h3><code>str(expr, params? ... ), str(expr, param_list)</code></h3>
|
||||||
*
|
*
|
||||||
* <p>Returns a formatted string representing expression.
|
* <p>Returns a formatted string representing expression.
|
||||||
* Accepts formatting style accepted by <code>String.format</code>.
|
* Accepts formatting style accepted by <code>String.format</code>.
|
||||||
@@ -2247,6 +2271,7 @@ public class Expression implements Cloneable
|
|||||||
* str('3bar') => null
|
* str('3bar') => null
|
||||||
* str(2)+str(2) => 22
|
* str(2)+str(2) => 22
|
||||||
* str('pi: %.2f',pi) => 'pi: 3.14'
|
* str('pi: %.2f',pi) => 'pi: 3.14'
|
||||||
|
* str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <hr>
|
* <hr>
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
package carpet.script;
|
package carpet.script;
|
||||||
|
|
||||||
|
import carpet.CarpetServer;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sole purpose of this package is to provide public access to package-private methods of Expression and CarpetExpression
|
* sole purpose of this package is to provide public access to package-private methods of Expression and CarpetExpression
|
||||||
@@ -17,4 +24,34 @@ public class ExpressionInspector
|
|||||||
{
|
{
|
||||||
return e.getName();
|
return e.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TreeSet<String> scarpetFunctions;
|
||||||
|
private static TreeSet<String> APIFunctions;
|
||||||
|
static
|
||||||
|
{
|
||||||
|
Set<String> allFunctions = (new CarpetExpression("null", null, null)).getExpr().getFunctionNames();
|
||||||
|
scarpetFunctions = new TreeSet<>(new Expression("null").getFunctionNames());
|
||||||
|
APIFunctions = new TreeSet<>(allFunctions.stream().filter(s -> !scarpetFunctions.contains(s)).collect(Collectors.toSet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> suggestFunctions(ScriptHost host, String previous, String prefix)
|
||||||
|
{
|
||||||
|
previous = previous.replace("\\'", "");
|
||||||
|
int quoteCount = StringUtils.countMatches(previous,'\'');
|
||||||
|
if (quoteCount % 2 == 1)
|
||||||
|
return Collections.emptyList();
|
||||||
|
int maxLen = prefix.length()<3 ? (prefix.length()*2+1) : 1234;
|
||||||
|
List<String> scarpetMatches = scarpetFunctions.stream().
|
||||||
|
filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> s+"(").collect(Collectors.toList());
|
||||||
|
scarpetMatches.addAll(APIFunctions.stream().
|
||||||
|
filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> s+"(").collect(Collectors.toList()));
|
||||||
|
// not that useful in commandline, more so in external scripts, so skipping here
|
||||||
|
//scarpetMatches.addAll(CarpetServer.scriptServer.events.eventHandlers.keySet().stream().
|
||||||
|
// filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> "__"+s+"(").collect(Collectors.toList()));
|
||||||
|
scarpetMatches.addAll(host.globalFunctions.keySet().stream().
|
||||||
|
filter(s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList()));
|
||||||
|
scarpetMatches.addAll(host.globalVariables.keySet().stream().
|
||||||
|
filter(s -> s.startsWith(prefix)).collect(Collectors.toList()));
|
||||||
|
return scarpetMatches;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,16 +6,25 @@ import carpet.script.exception.InternalExpressionException;
|
|||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
import net.minecraft.command.arguments.BlockArgumentParser;
|
import net.minecraft.command.arguments.BlockArgumentParser;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.util.Hand;
|
||||||
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class BlockValue extends Value
|
public class BlockValue extends Value
|
||||||
{
|
{
|
||||||
@@ -24,6 +33,7 @@ public class BlockValue extends Value
|
|||||||
private BlockState blockState;
|
private BlockState blockState;
|
||||||
private BlockPos pos;
|
private BlockPos pos;
|
||||||
private World world;
|
private World world;
|
||||||
|
private CompoundTag data;
|
||||||
|
|
||||||
public static BlockValue fromCoords(CarpetContext c, int x, int y, int z)
|
public static BlockValue fromCoords(CarpetContext c, int x, int y, int z)
|
||||||
{
|
{
|
||||||
@@ -41,7 +51,10 @@ public class BlockValue extends Value
|
|||||||
BlockArgumentParser blockstateparser = (new BlockArgumentParser(new StringReader(str), false)).parse(true);
|
BlockArgumentParser blockstateparser = (new BlockArgumentParser(new StringReader(str), false)).parse(true);
|
||||||
if (blockstateparser.getBlockState() != null)
|
if (blockstateparser.getBlockState() != null)
|
||||||
{
|
{
|
||||||
bv = new BlockValue(blockstateparser.getBlockState(), null, null);
|
CompoundTag bd = blockstateparser.getNbtData();
|
||||||
|
if (bd == null)
|
||||||
|
bd = new CompoundTag();
|
||||||
|
bv = new BlockValue(blockstateparser.getBlockState(), null, null, bd );
|
||||||
if (bvCache.size()>10000)
|
if (bvCache.size()>10000)
|
||||||
bvCache.clear();
|
bvCache.clear();
|
||||||
bvCache.put(str, bv);
|
bvCache.put(str, bv);
|
||||||
@@ -175,11 +188,44 @@ public class BlockValue extends Value
|
|||||||
throw new InternalExpressionException("Attemted to fetch blockstate without world or stored blockstate");
|
throw new InternalExpressionException("Attemted to fetch blockstate without world or stored blockstate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompoundTag getData()
|
||||||
|
{
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
if (data.isEmpty())
|
||||||
|
return null;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
if (world != null && pos != null)
|
||||||
|
{
|
||||||
|
BlockEntity be = world.getBlockEntity(pos);
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
if (be == null)
|
||||||
|
{
|
||||||
|
data = tag;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
data = be.toTag(tag);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
throw new InternalExpressionException("Attemted to fetch block data without world or stored block data");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public BlockValue(BlockState state, World world, BlockPos position)
|
public BlockValue(BlockState state, World world, BlockPos position)
|
||||||
{
|
{
|
||||||
this.world = world;
|
this.world = world;
|
||||||
blockState = state;
|
blockState = state;
|
||||||
pos = position;
|
pos = position;
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockValue(BlockState state, World world, BlockPos position, CompoundTag nbt)
|
||||||
|
{
|
||||||
|
this.world = world;
|
||||||
|
blockState = state;
|
||||||
|
pos = position;
|
||||||
|
data = nbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -239,4 +285,106 @@ public class BlockValue extends Value
|
|||||||
pitch = p;
|
pitch = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum SpecificDirection {
|
||||||
|
UP("up",0.5, 0.0, 0.5, Direction.UP),
|
||||||
|
|
||||||
|
UPNORTH ("up-north", 0.5, 0.0, 0.4, Direction.UP),
|
||||||
|
UPSOUTH ("up-south", 0.5, 0.0, 0.6, Direction.UP),
|
||||||
|
UPEAST("up-east", 0.6, 0.0, 0.5, Direction.UP),
|
||||||
|
UPWEST("up-west", 0.4, 0.0, 0.5, Direction.UP),
|
||||||
|
|
||||||
|
DOWN("down", 0.5, 1.0, 0.5, Direction.DOWN),
|
||||||
|
|
||||||
|
DOWNNORTH ("down-north", 0.5, 1.0, 0.4, Direction.DOWN),
|
||||||
|
DOWNSOUTH ("down-south", 0.5, 1.0, 0.6, Direction.DOWN),
|
||||||
|
DOWNEAST("down-east", 0.6, 1.0, 0.5, Direction.DOWN),
|
||||||
|
DOWNWEST("down-west", 0.4, 1.0, 0.5, Direction.DOWN),
|
||||||
|
|
||||||
|
|
||||||
|
NORTH ("north", 0.5, 0.4, 1.0, Direction.NORTH),
|
||||||
|
SOUTH ("south", 0.5, 0.4, 0.0, Direction.SOUTH),
|
||||||
|
EAST("east", 0.0, 0.4, 0.5, Direction.EAST),
|
||||||
|
WEST("west", 1.0, 0.4, 0.5, Direction.WEST),
|
||||||
|
|
||||||
|
NORTHUP ("north-up", 0.5, 0.6, 1.0, Direction.NORTH),
|
||||||
|
SOUTHUP ("south-up", 0.5, 0.6, 0.0, Direction.SOUTH),
|
||||||
|
EASTUP("east-up", 0.0, 0.6, 0.5, Direction.EAST),
|
||||||
|
WESTUP("west-up", 1.0, 0.6, 0.5, Direction.WEST);
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public Vec3d hitOffset;
|
||||||
|
public Direction facing;
|
||||||
|
|
||||||
|
private static final Map<String, SpecificDirection> DIRECTION_MAP = Arrays.stream(values()).collect(Collectors.toMap(SpecificDirection::getName, d -> d));
|
||||||
|
|
||||||
|
|
||||||
|
SpecificDirection(String name, double hitx, double hity, double hitz, Direction blockFacing)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.hitOffset = new Vec3d(hitx, hity, hitz);
|
||||||
|
this.facing = blockFacing;
|
||||||
|
}
|
||||||
|
private String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PlacementContext extends ItemPlacementContext {
|
||||||
|
private final Direction facing;
|
||||||
|
private final boolean sneakPlace;
|
||||||
|
|
||||||
|
public static PlacementContext from(World world, BlockPos pos, String direction, boolean sneakPlace, ItemStack itemStack)
|
||||||
|
{
|
||||||
|
SpecificDirection dir = SpecificDirection.DIRECTION_MAP.get(direction);
|
||||||
|
if (dir == null)
|
||||||
|
throw new InternalExpressionException("unknown block placement direction: "+direction);
|
||||||
|
BlockHitResult hitres = new BlockHitResult(new Vec3d(pos).add(dir.hitOffset), dir.facing, pos, false);
|
||||||
|
return new PlacementContext(world, dir.facing, sneakPlace, itemStack, hitres);
|
||||||
|
}
|
||||||
|
private PlacementContext(World world_1, Direction direction_1, boolean sneakPlace, ItemStack itemStack_1, BlockHitResult hitres) {
|
||||||
|
super(world_1, null, Hand.MAIN_HAND, itemStack_1, hitres);
|
||||||
|
this.facing = direction_1;
|
||||||
|
this.sneakPlace = sneakPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos getBlockPos() {
|
||||||
|
return this.hit.getBlockPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction getPlayerLookDirection() {
|
||||||
|
return facing.getOpposite();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction[] getPlacementDirections() {
|
||||||
|
switch(this.facing) {
|
||||||
|
case DOWN:
|
||||||
|
default:
|
||||||
|
return new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP};
|
||||||
|
case UP:
|
||||||
|
return new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
||||||
|
case NORTH:
|
||||||
|
return new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.SOUTH};
|
||||||
|
case SOUTH:
|
||||||
|
return new Direction[]{Direction.DOWN, Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.NORTH};
|
||||||
|
case WEST:
|
||||||
|
return new Direction[]{Direction.DOWN, Direction.WEST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.EAST};
|
||||||
|
case EAST:
|
||||||
|
return new Direction[]{Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.WEST};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction getPlayerFacing() {
|
||||||
|
return this.facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.facing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlayerSneaking() {
|
||||||
|
return sneakPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPlayerYaw() {
|
||||||
|
return (float)(this.facing.getHorizontal() * 90);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,35 +4,34 @@ import carpet.fakes.MobEntityInterface;
|
|||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import net.minecraft.client.network.packet.EntityVelocityUpdateS2CPacket;
|
||||||
|
import net.minecraft.command.EntitySelector;
|
||||||
|
import net.minecraft.command.EntitySelectorReader;
|
||||||
|
import net.minecraft.command.arguments.NbtPathArgumentType;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityPose;
|
import net.minecraft.entity.EntityPose;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.EquipmentSlot;
|
import net.minecraft.entity.EquipmentSlot;
|
||||||
import net.minecraft.entity.ItemEntity;
|
import net.minecraft.entity.ItemEntity;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.ai.goal.GoToWalkTargetGoal;
|
||||||
|
import net.minecraft.entity.ai.goal.Goal;
|
||||||
import net.minecraft.entity.effect.StatusEffect;
|
import net.minecraft.entity.effect.StatusEffect;
|
||||||
import net.minecraft.entity.effect.StatusEffectInstance;
|
import net.minecraft.entity.effect.StatusEffectInstance;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.entity.mob.MobEntity;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.entity.mob.MobEntityWithAi;
|
||||||
import net.minecraft.command.EntitySelector;
|
|
||||||
import net.minecraft.command.EntitySelectorReader;
|
|
||||||
import net.minecraft.command.arguments.NbtPathArgumentType;
|
|
||||||
import net.minecraft.entity.mob.MobEntityWithAi; // EntityCreature;
|
|
||||||
import net.minecraft.entity.mob.MobEntity; //LivingEntity;
|
|
||||||
import net.minecraft.entity.ai.goal.Goal;
|
|
||||||
import net.minecraft.entity.ai.goal.GoToWalkTargetGoal;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.client.network.packet.EntityVelocityUpdateS2CPacket;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.potion.Potion;
|
|
||||||
import net.minecraft.predicate.entity.EntityPredicates;
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.text.LiteralText;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
import net.minecraft.text.LiteralText;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -64,7 +63,7 @@ public class EntityValue extends Value
|
|||||||
{
|
{
|
||||||
return entitySelector.getEntities(source);
|
return entitySelector.getEntities(source);
|
||||||
}
|
}
|
||||||
entitySelector = new EntitySelectorReader(new StringReader(selector), true).build();
|
entitySelector = new EntitySelectorReader(new StringReader(selector), true).read();
|
||||||
selectorCache.put(selector, entitySelector);
|
selectorCache.put(selector, entitySelector);
|
||||||
return entitySelector.getEntities(source);
|
return entitySelector.getEntities(source);
|
||||||
}
|
}
|
||||||
@@ -104,6 +103,21 @@ public class EntityValue extends Value
|
|||||||
@Override
|
@Override
|
||||||
public Value in(Value v)
|
public Value in(Value v)
|
||||||
{
|
{
|
||||||
|
if (v instanceof ListValue)
|
||||||
|
{
|
||||||
|
List<Value> values = ((ListValue) v).getItems();
|
||||||
|
String what = values.get(0).getString();
|
||||||
|
Value arg = null;
|
||||||
|
if (values.size() == 2)
|
||||||
|
{
|
||||||
|
arg = values.get(1);
|
||||||
|
}
|
||||||
|
else if (values.size() > 2)
|
||||||
|
{
|
||||||
|
arg = ListValue.wrap(values.subList(1,values.size()));
|
||||||
|
}
|
||||||
|
return this.get(what, arg);
|
||||||
|
}
|
||||||
String what = v.getString();
|
String what = v.getString();
|
||||||
return this.get(what, null);
|
return this.get(what, null);
|
||||||
}
|
}
|
||||||
@@ -139,6 +153,7 @@ public class EntityValue extends Value
|
|||||||
put("feet", EquipmentSlot.FEET);
|
put("feet", EquipmentSlot.FEET);
|
||||||
}};
|
}};
|
||||||
private static Map<String, BiFunction<Entity, Value, Value>> featureAccessors = new HashMap<String, BiFunction<Entity, Value, Value>>() {{
|
private static Map<String, BiFunction<Entity, Value, Value>> featureAccessors = new HashMap<String, BiFunction<Entity, Value, Value>>() {{
|
||||||
|
//put("test", (e, a) -> a == null ? Value.NULL : new StringValue(a.getString()));
|
||||||
put("removed", (entity, arg) -> new NumericValue(entity.removed));
|
put("removed", (entity, arg) -> new NumericValue(entity.removed));
|
||||||
put("uuid",(e, a) -> new StringValue(e.getUuidAsString()));
|
put("uuid",(e, a) -> new StringValue(e.getUuidAsString()));
|
||||||
put("id",(e, a) -> new NumericValue(e.getEntityId()));
|
put("id",(e, a) -> new NumericValue(e.getEntityId()));
|
||||||
@@ -273,19 +288,24 @@ public class EntityValue extends Value
|
|||||||
if (where == null)
|
if (where == null)
|
||||||
throw new InternalExpressionException("Unknown inventory slot: "+a.getString());
|
throw new InternalExpressionException("Unknown inventory slot: "+a.getString());
|
||||||
if (e instanceof LivingEntity)
|
if (e instanceof LivingEntity)
|
||||||
{
|
return ListValue.fromItemStack(((LivingEntity)e).getEquippedStack(where));
|
||||||
ItemStack itemstack = ((LivingEntity)e).getEquippedStack(where);
|
|
||||||
if (!itemstack.isEmpty())
|
|
||||||
{
|
|
||||||
return ListValue.of(
|
|
||||||
new StringValue(Registry.ITEM.getId(itemstack.getItem()).getPath()),
|
|
||||||
new NumericValue(itemstack.getCount()),
|
|
||||||
new StringValue(itemstack.toTag(new CompoundTag()).toString())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Value.NULL;
|
return Value.NULL;
|
||||||
|
});
|
||||||
|
|
||||||
|
put("selected_slot", (e, a) -> {
|
||||||
|
if (e instanceof PlayerEntity)
|
||||||
|
return new NumericValue(((PlayerEntity) e).inventory.selectedSlot);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
put("facing", (e, a) -> {
|
||||||
|
int index = 0;
|
||||||
|
if (a != null)
|
||||||
|
index = (6+(int)NumericValue.asNumber(a).getLong())%6;
|
||||||
|
if (index < 0 || index > 5)
|
||||||
|
throw new InternalExpressionException("facing order should be between -6 and 5");
|
||||||
|
|
||||||
|
return new StringValue(Direction.getEntityFacingOrder(e)[index].asString());
|
||||||
});
|
});
|
||||||
|
|
||||||
put("nbt",(e, a) -> {
|
put("nbt",(e, a) -> {
|
||||||
|
|||||||
@@ -100,4 +100,10 @@ public abstract class LazyListValue extends ListValue implements Iterator<Value>
|
|||||||
throw new InternalExpressionException("Cannot add to iterators");
|
throw new InternalExpressionException("Cannot add to iterators");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Value o)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.util.registry.Registry;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -44,6 +47,19 @@ public class ListValue extends Value
|
|||||||
items = new ArrayList<>();
|
items = new ArrayList<>();
|
||||||
items.addAll(list);
|
items.addAll(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Value fromItemStack(ItemStack itemstack)
|
||||||
|
{
|
||||||
|
if (itemstack == null || itemstack.isEmpty())
|
||||||
|
return Value.NULL;
|
||||||
|
return ListValue.of(
|
||||||
|
new StringValue(Registry.ITEM.getId(itemstack.getItem()).getPath()),
|
||||||
|
new NumericValue(itemstack.getCount()),
|
||||||
|
new NBTSerializableValue(itemstack)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ListValue wrap(List<Value> list)
|
public static ListValue wrap(List<Value> list)
|
||||||
{
|
{
|
||||||
ListValue created = new ListValue();
|
ListValue created = new ListValue();
|
||||||
@@ -187,25 +203,33 @@ public class ListValue extends Value
|
|||||||
ListValue ol = (ListValue)o;
|
ListValue ol = (ListValue)o;
|
||||||
int this_size = this.getItems().size();
|
int this_size = this.getItems().size();
|
||||||
int o_size = ol.getItems().size();
|
int o_size = ol.getItems().size();
|
||||||
if (this_size != o_size)
|
if (this_size != o_size) return this_size - o_size;
|
||||||
{
|
if (this_size == 0) return 0;
|
||||||
return this_size - o_size;
|
|
||||||
}
|
|
||||||
if (this_size == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < this_size; i++)
|
for (int i = 0; i < this_size; i++)
|
||||||
{
|
{
|
||||||
int res = this.items.get(i).compareTo(ol.items.get(i));
|
int res = this.items.get(i).compareTo(ol.items.get(i));
|
||||||
if (res != 0)
|
if (res != 0) return res;
|
||||||
{
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return super.compareTo(o);
|
return getString().compareTo(o.getString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Value o)
|
||||||
|
{
|
||||||
|
if (o instanceof ListValue)
|
||||||
|
{
|
||||||
|
ListValue ol = (ListValue)o;
|
||||||
|
int this_size = this.getItems().size();
|
||||||
|
int o_size = ol.getItems().size();
|
||||||
|
if (this_size != o_size) return false;
|
||||||
|
if (this_size == 0) return true;
|
||||||
|
for (int i = 0; i < this_size; i++)
|
||||||
|
if (!this.items.get(i).equals(ol.items.get(i))) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Value> getItems()
|
public List<Value> getItems()
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
package carpet.script.value;
|
||||||
|
|
||||||
|
import carpet.script.CarpetContext;
|
||||||
|
import carpet.script.LazyValue;
|
||||||
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import com.mojang.brigadier.StringReader;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import net.minecraft.block.entity.HopperBlockEntity;
|
||||||
|
import net.minecraft.command.arguments.ItemStackArgument;
|
||||||
|
import net.minecraft.command.arguments.ItemStringReader;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.passive.VillagerEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.inventory.Inventory;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.StringNbtReader;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class NBTSerializableValue extends Value
|
||||||
|
{
|
||||||
|
private String nbtString = null;
|
||||||
|
private CompoundTag nbtTag = null;
|
||||||
|
private Supplier<CompoundTag> nbtSupplier = null;
|
||||||
|
|
||||||
|
public NBTSerializableValue(ItemStack stack)
|
||||||
|
{
|
||||||
|
nbtSupplier = () -> stack.hasTag() ? stack.getTag(): new CompoundTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBTSerializableValue(String nbtString)
|
||||||
|
{
|
||||||
|
nbtSupplier = () ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (new StringNbtReader(new StringReader(nbtString))).parseCompoundTag();
|
||||||
|
}
|
||||||
|
catch (CommandSyntaxException e)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Incorrect nbt data: "+nbtString);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBTSerializableValue(CompoundTag tag)
|
||||||
|
{
|
||||||
|
nbtTag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Entity e = ((EntityValue) v1).getEntity();
|
||||||
|
if (e instanceof PlayerEntity) inv = ((PlayerEntity) e).inventory;
|
||||||
|
else if (e instanceof Inventory) inv = (Inventory) e;
|
||||||
|
else if (e instanceof VillagerEntity) inv = ((VillagerEntity) e).getInventory();
|
||||||
|
|
||||||
|
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 = HopperBlockEntity.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 = HopperBlockEntity.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 = HopperBlockEntity.getInventoryAt(c.s.getWorld(), pos);
|
||||||
|
if (inv == null)
|
||||||
|
return null;
|
||||||
|
return new InventoryLocator(pos, pos, inv, offset + 3);
|
||||||
|
}
|
||||||
|
catch (IndexOutOfBoundsException e)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Inventory should be defined either by three coordinates, a block value, or an entity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String,ItemStackArgument> itemCache = new HashMap<>();
|
||||||
|
|
||||||
|
public static ItemStackArgument parseItem(String itemString)
|
||||||
|
{
|
||||||
|
return parseItem(itemString, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStackArgument parseItem(String itemString, CompoundTag customTag)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ItemStackArgument res = itemCache.get(itemString);
|
||||||
|
if (res != null)
|
||||||
|
if (customTag == null)
|
||||||
|
return res;
|
||||||
|
else
|
||||||
|
return new ItemStackArgument(res.getItem(), customTag);
|
||||||
|
|
||||||
|
ItemStringReader parser = (new ItemStringReader(new StringReader(itemString), false)).consume();
|
||||||
|
res = new ItemStackArgument(parser.getItem(), parser.getTag());
|
||||||
|
itemCache.put(itemString, res);
|
||||||
|
if (itemCache.size()>64000)
|
||||||
|
itemCache.clear();
|
||||||
|
if (customTag == null)
|
||||||
|
return res;
|
||||||
|
else
|
||||||
|
return new ItemStackArgument(res.getItem(), customTag);
|
||||||
|
}
|
||||||
|
catch (CommandSyntaxException e)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Incorrect item: "+itemString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int validateSlot(int slot, Inventory inv)
|
||||||
|
{
|
||||||
|
int invSize = inv.getInvSize();
|
||||||
|
if (slot < 0)
|
||||||
|
slot = invSize + slot;
|
||||||
|
if (slot < 0 || slot >= invSize)
|
||||||
|
return inv.getInvSize(); // outside of inventory
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag getTag()
|
||||||
|
{
|
||||||
|
if (nbtTag == null)
|
||||||
|
nbtTag = nbtSupplier.get();
|
||||||
|
return nbtTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Value o)
|
||||||
|
{
|
||||||
|
if (o instanceof NBTSerializableValue)
|
||||||
|
return getTag().equals(((NBTSerializableValue) o).getTag());
|
||||||
|
return super.equals(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString()
|
||||||
|
{
|
||||||
|
if (nbtString == null)
|
||||||
|
nbtString = getTag().toString();
|
||||||
|
return nbtString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBoolean()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InventoryLocator
|
||||||
|
{
|
||||||
|
public Object owner;
|
||||||
|
public BlockPos position;
|
||||||
|
public Inventory inventory;
|
||||||
|
public int offset;
|
||||||
|
InventoryLocator(Object owner, BlockPos pos, Inventory i, int o)
|
||||||
|
{
|
||||||
|
this.owner = owner;
|
||||||
|
position = pos;
|
||||||
|
inventory = i;
|
||||||
|
offset = o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -116,7 +116,7 @@ public class NumericValue extends Value
|
|||||||
{
|
{
|
||||||
return value.compareTo(((NumericValue) o).getDouble());
|
return value.compareTo(((NumericValue) o).getDouble());
|
||||||
}
|
}
|
||||||
return super.compareTo(o);
|
return getString().compareTo(o.getString());
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Value o)
|
public boolean equals(Value o)
|
||||||
|
|||||||
@@ -51,11 +51,7 @@ public class CarpetSettings
|
|||||||
@Rule(desc = "Dropping entire stacks works also from on the crafting UI result slot", category = {BUGFIX, SURVIVAL})
|
@Rule(desc = "Dropping entire stacks works also from on the crafting UI result slot", category = {BUGFIX, SURVIVAL})
|
||||||
public static boolean ctrlQCraftingFix = false;
|
public static boolean ctrlQCraftingFix = false;
|
||||||
|
|
||||||
@Rule(
|
@Rule(desc = "Parrots don't get of your shoulders until you receive proper damage", category = {SURVIVAL, FEATURE})
|
||||||
desc = "Parrots don't get of your shoulders until you receive damage",
|
|
||||||
category = {SURVIVAL, FEATURE},
|
|
||||||
validate = Validator.WIP.class
|
|
||||||
)
|
|
||||||
public static boolean persistentParrots = false;
|
public static boolean persistentParrots = false;
|
||||||
|
|
||||||
/*@Rule(
|
/*@Rule(
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public abstract class Validator<T>
|
|||||||
if (source != null)
|
if (source != null)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Messenger.m(source, "r Work in progress - limited or no functionality available for "+currentRule.name);
|
Messenger.m(source, "r "+currentRule.name+" is not yet fully implemented - we are still working on it.");
|
||||||
}
|
}
|
||||||
catch (NullPointerException ignored) { }
|
catch (NullPointerException ignored) { }
|
||||||
return newValue;
|
return newValue;
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ public class BlockInfo
|
|||||||
lst.add(Messenger.s(String.format(" - Brightness in: %.2f, above: %.2f", world.getBrightness(pos), world.getBrightness(pos.up()))));
|
lst.add(Messenger.s(String.format(" - Brightness in: %.2f, above: %.2f", world.getBrightness(pos), world.getBrightness(pos.up()))));
|
||||||
lst.add(Messenger.s(String.format(" - Is opaque: %s", material.isSolid() )));
|
lst.add(Messenger.s(String.format(" - Is opaque: %s", material.isSolid() )));
|
||||||
//lst.add(Messenger.s(String.format(" - Light opacity: %d", state.getOpacity(world,pos))));
|
//lst.add(Messenger.s(String.format(" - Light opacity: %d", state.getOpacity(world,pos))));
|
||||||
//lst.add(Messenger.s(String.format(" - Blocks light: %s", state.propagatesSkylightDown(world, pos))));
|
lst.add(Messenger.s(String.format(" - Blocks light: %s", state.getMaterial().blocksLight())));
|
||||||
//lst.add(Messenger.s(String.format(" - Emitted light: %d", state.getLightValue())));
|
//lst.add(Messenger.s(String.format(" - Emitted light: %d", state.getLightValue())));
|
||||||
//lst.add(Messenger.s(String.format(" - Picks neighbour light value: %s", state.useNeighborBrightness(world, pos))));
|
//lst.add(Messenger.s(String.format(" - Picks neighbour light value: %s", state.useNeighborBrightness(world, pos))));
|
||||||
lst.add(Messenger.s(""));
|
lst.add(Messenger.s(""));
|
||||||
|
|||||||
Reference in new issue
Block a user