Merge branch 'master' into fast_redstone

This commit is contained in:
gnembon committed 2019-07-03 12:05:18 -03:00
commit 1fce23560a
22 files changed
+1088 -134

No files matched your search

+2
View File
@@ -4,6 +4,8 @@
build/
out/
classes/
publish/
docs/
# idea
+4 -1
View File
@@ -7,7 +7,7 @@ sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
archivesBaseName = project.archives_base_name
version = project.mod_version
version = project.minecraft_version+'-'+project.mod_version
group = project.maven_group
minecraft {
@@ -76,5 +76,8 @@ publishing {
repositories {
// uncomment to publish to the local maven
// mavenLocal()
maven {
url "$projectDir/publish"
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ org.gradle.jvmargs=-Xmx1G
loader_version=0.4.8+build.155
# Mod Properties
mod_version = 0.1a
mod_version = 1.0.0+v190627
maven_group = carpet
archives_base_name = fabric-carpet
@@ -8,7 +8,9 @@ import carpet.utils.Messenger;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.BaseText;
@@ -23,6 +25,14 @@ import static net.minecraft.server.command.CommandSource.suggestMatching;
public class CarpetCommand
{
private static ParsedRule<?> getRule(CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException
{
String ruleName = StringArgumentType.getString(ctx, "rule");
ParsedRule<?> rule = SettingsManager.getRule(ruleName);
if (rule == null)
throw new SimpleCommandExceptionType(Messenger.c("rb Unknown rule: "+ruleName)).create();
return rule;
}
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
{
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal("carpet").requires((player) ->
@@ -41,30 +51,27 @@ public class CarpetCommand
suggests( (c, b)->suggestMatching(SettingsManager.getCategories(), b)).
executes( (c) -> listSettings(c.getSource(),
String.format("CarpetMod Settings matching \"%s\"", StringArgumentType.getString(c, "tag")),
CarpetServer.settingsManager.getRulesMatching(StringArgumentType.getString(c, "tag"))))));
CarpetServer.settingsManager.getRulesMatching(StringArgumentType.getString(c, "tag")))))).
then(literal("removeDefault").
requires(s -> !CarpetServer.settingsManager.locked).
then(argument("rule", StringArgumentType.word()).
suggests( (c, b) -> suggestMatching(SettingsManager.getRules().stream().map(r -> r.name), b)).
executes((c) -> removeDefault(c.getSource(), getRule(c))))).
then(literal("setDefault").
requires(s -> !CarpetServer.settingsManager.locked).
then(argument("rule", StringArgumentType.word()).
suggests( (c, b) -> suggestMatching(SettingsManager.getRules().stream().map(r -> r.name), b)).
then(argument("value", StringArgumentType.word()).
suggests((c, b)-> suggestMatching(getRule(c).options, b)).
executes((c) -> setDefault(c.getSource(), getRule(c), StringArgumentType.getString(c, "value")))))).
then(argument("rule", StringArgumentType.word()).
suggests( (c, b) -> suggestMatching(SettingsManager.getRules().stream().map(r -> r.name), b)).
requires(s -> !CarpetServer.settingsManager.locked ).
executes( (c) -> displayRuleMenu(c.getSource(),getRule(c))).
then(argument("value", StringArgumentType.word()).
suggests((c, b)-> suggestMatching(getRule(c).options,b)).
executes((c) -> setRule(c.getSource(), getRule(c), StringArgumentType.getString(c, "value")))));
for (ParsedRule<?> rule: SettingsManager.getRules())
{
literalargumentbuilder.then(literal(rule.name).executes( (context) ->
displayRuleMenu(context.getSource(),rule)));
literalargumentbuilder.then(literal("removeDefault").
requires(s -> !CarpetServer.settingsManager.locked).
then(literal(rule.name).executes((context) ->
removeDefault(context.getSource(), rule))));
literalargumentbuilder.then(literal(rule.name).
requires(s -> !CarpetServer.settingsManager.locked).
then(argument("value", StringArgumentType.word()).
suggests((c, b)-> suggestMatching(rule.options,b)).
executes((context) ->
setRule(context.getSource(), rule, StringArgumentType.getString(context, "value")))));
literalargumentbuilder.then(literal("setDefault").
requires(s -> !CarpetServer.settingsManager.locked).
then(literal(rule.name).
then(argument("value", StringArgumentType.word()).
suggests((c, b)-> suggestMatching(rule.options,b)).
executes((context) ->
setDefault(context.getSource(), rule, StringArgumentType.getString(context, "value"))))));
}
dispatcher.register(literalargumentbuilder);
}
@@ -14,6 +14,8 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
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.pattern.CachedBlockPosition;
import net.minecraft.command.arguments.BlockPosArgumentType;
@@ -31,6 +33,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -40,6 +43,29 @@ import static net.minecraft.server.command.CommandSource.suggestMatching;
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)
{
LiteralArgumentBuilder<ServerCommandSource> b = literal("globals").
@@ -50,7 +76,7 @@ public class ScriptCommand
executes( (cc) -> { CarpetServer.scriptServer.stopAll = false; return 1;});
LiteralArgumentBuilder<ServerCommandSource> l = literal("run").
requires((player) -> player.hasPermissionLevel(2)).
then(argument("expr", StringArgumentType.greedyString()).
then(argument("expr", StringArgumentType.greedyString()).suggests((cc, bb) -> suggestCode(cc, bb)).
executes((cc) -> compute(
cc,
StringArgumentType.getString(cc, "expr"))));
@@ -113,6 +139,7 @@ public class ScriptCommand
then(argument("from", BlockPosArgumentType.create()).
then(argument("to", BlockPosArgumentType.create()).
then(argument("expr", StringArgumentType.greedyString()).
suggests((cc, bb) -> suggestCode(cc, bb)).
executes( (cc) -> scriptScan(
cc,
BlockPosArgumentType.getBlockPos(cc, "origin"),
@@ -119,7 +119,7 @@ public abstract class ServerChunkManagerMixin
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
//if (entityCategory != EntityCategory.CREATURE || world.getServer().getTicks() % 400 == 0)
@@ -136,6 +136,8 @@ public abstract class ServerChunkManagerMixin
@Inject(method = "tickChunks", at = @At("RETURN"))
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)
{
for (EntityCategory cat: EntityCategory.values())
@@ -144,11 +146,15 @@ public abstract class ServerChunkManagerMixin
Pair key = Pair.of(world.dimension.getType(), cat);
int spawnTries = SpawnReporter.spawn_tries.get(cat);
if (!SpawnReporter.local_spawns.containsKey(cat))
{
if (!cat.isAnimal() || boolean_3)
{
// 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)
{
// tick spawned mobs for that type
+478 -29
View File
@@ -1,58 +1,66 @@
package carpet.script;
import carpet.CarpetServer;
import carpet.script.value.NullValue;
import carpet.settings.CarpetSettings;
import carpet.helpers.FeatureGenerator;
import carpet.fakes.MinecraftServerInterface;
import carpet.helpers.FeatureGenerator;
import carpet.script.Fluff.TriFunction;
import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.Fluff.TriFunction;
import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue;
import carpet.script.value.LazyListValue;
import carpet.script.value.ListValue;
import carpet.script.value.NBTSerializableValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.settings.CarpetSettings;
import carpet.utils.BlockInfo;
import carpet.utils.Messenger;
import com.google.common.collect.Sets;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.Block;
import net.minecraft.block.BlockPlacementEnvironment;
import net.minecraft.block.BlockState;
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.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
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.item.BlockItem;
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.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.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.util.Identifier;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.EulerRotation;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.Heightmap;
import net.minecraft.world.LightType;
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.LootContextParameters;
import org.apache.commons.lang3.tuple.Pair;
@@ -94,6 +102,7 @@ public class CarpetExpression
private ServerCommandSource source;
private BlockPos origin;
private Expression expr;
Expression getExpr() {return expr;}
private static long tickStart = 0L;
private static boolean stopAll = false;
@@ -272,12 +281,13 @@ public class CarpetExpression
* <h2>Specifying blocks</h2>
* <h3><code>block(x, y, z), block(state)</code></h3>
* <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.
* Blocks can be referenced everywhere by its simple name, but its only used in its default state</p>
* (as used by <code>/setblock</code> command), so allowing for block properties, block entity data etc.
* Blocks otherwise can be referenced everywhere by its simple string name, but its only used in its default state</p>
* <pre>
* block('air') =&gt; air
* block('iron_trapdoor[half=top]') =&gt; iron_trapdoor
* block(0,0,0) == block('bedrock') =&gt; 1
* block('hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') =&gt; hopper
* </pre>
* <h2>World Manipulation</h2>
* <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') // correct - top trapdoor
* 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>
* <h3><code>update(pos)</code></h3>
* <p>Causes a block update at position.</p>
@@ -343,7 +368,12 @@ public class CarpetExpression
* property(block('iron_trapdoor[half=top]'),'powered') =&gt; false
* bool(property(block('iron_trapdoor[half=top]'),'powered')) =&gt; 0
* </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) =&gt; '{TransferCooldown:0,x:450,y:68, ... }'
* </pre>
* <h3><code>solid(pos)</code></h3>
* <p>Boolean function, true if the block is solid</p>
* <h3> <code>air(pos)</code></h3>
@@ -539,6 +569,20 @@ public class CarpetExpression
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) ->
{
Value arg = lv.get(0).evalValue(c);
@@ -607,7 +651,8 @@ public class CarpetExpression
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);
Heightmap.Type htype;
switch (type)
@@ -709,17 +754,36 @@ public class CarpetExpression
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
}
}
if (sourceBlockState == targetBlockState)
CompoundTag data = sourceLocator.block.getData();
if (sourceBlockState == targetBlockState && data == null)
return (c_, t_) -> Value.FALSE;
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;
Value retval = new BlockValue(sourceBlockState, world, targetLocator.block.getPos());
return (c_, t_) -> retval;
});
this.expr.addLazyFunction("destroy", -1, (c, t, lv) -> {
this.expr.addLazyFunction("destroy", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerWorld world = cc.s.getWorld();
BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0);
@@ -756,8 +820,10 @@ public class CarpetExpression
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;
World world = cc.s.getWorld();
Value entityValue = lv.get(0).evalValue(cc);
@@ -781,6 +847,52 @@ public class CarpetExpression
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) ->
booleanStateTest(c, "blocks_movement", lv, (s, p) ->
!s.canPlaceAtSide(((CarpetContext) c).s.getWorld(), p, BlockPlacementEnvironment.LAND)));
@@ -809,8 +921,329 @@ public class CarpetExpression
Value retval = new StringValue(state.get(property).toString());
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') =&gt; 1
* stack_limit('ender_pearl') =&gt; 16
* stack_limit('stone') =&gt; 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()) =&gt; 41
* inventory_size(x,y,z) =&gt; 27 // chest
* inventory_size(block(pos)) =&gt; 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) =&gt; null // nothing in first hotbar slot
* inventory_get(x,y,z, 5) =&gt; ['stone', 1, {}]
* inventory_get(player(), -1) =&gt; ['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) =&gt; ['stone', 64, {}] // player had a stack of stone in first hotbar slot
* inventory_set(player(), 0, 6) =&gt; ['diamond', 64, {}] // changed stack of diamonds in player slot to 6
* inventory_set(player(), 0, 1, 'diamond_axe','{Damage:5}') =&gt; 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') =&gt; 0 // player has stone in first hotbar slot
* inventory_find(player(), null) =&gt; 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) =&gt; 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') =&gt; 1 // removed diamond from player inventory
* inventory_remove(player(), 'diamond', 100) =&gt; 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) =&gt; 1 // Q's one item on the ground
* inventory_drop(x,y,z, 0) =&gt; 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>
* <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
* <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>
* <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') &lt;=&gt; p ~ 'name' // much shorter and cleaner
* query(p, 'holds', 'offhand') &lt;=&gt; p ~ l('holds', 'offhand') // not really but can be done
* </pre>
* <h3><code>query(e,'removed')</code></h3>
* <p>Boolean. True if the entity is removed</p>
* <h3><code>query(e,'id')</code></h3>
@@ -969,6 +1408,12 @@ public class CarpetExpression
* <li><code>feet</code></li>
* </ul>
* <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>
* <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
@@ -2051,6 +2496,7 @@ public class CarpetExpression
API_BlockManipulation();
API_EntityManipulation();
API_InventoryManipulation();
API_IteratingOverAreasOfBlocks();
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,
* fill method with <code>round</code> function and draw command are equivalent</p>
* </div>
* @param host .
* @param x .
* @param y .
* @param z .
@@ -2134,12 +2581,14 @@ public class CarpetExpression
/**
* <h1><code>/script run</code> command</h1>
* <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>
* 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
* commands in a different scope</p>
* </div>
* @param host .
* @param pos .
* @return .
*/
+45 -20
View File
@@ -19,6 +19,7 @@ import carpet.script.value.ListValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.settings.CarpetSettings;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -31,6 +32,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.function.BiFunction;
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");}
private Map<String, ILazyFunction> functions = new HashMap<>();
Set<String> getFunctionNames() {return functions.keySet();}
//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
* scoping allow to organize even larger scripts</p>
* <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
* 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>
* def() -&gt; (
* expr1;
@@ -870,12 +874,14 @@ public class Expression implements Cloneable
* </pre>
* <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>
* <h3><code>exit(expr)</code></h3>
* <p>It terminates entire program passing <code>expr</code> as the result of the program execution.</p>
* <h3><code>try(expr, catch_expr(_)) ... throw(value)</code></h3>
* <h3><code>exit(expr?)</code></h3>
* <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>
* <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
* 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
* 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>
@@ -961,12 +967,14 @@ public class Expression implements Cloneable
return (cc, tt) -> new StringValue("OK");
});
addUnaryFunction("exit", (v) -> { throw new ExitStatement(v); });
addUnaryFunction("return", (v) -> { throw new ReturnStatement(v); });
addUnaryFunction("throw", (v)-> {throw new ThrowStatement(v); });
addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
addFunction("return", (lv) -> { throw new ReturnStatement(lv.size()==0?Value.NULL:lv.get(0));} );
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
{
Value retval = lv.get(0).evalValue(c, t);
@@ -974,6 +982,8 @@ public class Expression implements Cloneable
}
catch (ThrowStatement ret)
{
if (lv.size() == 1)
return (c_, t_) -> Value.NULL;
LazyValue __ = c.getVariable("_");
c.setVariable("_", (__c, __t) -> ret.retval.reboundedTo("_"));
Value val = lv.get(1).evalValue(c, t);
@@ -1613,15 +1623,15 @@ public class Expression implements Cloneable
* range(20, 10, -2) =&gt; [20, 18, 16, 14, 12]
* </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>.
* 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>
* <pre>
* element(l(range(10)), 5) =&gt; 5
* element(l(range(10)), -1) =&gt; 9
* element(l(range(10)), 10) =&gt; 0
* element(l(range(10)), 93) =&gt; 3
* get(l(range(10)), 5) =&gt; 5
* get(l(range(10)), -1) =&gt; 9
* get(l(range(10)), 10) =&gt; 0
* get(l(range(10)), 93) =&gt; 3
* </pre>
*
* <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);
});
addBinaryFunction("element", (v1, v2) -> {
if (v1 instanceof LazyListValue || !(v1 instanceof ListValue))
addBinaryFunction("get", (v1, v2) ->
{
throw new InternalExpressionException("First argument of element should be a list");
}
if (v1 instanceof LazyListValue || !(v1 instanceof ListValue))
throw new InternalExpressionException("First argument of get should be a list");
List<Value> items = ((ListValue)v1).getItems();
long index = NumericValue.asNumber(v2).getLong();
int numitems = items.size();
@@ -1846,6 +1855,21 @@ public class Expression implements Cloneable
index = index % numitems;
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 get 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) ->
{
if(lv.size()<3)
@@ -2224,7 +2248,7 @@ public class Expression implements Cloneable
* number('2')+number('2') =&gt; 4
* </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.
* Accepts formatting style accepted by <code>String.format</code>.
@@ -2247,6 +2271,7 @@ public class Expression implements Cloneable
* str('3bar') =&gt; null
* str(2)+str(2) =&gt; 22
* str('pi: %.2f',pi) =&gt; 'pi: 3.14'
* str('player at: %d %d %d',pos(player())) =&gt; 'player at: 567, -2423, 124'
* </pre>
*
* <hr>
@@ -1,6 +1,13 @@
package carpet.script;
import carpet.CarpetServer;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
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
@@ -17,4 +24,34 @@ public class ExpressionInspector
{
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.exceptions.CommandSyntaxException;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.command.arguments.BlockArgumentParser;
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.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class BlockValue extends Value
{
@@ -24,6 +33,7 @@ public class BlockValue extends Value
private BlockState blockState;
private BlockPos pos;
private World world;
private CompoundTag data;
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);
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)
bvCache.clear();
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");
}
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)
{
this.world = world;
blockState = state;
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,112 @@ public class BlockValue extends Value
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;
}
@Override
public BlockPos getBlockPos() {
return this.hit.getBlockPos();
}
@Override
public Direction getPlayerLookDirection() {
return facing.getOpposite();
}
@Override
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};
}
}
@Override
public Direction getPlayerFacing() {
return this.facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.facing;
}
@Override
public boolean isPlayerSneaking() {
return sneakPlace;
}
@Override
public float getPlayerYaw() {
return (float)(this.facing.getHorizontal() * 90);
}
}
}
@@ -4,35 +4,34 @@ import carpet.fakes.MobEntityInterface;
import carpet.script.exception.InternalExpressionException;
import com.mojang.brigadier.StringReader;
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.EntityPose;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.ItemEntity;
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.StatusEffectInstance;
import net.minecraft.nbt.Tag;
import net.minecraft.server.command.ServerCommandSource;
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.mob.MobEntity;
import net.minecraft.entity.mob.MobEntityWithAi;
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.client.network.packet.EntityVelocityUpdateS2CPacket;
import net.minecraft.potion.Potion;
import net.minecraft.nbt.Tag;
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.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.text.LiteralText;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
@@ -64,7 +63,7 @@ public class EntityValue extends Value
{
return entitySelector.getEntities(source);
}
entitySelector = new EntitySelectorReader(new StringReader(selector), true).build();
entitySelector = new EntitySelectorReader(new StringReader(selector), true).read();
selectorCache.put(selector, entitySelector);
return entitySelector.getEntities(source);
}
@@ -104,6 +103,21 @@ public class EntityValue extends Value
@Override
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();
return this.get(what, null);
}
@@ -139,6 +153,7 @@ public class EntityValue extends Value
put("feet", EquipmentSlot.FEET);
}};
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("uuid",(e, a) -> new StringValue(e.getUuidAsString()));
put("id",(e, a) -> new NumericValue(e.getEntityId()));
@@ -273,19 +288,24 @@ public class EntityValue extends Value
if (where == null)
throw new InternalExpressionException("Unknown inventory slot: "+a.getString());
if (e instanceof LivingEntity)
{
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 ListValue.fromItemStack(((LivingEntity)e).getEquippedStack(where));
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) -> {
@@ -100,4 +100,10 @@ public abstract class LazyListValue extends ListValue implements Iterator<Value>
throw new InternalExpressionException("Cannot add to iterators");
}
@Override
public boolean equals(final Value o)
{
return false;
}
}
@@ -1,6 +1,9 @@
package carpet.script.value;
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.Arrays;
@@ -44,6 +47,19 @@ public class ListValue extends Value
items = new ArrayList<>();
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)
{
ListValue created = new ListValue();
@@ -187,25 +203,33 @@ public class ListValue extends Value
ListValue ol = (ListValue)o;
int this_size = this.getItems().size();
int o_size = ol.getItems().size();
if (this_size != o_size)
{
return this_size - o_size;
}
if (this_size == 0)
{
return 0;
}
if (this_size != o_size) return this_size - o_size;
if (this_size == 0) return 0;
for (int i = 0; i < this_size; i++)
{
int res = this.items.get(i).compareTo(ol.items.get(i));
if (res != 0)
{
return res;
}
if (res != 0) return res;
}
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()
@@ -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 super.compareTo(o);
return getString().compareTo(o.getString());
}
@Override
public boolean equals(Value o)
@@ -25,7 +25,7 @@ import static carpet.settings.RuleCategory.SURVIVAL;
public class CarpetSettings
{
public static final String carpetVersion = "v19_06_24alpha";
public static final String carpetVersion = "v190627";
public static final Logger LOG = LogManager.getLogger();
public static boolean skipGenerationChecks = false;
public static boolean impendingFillSkipUpdates = false;
@@ -51,11 +51,7 @@ public class CarpetSettings
@Rule(desc = "Dropping entire stacks works also from on the crafting UI result slot", category = {BUGFIX, SURVIVAL})
public static boolean ctrlQCraftingFix = false;
@Rule(
desc = "Parrots don't get of your shoulders until you receive damage",
category = {SURVIVAL, FEATURE},
validate = Validator.WIP.class
)
@Rule(desc = "Parrots don't get of your shoulders until you receive proper damage", category = {SURVIVAL, FEATURE})
public static boolean persistentParrots = false;
/*@Rule(
@@ -117,7 +117,7 @@ public final class ParsedRule<T> implements Comparable<ParsedRule> {
if (!value.equals(get()))
{
this.field.set(null, value);
SettingsManager.notifyRuleChanged(this, stringValue);
SettingsManager.notifyRuleChanged(source, this, stringValue);
}
}
catch (IllegalAccessException e)
+1 -1
View File
@@ -17,7 +17,7 @@ import java.lang.annotation.Target;
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Rule
public @interface Rule
{
/**
* The rule name, by default the same as the field name
@@ -7,6 +7,7 @@ import net.minecraft.server.network.ServerPlayerEntity;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.TriConsumer;
import java.io.BufferedReader;
import java.io.File;
@@ -25,7 +26,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
public class SettingsManager
@@ -34,7 +34,7 @@ public class SettingsManager
private static Map<String, ParsedRule<?>> rules = new HashMap<>();
public boolean locked;
private MinecraftServer server;
private static List<BiConsumer<ParsedRule<?>, String>> observers = new ArrayList<>();
private static List<TriConsumer<ServerCommandSource, ParsedRule<?>, String>> observers = new ArrayList<>();
public SettingsManager(MinecraftServer server)
{
@@ -53,14 +53,14 @@ public class SettingsManager
}
}
public static void addRuleObserver(BiConsumer<ParsedRule<?>, String> observer)
public static void addRuleObserver(TriConsumer<ServerCommandSource, ParsedRule<?>, String> observer)
{
observers.add(observer);
}
static void notifyRuleChanged(ParsedRule<?> rule, String userTypedValue)
static void notifyRuleChanged(ServerCommandSource source, ParsedRule<?> rule, String userTypedValue)
{
observers.forEach(observer -> observer.accept(rule, userTypedValue));
observers.forEach(observer -> observer.accept(source, rule, userTypedValue));
}
public static Iterable<String> getCategories()
@@ -71,7 +71,7 @@ public class SettingsManager
}
public ParsedRule getRule(String name)
public static ParsedRule<?> getRule(String name)
{
return rules.get(name);
}
+1 -1
View File
@@ -47,7 +47,7 @@ public abstract class Validator<T>
if (source != null)
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) { }
return newValue;
+1 -1
View File
@@ -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(" - Is opaque: %s", material.isSolid() )));
//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(" - Picks neighbour light value: %s", state.useNeighborBrightness(world, pos))));
lst.add(Messenger.s(""));
+1 -1
View File
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"id": "carpet",
"version": "0.1",
"version": "${version}",
"name": "Carpet Mod in Fabric",
"description": "Carpet made out of fabric",