Merge branch 'master' into 1.15

# Conflicts:
#	src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java
#	src/main/java/carpet/script/CarpetExpression.java
This commit is contained in:
gnembon committed 2019-10-08 02:07:45 -03:00
commit dc9532cf05
24 files changed
+1695 -655

No files matched your search

@@ -233,7 +233,7 @@ public class ScriptCommand
executes( (cc) -> listEvents(cc.getSource())).
then(literal("add_to").
then(argument("event", StringArgumentType.word()).
suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.keySet() ,bb)).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)).
then(argument("call", StringArgumentType.word()).
suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)).
executes( (cc) -> CarpetServer.scriptServer.events.addEvent(
@@ -253,9 +253,9 @@ public class ScriptCommand
)?1:0)))))).
then(literal("remove_from").
then(argument("event", StringArgumentType.word()).
suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.keySet() ,bb)).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)).
then(argument("call", StringArgumentType.greedyString()).
suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.get(StringArgumentType.getString(cc, "event")).callList.stream().map(CarpetEventServer.Callback::toString), bb)).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.get(StringArgumentType.getString(cc, "event")).handler.callList.stream().map(CarpetEventServer.Callback::toString), bb)).
executes( (cc) -> CarpetServer.scriptServer.events.removeEvent(
StringArgumentType.getString(cc, "event"),
StringArgumentType.getString(cc, "call")
@@ -293,14 +293,14 @@ public class ScriptCommand
private static int listEvents(ServerCommandSource source)
{
Messenger.m(source, "w Lists ALL event handlers:");
for (String event: CarpetServer.scriptServer.events.eventHandlers.keySet())
for (CarpetEventServer.Event event: CarpetEventServer.Event.values())
{
boolean shownEvent = false;
for (CarpetEventServer.Callback c: CarpetServer.scriptServer.events.eventHandlers.get(event).callList)
for (CarpetEventServer.Callback c: event.handler.callList)
{
if (!shownEvent)
{
Messenger.m(source, "w Handlers for "+event+": ");
Messenger.m(source, "w Handlers for "+event.name+": ");
shownEvent = true;
}
Messenger.m(source, "w - "+c.udf+(c.host==null?"":" (from "+c.host+")"));
@@ -14,6 +14,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.BooleanSupplier;
import static carpet.script.CarpetEventServer.Event.ENDER_TICK;
import static carpet.script.CarpetEventServer.Event.NETHER_TICK;
import static carpet.script.CarpetEventServer.Event.TICK;
@Mixin(MinecraftServer.class)
public abstract class MinecraftServer_scarpetMixin extends NonBlockingThreadExecutor<ServerTask> implements MinecraftServerInterface
{
@@ -44,6 +48,8 @@ public abstract class MinecraftServer_scarpetMixin extends NonBlockingThreadExec
))
public void tickTasks(BooleanSupplier booleanSupplier_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onTick();
TICK.onTick();
NETHER_TICK.onTick();
ENDER_TICK.onTick();
}
}
@@ -1,6 +1,6 @@
package carpet.mixins;
import carpet.CarpetServer;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.network.packet.ClientCommandC2SPacket;
@@ -16,8 +16,25 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static carpet.script.CarpetEventServer.Event.PLAYER_ATTACKS_ENTITY;
import static carpet.script.CarpetEventServer.Event.PLAYER_CLICKS_BLOCK;
import static carpet.script.CarpetEventServer.Event.PLAYER_DEPLOYS_ELYTRA;
import static carpet.script.CarpetEventServer.Event.PLAYER_INERACTSW_WITH_ENTITY;
import static carpet.script.CarpetEventServer.Event.PLAYER_RELEASED_ITEM;
import static carpet.script.CarpetEventServer.Event.PLAYER_RIDES;
import static carpet.script.CarpetEventServer.Event.PLAYER_JUMPS;
import static carpet.script.CarpetEventServer.Event.PLAYER_RIGHT_CLICKS_BLOCK;
import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SNEAKING;
import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SPRINTING;
import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SNEAKING;
import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SPRINTING;
import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM;
import static carpet.script.CarpetEventServer.Event.PLAYER_WAKES_UP;
@Mixin(ServerPlayNetworkHandler.class)
public class ServerPlayNetworkHandler_scarpetEventsMixin
{
@@ -26,9 +43,9 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
@Inject(method = "onPlayerInput", at = @At(value = "RETURN"))
private void checkMoves(PlayerInputC2SPacket p, CallbackInfo ci)
{
if (p.getSideways() != 0.0F || p.getForward() != 0.0F || p.isJumping() || p.isSneaking())
if (PLAYER_RIDES.isNeeded() && (p.getSideways() != 0.0F || p.getForward() != 0.0F || p.isJumping() || p.isSneaking()))
{
CarpetServer.scriptServer.events.onMountControls(player, p.getSideways(), p.getForward(), p.isJumping(), p.isSneaking());
PLAYER_RIDES.onMountControls(player, p.getSideways(), p.getForward(), p.isJumping(), p.isSneaking());
}
}
@@ -38,17 +55,37 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onJump(PlayerMoveC2SPacket playerMoveC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onPlayerJumped(player);
PLAYER_JUMPS.onPlayerEvent(player);
}
@Inject(method = "onPlayerAction", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;processBlockBreakingAction(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/server/network/packet/PlayerActionC2SPacket$Action;Lnet/minecraft/util/math/Direction;I)V"
target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;processBlockBreakingAction(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/server/network/packet/PlayerActionC2SPacket$Action;Lnet/minecraft/util/math/Direction;I)V",
shift = At.Shift.AFTER
))
private void onClicked(PlayerActionC2SPacket packet, CallbackInfo ci)
{
if (packet.getAction() == PlayerActionC2SPacket.Action.START_DESTROY_BLOCK)
CarpetServer.scriptServer.events.onBlockClicked(player, packet.getPos(), packet.getDirection());
PLAYER_CLICKS_BLOCK.onBlockAction(player, packet.getPos(), packet.getDirection());
}
@Redirect(method = "onPlayerAction", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/network/ServerPlayerEntity;stopUsingItem()V"
))
private void onStopUsing(ServerPlayerEntity serverPlayerEntity)
{
if (PLAYER_RELEASED_ITEM.isNeeded())
{
Hand hand = serverPlayerEntity.getActiveHand();
ItemStack stack = serverPlayerEntity.getActiveItem().copy();
serverPlayerEntity.stopUsingItem();
PLAYER_RELEASED_ITEM.onItemAction(player, hand, stack);
}
else
{
serverPlayerEntity.stopUsingItem();
}
}
@Inject(method = "onPlayerInteractBlock", at = @At(
@@ -57,9 +94,12 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onBlockInteracted(PlayerInteractBlockC2SPacket playerInteractBlockC2SPacket_1, CallbackInfo ci)
{
Hand hand = playerInteractBlockC2SPacket_1.getHand();
BlockHitResult hitRes = playerInteractBlockC2SPacket_1.getHitY();
CarpetServer.scriptServer.events.onRightClickBlock(player, hand, hitRes );
if (PLAYER_RIGHT_CLICKS_BLOCK.isNeeded())
{
Hand hand = playerInteractBlockC2SPacket_1.getHand();
BlockHitResult hitRes = playerInteractBlockC2SPacket_1.getHitY();
PLAYER_RIGHT_CLICKS_BLOCK.onBlockHit(player, hand, hitRes);
}
}
@Inject(method = "onPlayerInteractItem", at = @At(
@@ -68,7 +108,11 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onItemClicked(PlayerInteractItemC2SPacket playerInteractItemC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onRightClick(player, playerInteractItemC2SPacket_1.getHand());
if (PLAYER_USES_ITEM.isNeeded())
{
Hand hand = playerInteractItemC2SPacket_1.getHand();
PLAYER_USES_ITEM.onItemAction(player, hand, player.getStackInHand(hand).copy());
}
}
@Inject(method = "onClientCommand", at = @At(
@@ -78,7 +122,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onStartSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onStartSneaking(player);
PLAYER_STARTS_SNEAKING.onPlayerEvent(player);
}
@Inject(method = "onClientCommand", at = @At(
@@ -88,7 +132,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onStopSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onStopSneaking(player);
PLAYER_STOPS_SNEAKING.onPlayerEvent(player);
}
@Inject(method = "onClientCommand", at = @At(
@@ -98,7 +142,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onStartSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onStartSprinting(player);
PLAYER_STARTS_SPRINTING.onPlayerEvent(player);
}
@Inject(method = "onClientCommand", at = @At(
@@ -108,7 +152,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onStopSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onStopSprinting(player);
PLAYER_STOPS_SPRINTING.onPlayerEvent(player);
}
@Inject(method = "onClientCommand", at = @At(
@@ -119,7 +163,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
private void onWakeUp(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci)
{
//weird one - doesn't seem to work, maybe MP
CarpetServer.scriptServer.events.onOutOfBed(player);
PLAYER_WAKES_UP.onPlayerEvent(player);
}
@Inject(method = "onClientCommand", at = @At(
@@ -129,7 +173,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onElytraEngage(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onElytraDeploy(player);
PLAYER_DEPLOYS_ELYTRA.onPlayerEvent(player);
}
@Inject(method = "onPlayerInteractEntity", at = @At(
@@ -138,7 +182,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
))
private void onEntityInteract(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci)
{
CarpetServer.scriptServer.events.onEntityInteracted(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand());
PLAYER_INERACTSW_WITH_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand());
}
@Inject(method = "onPlayerInteractEntity", at = @At(
@@ -148,6 +192,6 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
private void onEntityAttack(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci)
{
//todo add hit and hand in the future
CarpetServer.scriptServer.events.onEntityAttacked(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()));
PLAYER_ATTACKS_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null);
}
}
@@ -0,0 +1,46 @@
package carpet.mixins;
import com.mojang.authlib.GameProfile;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM;
@Mixin(ServerPlayerEntity.class)
public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
{
public ServerPlayerEntity_scarpetEventMixin(World world_1, GameProfile gameProfile_1)
{
super(world_1, gameProfile_1);
}
@Shadow protected abstract void method_6040();
@Redirect(method = "method_6040", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/entity/player/PlayerEntity;method_6040()V"
))
private void finishedUsingItem(PlayerEntity playerEntity)
{
if (PLAYER_FINISHED_USING_ITEM.isNeeded())
{
Hand hand = getActiveHand();
ItemStack stack = getActiveItem().copy();
// do vanilla
super.method_6040();
PLAYER_FINISHED_USING_ITEM.onItemAction((ServerPlayerEntity) (Object)this, hand, stack);
}
else
{
// do vanilla
super.method_6040();
}
}
}
@@ -1,6 +1,5 @@
package carpet.mixins;
import carpet.CarpetServer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
@@ -14,6 +13,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import static carpet.script.CarpetEventServer.Event.PLAYER_BREAK_BLOCK;
@Mixin(ServerPlayerInteractionManager.class)
public class ServerPlayerInteractionManager_scarpetEventsMixin
{
@@ -26,6 +27,6 @@ public class ServerPlayerInteractionManager_scarpetEventsMixin
))
private void onBlockBroken(BlockPos blockPos_1, CallbackInfoReturnable<Boolean> cir, BlockState blockState_1, BlockEntity be, Block b, boolean boolean_1)
{
CarpetServer.scriptServer.events.onBlockBroken(player, blockPos_1, blockState_1);
PLAYER_BREAK_BLOCK.onBlockBroken(player, blockPos_1, blockState_1);
}
}
+264 -168
View File
@@ -7,13 +7,10 @@ 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 net.minecraft.block.BlockState;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
@@ -21,9 +18,6 @@ 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 net.minecraft.world.dimension.DimensionType;
import java.util.ArrayList;
import java.util.Arrays;
@@ -36,10 +30,11 @@ import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class CarpetEventServer
{
public List<ScheduledCall> scheduledCalls = new LinkedList<>();
public static class Callback
{
public String host;
@@ -146,9 +141,262 @@ public class CarpetEventServer
}
}
public Map<String, CallbackList> eventHandlers = new HashMap<>();
public enum Event
{
TICK("tick", new CallbackList(0))
{
@Override
public void onTick()
{
handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource);
}
},
NETHER_TICK("tick_nether",new CallbackList(0))
{
@Override
public void onTick()
{
handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource);
}
},
ENDER_TICK("tick_ender",new CallbackList(0))
{
@Override
public void onTick()
{
handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource);
}
},
PLAYER_JUMPS("player_jumps", new CallbackList(1))
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_DEPLOYS_ELYTRA("player_deploys_elytra",new CallbackList(1))
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_WAKES_UP("player_wakes_up",new CallbackList(1))
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_RIDES("player_rides",new CallbackList(5))
{
@Override
public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking)
{
handler.call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new NumericValue(forwardSpeed)),
((c, t) -> new NumericValue(strafeSpeed)),
((c, t) -> new NumericValue(jumping)),
((c, t) -> new NumericValue(sneaking))
), player::getCommandSource);
}
},
PLAYER_USES_ITEM("player_uses_item",new CallbackList(3))
{
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack)
{
handler.call( () ->
{
//ItemStack itemstack = player.getStackInHand(enumhand);
return Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> ListValue.fromItemStack(itemstack)),
((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"))
);
}, player::getCommandSource);
}
},
PLAYER_CLICKS_BLOCK("player_clicks_block",new CallbackList(3))
{
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack)
{
handler.call( () ->
{
return Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> ListValue.fromItemStack(itemstack)),
((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"))
);
}, player::getCommandSource);
}
},
PLAYER_RIGHT_CLICKS_BLOCK("player_right_clicks_block",new CallbackList(6))
{
@Override
public void onBlockHit(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d)
{
handler.call( () ->
{
ItemStack itemstack = player.getStackInHand(enumhand);
BlockPos blockpos = hitRes.getBlockPos();
Direction enumfacing = hitRes.getSide();
Vec3d vec3d = hitRes.getPos().subtract(blockpos.getX(), blockpos.getY(), blockpos.getZ());
return Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> ListValue.fromItemStack(itemstack)),
((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")),
((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)),
((c, t) -> new StringValue(enumfacing.getName())),
((c, t) -> ListValue.of(
new NumericValue(vec3d.x),
new NumericValue(vec3d.y),
new NumericValue(vec3d.z)
))
);
}, player::getCommandSource);
}
},
PLAYER_BREAK_BLOCK("player_breaks_block",new CallbackList(2))
{
@Override
public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS)
{
handler.call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new BlockValue(previousBS, player.getServerWorld(), pos))
), player::getCommandSource);
}
},
PLAYER_INERACTSW_WITH_ENTITY("player_interacts_with_entity",new CallbackList(3))
{
@Override
public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand)
{
handler.call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new EntityValue(entity)),
((c, t) -> new StringValue(enumhand==Hand.MAIN_HAND?"mainhand":"offhand"))
), player::getCommandSource);
}
},
PLAYER_ATTACKS_ENTITY("player_attacks_entity",new CallbackList(2))
{
@Override
public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand)
{
handler.call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new EntityValue(entity))
), player::getCommandSource);
}
},
PLAYER_STARTS_SNEAKING("player_starts_sneaking",new CallbackList(1))
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_STOPS_SNEAKING("player_stops_sneaking",new CallbackList(1))
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_STARTS_SPRINTING("player_starts_sprinting",new CallbackList(1))
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_STOPS_SPRINTING("player_stops_sprinting",new CallbackList(1))
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
public List<ScheduledCall> scheduledCalls = new LinkedList<>();
PLAYER_RELEASED_ITEM("player_releases_item",new CallbackList(3))
{
@Override
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack)
{
// this.getStackInHand(this.getActiveHand()), this.activeItemStack)
handler.call( () ->
{
return Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> ListValue.fromItemStack(itemstack)),
((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"))
);
}, player::getCommandSource);
}
},
PLAYER_FINISHED_USING_ITEM("player_finishes_using_item",new CallbackList(3))
{
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack)
{
// this.getStackInHand(this.getActiveHand()), this.activeItemStack)
handler.call( () ->
{
return Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> ListValue.fromItemStack(itemstack)),
((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"))
);
}, player::getCommandSource);
}
};
// on projectile thrown (arrow from bows, crossbows, tridents, snoballs, e-pearls
public String name;
public static Map<String, Event> byName = new HashMap<String, Event>(){{
for (Event e: Event.values())
{
put(e.name, e);
}
}};
public CallbackList handler;
Event(String name, CallbackList eventHandler)
{
this.name = name;
this.handler = eventHandler;
}
public boolean isNeeded()
{
return handler.callList.size() > 0;
}
//stubs for calls just to ease calls in vanilla code so they don't need to deal with scarpet value types
public void onTick() { }
public void onPlayerEvent(ServerPlayerEntity player) { }
public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { }
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { }
public void onBlockAction(ServerPlayerEntity player, BlockPos blockpos, Direction facing) { }
public void onBlockHit(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes) { }
public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) { }
public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { }
}
public CarpetEventServer()
{
for (Event e: Event.values())
e.handler.callList.clear();
}
public void tick()
{
@@ -175,50 +423,28 @@ public class CarpetEventServer
scheduledCalls.add(new ScheduledCall(context, function, args, due));
}
public CarpetEventServer()
{
eventHandlers.put("tick",new CallbackList(0));
eventHandlers.put("tick_nether",new CallbackList(0));
eventHandlers.put("tick_ender",new CallbackList(0));
eventHandlers.put("player_jumps",new CallbackList(1));
eventHandlers.put("player_deploys_elytra",new CallbackList(1));
eventHandlers.put("player_wakes_up",new CallbackList(1));
eventHandlers.put("player_rides",new CallbackList(5));
eventHandlers.put("player_uses_item",new CallbackList(3));
eventHandlers.put("player_clicks_block",new CallbackList(3));
eventHandlers.put("player_right_clicks_block",new CallbackList(6));
eventHandlers.put("player_breaks_block",new CallbackList(2));
eventHandlers.put("player_interacts_with_entity",new CallbackList(3));
eventHandlers.put("player_attacks_entity",new CallbackList(2));
eventHandlers.put("player_starts_sneaking",new CallbackList(1));
eventHandlers.put("player_stops_sneaking",new CallbackList(1));
eventHandlers.put("player_starts_sprinting",new CallbackList(1));
eventHandlers.put("player_stops_sprinting",new CallbackList(1));
}
public boolean addEvent(String event, String host, String funName)
{
if (!eventHandlers.containsKey(event))
if (!Event.byName.containsKey(event))
{
return false;
}
return eventHandlers.get(event).addEventCall(host, funName);
return Event.byName.get(event).handler.addEventCall(host, funName);
}
public boolean removeEvent(String event, String funName)
{
if (!eventHandlers.containsKey(event))
if (!Event.byName.containsKey(event))
return false;
Callback callback= decodeCallback(funName);
eventHandlers.get(event).removeEventCall(callback.host, callback.udf);
Event.byName.get(event).handler.removeEventCall(callback.host, callback.udf);
return true;
}
public void removeAllHostEvents(String hostName)
{
eventHandlers.forEach((e, a) -> a.removeAllCalls(hostName));
Event.byName.values().forEach((e) -> e.handler.removeAllCalls(hostName));
}
private Callback decodeCallback(String funName)
@@ -235,7 +461,7 @@ public class CarpetEventServer
private ScheduledCall makeEventCall(CarpetContext cc, String function, List<Value> extraArgs, int argCount)
{
UserDefinedFunction udf = cc.host.globalFunctions.get(function);
if (udf == null || (udf.getArguments().size()-extraArgs.size()) != argCount)
if (udf == null || (udf.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argCount)
{
// call won't match arguments
return null;
@@ -250,131 +476,6 @@ public class CarpetEventServer
return new ScheduledCall(cc, function, lazyArgs, 0);
}
public void onTick()
{
eventHandlers.get("tick").call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource);
eventHandlers.get("tick_nether").call(Collections::emptyList, () ->
CarpetServer.minecraft_server.getCommandSource().withWorld(CarpetServer.minecraft_server.getWorld(DimensionType.THE_NETHER)));
eventHandlers.get("tick_ender").call(Collections::emptyList, () ->
CarpetServer.minecraft_server.getCommandSource().withWorld(CarpetServer.minecraft_server.getWorld(DimensionType.THE_END)));
}
public void onPlayerJumped(PlayerEntity player)
{
eventHandlers.get("player_jumps").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
public void onElytraDeploy(ServerPlayerEntity player)
{
eventHandlers.get("player_deploys_elytra").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
public void onOutOfBed(ServerPlayerEntity player)
{
eventHandlers.get("player_wakes_up").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
public void onMountControls(PlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking)
{
eventHandlers.get("player_rides").call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new NumericValue(forwardSpeed)),
((c, t) -> new NumericValue(strafeSpeed)),
((c, t) -> new NumericValue(jumping)),
((c, t) -> new NumericValue(sneaking))
), player::getCommandSource);
}
public void onRightClick(ServerPlayerEntity player, Hand enumhand)
{
eventHandlers.get("player_uses_item").call( () ->
{
ItemStack itemstack = player.getStackInHand(enumhand);
return Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> ListValue.fromItemStack(itemstack)),
((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand"))
);
}, player::getCommandSource);
}
public void onBlockClicked(ServerPlayerEntity player, BlockPos blockpos, Direction facing)
{
eventHandlers.get("player_clicks_block").call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)),
((c, t) -> new StringValue(facing.getName()))
), player::getCommandSource);
}
public void onRightClickBlock(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d)
{
eventHandlers.get("player_right_clicks_block").call( () ->
{
ItemStack itemstack = player.getStackInHand(enumhand);
BlockPos blockpos = hitRes.getBlockPos();
Direction enumfacing = hitRes.getSide();
Vec3d vec3d = hitRes.getPos().subtract(blockpos.getX(), blockpos.getY(), blockpos.getZ());
return Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> ListValue.fromItemStack(itemstack)),
((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")),
((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)),
((c, t) -> new StringValue(enumfacing.getName())),
((c, t) -> ListValue.of(
new NumericValue(vec3d.x),
new NumericValue(vec3d.y),
new NumericValue(vec3d.z)
))
);
}, player::getCommandSource);
}
public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS)
{
eventHandlers.get("player_breaks_block").call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new BlockValue(previousBS, player.getServerWorld(), pos))
), player::getCommandSource);
}
public void onEntityInteracted(ServerPlayerEntity player, Entity entity, Hand enumhand)
{
eventHandlers.get("player_interacts_with_entity").call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new EntityValue(entity)),
((c, t) -> new StringValue(enumhand==Hand.MAIN_HAND?"mainhand":"offhand"))
), player::getCommandSource);
}
public void onEntityAttacked(ServerPlayerEntity player, Entity entity)
{
eventHandlers.get("player_attacks_entity").call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new EntityValue(entity))
), player::getCommandSource);
}
public void onStartSneaking(ServerPlayerEntity player)
{
eventHandlers.get("player_starts_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
public void onStopSneaking(ServerPlayerEntity player)
{
eventHandlers.get("player_stops_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
public void onStartSprinting(ServerPlayerEntity player)
{
eventHandlers.get("player_starts_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
public void onStopSprinting(ServerPlayerEntity player)
{
eventHandlers.get("player_stops_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
public ScheduledCall makeDeathCall(CarpetContext cc, String function, List<Value> extraArgs)
{
return makeEventCall(cc, function, extraArgs, 2);
@@ -389,11 +490,9 @@ public class CarpetEventServer
}
public ScheduledCall makeDamageCall(CarpetContext cc, String function, List<Value> extraArgs)
{
return makeEventCall(cc, function, extraArgs, 3);
return makeEventCall(cc, function, extraArgs, 4);
}
public void onEntityDeath(ScheduledCall call, Entity e, String reason)
{
call.execute( Arrays.asList( (c, t)-> new EntityValue(e), (c, t)-> new StringValue(reason)));
@@ -415,7 +514,4 @@ public class CarpetEventServer
(c, t) -> source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker())
));
}
}
+111 -26
View File
@@ -100,7 +100,7 @@ import static java.lang.Math.sqrt;
/**
* <h1>Minecraft specific API and <code>scarpet</code> language add-ons and commands</h1>
* <p>Here is the gist of the Minecraft related functions. Otherwise the CarpetScript could live without Minecraft.</p>
* <h2>Dimension issues</h2>
* <h2>Dimension warning</h2>
* <p>One note, which is important is that most of the calls for entities and blocks
* would refer to the current dimension of the caller, meaning, that if we for example
* list all the players using <code>player('all')</code> function, if a player is in the
@@ -109,7 +109,8 @@ import static java.lang.Math.sqrt;
* refer to the overworld blocks and entities.
* In case you would want to run commands across all dimensions, just run three of them, using
* <code>/execute in overworld/the_nether/the_end run script run ...</code> and query
* players using <code>player('*')</code>, which only returns players in current dimension.</p>
* players using <code>player('*')</code>, which only returns players in current dimension, or use
* <code>in_dimension(expr)</code> function.</p>
*/
public class CarpetExpression
{
@@ -348,7 +349,7 @@ public class CarpetExpression
* <h1>Blocks / World API</h1>
* <div style="padding-left: 20px; border-radius: 5px 45px; border:1px solid grey;">
* <h2>Specifying blocks</h2>
* <h3><code>block(x, y, z), block(state)</code></h3>
* <h3><code>block(x, y, z), block(l(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, block entity data etc.
* Blocks otherwise can be referenced everywhere by its simple string name, but its only used in its default state</p>
@@ -401,6 +402,8 @@ public class CarpetExpression
* 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>biome(pos)</code></h3>
* <p>returns biome at that block position.</p>
* <h3><code>set_biome(pos, biome_name)</code></h3>
* <p>changes biome at that block position.</p>
* <h3><code>update(pos)</code></h3>
@@ -998,6 +1001,16 @@ public class CarpetExpression
return (_c, _t) -> res;
});
this.expr.addLazyFunction("biome", -1, (c, t, lv) -> {
CarpetContext cc = (CarpetContext)c;
BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0);
ServerWorld world = cc.s.getWorld();
BlockPos pos = locator.block.getPos();
Biome biome = world.getBiome(pos);
Value res = new StringValue(biome.getTranslationKey().replaceFirst("^biome\\.minecraft\\.", ""));
return (_c, _t) -> res;
});
this.expr.addLazyFunction("set_biome", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
@@ -1179,6 +1192,8 @@ public class CarpetExpression
Value nbtValue = lv.get(inventoryLocator.offset+3).evalValue(c);
if (nbtValue instanceof NBTSerializableValue)
nbt = ((NBTSerializableValue)nbtValue).getCompoundTag();
else if (nbtValue instanceof NullValue)
nbt = null;
else
nbt = new NBTSerializableValue(nbtValue.getString()).getCompoundTag();
}
@@ -1241,7 +1256,7 @@ public class CarpetExpression
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");
throw new InternalExpressionException("inventory_remove 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)
@@ -1464,9 +1479,11 @@ public class CarpetExpression
* <h3><code>query(e,'age')</code></h3>
* <p>Age, in ticks, of the entity, i.e. how long it existed.</p>
* <h3><code>query(e,'item')</code></h3>
* <p>Name of the item if its an item entity, <code>null</code> otherwise</p>
* <p>The item triple (name, count, nbt) if its an item entity, <code>null</code> otherwise</p>
* <h3><code>query(e,'count')</code></h3>
* <p>Number of items in a stack from item entity, <code>null</code> otherwise</p>
* <h3><code>query(e,'pickup_delay')</code></h3>
* <p>Retrieves pickup delay timeout for an item entity, <code>null</code> otherwise</p>
* <h3><code>query(e,'is_baby')</code></h3>
* <p>Boolean, true if its a baby.</p>
* <h3><code>query(e,'target')</code></h3>
@@ -1528,10 +1545,9 @@ public class CarpetExpression
* that corresponds to the path. For specification of <code>path</code> attribute, consult
* vanilla <code>/data get entity</code> command.</p>
* <p>Note that calls to <code>nbt</code> are considerably more expensive comparing to other
* calls in Minecraft API, and should only be used when there is no other option. Also returned
* NBT is just a string to any retrieval of information post can currently only be done with matching
* operator <code>~</code>. With time we are hoping not to support the <code>'nbt'</code> call better,
* but rather to fill the API, so that <code>'nbt'</code> calls are not needed</p>
* calls in Minecraft API, and should only be used when there is no other option. Returned value is of type <code>nbt</code>,
* which can be further manipulated with nbt type objects via <code>get, put, has, delete</code>, so try to use API calls
* first for that.</p>
* <h2>Entity Modification</h2>
* <p>Like with entity querying, entity modifications happen through one function. Most position and movements
* modifications don't work currently on players as their position is controlled by clients.</p>
@@ -1557,7 +1573,8 @@ public class CarpetExpression
* <h3><code>modify(e, 'accelerate', x, y, z), modify(e, 'accelerate', l(x, y, z) )</code></h3>
* <p>Adds a vector to the motion vector. Most realistic way to apply a force to an entity.</p>
* <h3><code>modify(e, 'custom_name'), modify(e, 'custom_name', name )</code></h3>
* <p>Sets a custom name for an entity. Removes a custom name if the argument is <code>null</code></p>
* <h3><code>modify(e,'pickup_delay')</code></h3>
* <p>Sets a custom pickup delay if the entity argument is an item entity</p>
* <h3><code>modify(e, 'dismount')</code></h3>
* <p>Dismounts riding entity.</p>
* <h3><code>modify(e, 'mount', other)</code></h3>
@@ -1576,6 +1593,44 @@ public class CarpetExpression
* <p>Sets AI to stay around the home position, within <code>distance</code> blocks from it. <code>distance</code>
* defaults to 16 blocks. <code>null</code> removes it. <i>May</i> not work fully with mobs that have this AI built in, like
* Villagers.</p>
*
* <h2>Entity Events</h2>
* <p>There is a number of events that happen to entities that you can attach your own code to in the form of event
* handlers. The event handler is any function that runs in your package that accepts certain expected parameters, which
* you can expand with your own arguments. When it comes to the moment when the given command needs to be executed, it
* does so providing that number of arguments it accepts is equal number of event arguments, and extra arguments passed when
* defining the callback with <code>entity_event</code></p>
* <p>The following events can be handled by entities. </p>
* <ul>
* <li><code>'on_tick'</code>: executes every tick right before entity is ticked in the game. Required arguments: <code>entity</code></li>
* <li><code>'on_death'</code>: executes once when a living entity dies. Required arguments: <code>entity, reason</code></li>
* <li><code>'on_removed'</code>: execute once when an entity is removed. Required arguments: <code>entity</code></li>
* <li><code>'on_damaged'</code>: executed every time a living entity is about to receive damage. Required arguments: <code>entity, amount, source, attacking_entity</code></li>
* </ul>
* <p>It doesn't mean that all entity types will have a chance to
* execute a given event, but entities will not error when you will attach inapplicable event to it.</p>
* <h3><code>entity_event(e, event, call_name, args...)</code></h3>
* <p>Attaches specific function from the current package to be called upon the <code>event</code>, with extra <code>args</code>
* curried to the original required arguments for the event handler</p>
* <pre>
* protect_villager(entity, amount, source, source_entity, healing_player) -&gt;
* (
* if(source_entity &amp;&amp; source_entity~'type' != 'player',
* modify(entity, 'health', amount + entity~'health' );
* particle('end_rod', pos(entity)+l(0,3,0));
* print(str('%s healed thanks to %s', entity, healing_player))
* )
* );
*
* __on_player_interacts_with_entity(player, entity, hand) -&gt;
* (
* if (entity~'type' == 'villager',
* entity_event(entity, 'on_damage', 'protect_villager', player~'name')
* )
* )
* </pre>
* <p>In this case this will protect a villager from entity damage (zombies, etc.) except players by granting all the health
* back to the villager after being harmed.</p>
* </div>
*/
@@ -1673,8 +1728,12 @@ public class CarpetExpression
boolean hasTag = false;
if (lv.size() > position.offset)
{
hasTag = true;
tag = new NBTSerializableValue(lv.get(position.offset).evalValue(c).getString()).getCompoundTag();
NBTSerializableValue v = NBTSerializableValue.parseString(lv.get(position.offset).evalValue(c).getString());
if (v != null)
{
hasTag = true;
tag = v.getCompoundTag();
}
}
tag.putString("id", entityId.toString());
Vec3d vec3d = position.vec;
@@ -2104,6 +2163,12 @@ public class CarpetExpression
y = miny;
z = minz;
}
@Override
public String getString()
{
return String.format("rect[(%d,%d,%d),..,(%d,%d,%d)]",minx, miny, minz, maxx, maxy, maxz);
}
};
});
@@ -2195,6 +2260,12 @@ public class CarpetExpression
curradius = 0;
curpos = 0;
}
@Override
public String getString()
{
return String.format("diamond[(%d,%d,%d),%d,0]",cx, cy, cz, width);
}
};
}
else
@@ -2251,6 +2322,12 @@ public class CarpetExpression
curpos = 0;
curheight = -height;
}
@Override
public String getString()
{
return String.format("diamond[(%d,%d,%d),%d,%d]",cx, cy, cz, width, height);
}
};
}
});
@@ -2286,8 +2363,8 @@ public class CarpetExpression
* <h2>System function</h2>
* <h3><code>nbt(expr)</code></h3>
* <p>Treats the argument as a nbt serializable string and returns its nbt value.
* In case nbt is not in a correct nbt compound tag format, the execution of a script will stop with an error,
* whenever that tag is used.</p>
* In case nbt is not in a correct nbt compound tag format, it will return <code>null</code> value.</p>
* <p>Consult section about container operations in <code>Expression</code> to learn about possible operations on nbt values.</p>
* <h3><code>print(expr)</code></h3>
* <p>Displays the result of the expression to the chat. Overrides default <code>scarpet</code> behaviour of
* sending everyting to stderr.</p>
@@ -2307,11 +2384,11 @@ public class CarpetExpression
* loop(1000,tick(100)) // runs the game twice as slow for 1000 ticks
* </pre>
* <h3><code>current_dimension()</code></h3>
* <p>Returns current dimension that scripts run in.</p>
* <p>Returns current dimension that the script runs in.</p>
* <h3><code>in_dimension(smth, expr)</code></h3>
* <p>Evaluates the expression <code>expr</code> with different dimension execution context. <code>smth</code> can
* be an entity, world-localized block, so not <code>block('stone')</code>, or a string representing a dimension like:
* <code>'nether'</code>, <code>'end'</code> or <code>'overworld'</code>.</p>
* <code>'nether'</code>, <code>'the_nether'</code>, <code>'end'</code> or <code>'overworld'</code>, etc.</p>
* <h3><code>schedule(delay, function, args...)</code></h3>
* <p>Schedules a user defined function to run with a specified <code>delay</code> ticks of delay.
* Scheduled functions run at the end of the tick, and they will run in order they were scheduled.</p>
@@ -2608,7 +2685,9 @@ public class CarpetExpression
Value v = lv.get(0).evalValue(c);
if (v instanceof NBTSerializableValue)
return (cc, tt) -> v;
Value ret = new NBTSerializableValue(v.getString());
Value ret = NBTSerializableValue.parseString(v.getString());
if (ret == null)
return LazyValue.NULL;
return (cc, tt) -> ret;
});
@@ -3005,22 +3084,25 @@ public class CarpetExpression
* <h2></h2>
* <h3>Event list</h3>
* <p>Here is a list of events that can be handled by scarpet. This list includes prefixes required by modules to
* autoload them, but you can add any function to any event if it had required parameters:</p>
* autoload them, but you can add any function to any event if it accepts required number of parameters:</p>
* <pre>
* __on_tick() // can access blocks and entities in the overworld
* __on_tick_nether() // can access blocks and entities in the nether
* __on_tick_ender() // can access blocks and entities in the end
*
* // player specific callbacks
* __on_player_uses_item(player, item_tuple, hand) // right click action
* __on_player_releases_item(player, item_tuple, hand) // client action (e.g. bow)
* __on_player_finishes_using_item(player, item_tuple, hand)) // server action (e.g. food), called item is from before it is used.
* __on_player_clicks_block(player, block, face) // left click attack on a block
* __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec)
* __on_player_breaks_block(player, block) // called after block is broken (the caller receives previous blockstate)
* __on_player_interacts_with_entity(player, entity, hand)
* __on_player_attacks_entity(player, entity)
* __on_player_rides(player, forward, strafe, jumping, sneaking)
* __on_player_jumps(player)
* __on_player_deploys_elytra(player)
* __on_player_wakes_up(player)
* __on_player_rides(player, forward, strafe, jumping, sneaking)
* __on_player_uses_item(player, item_tuple, hand)
* __on_player_clicks_block(player, block, face)
* __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec)
* __on_player_breaks_block(player, block)
* __on_player_interacts_with_entity(player, entity, hand)
* __on_player_attacks_entity(player, entity)
* __on_player_starts_sneaking(player)
* __on_player_stops_sneaking(player)
* __on_player_starts_sprinting(player)
@@ -3028,7 +3110,10 @@ public class CarpetExpression
* </pre>
* <h3><code>/script event</code> command</h3>
* <p>used to display current events and bounded functions. use <code>add_to</code> ro register new event, or <code>remove_from</code>
* to unbind a specific function from an event.</p>
* to unbind a specific function from an event. Function to be bounded to an event needs to have the same
* number of parameters as the action is attempting to bind to (see list above). All calls in modules
* loaded via <code>/script load</code> that have functions listed above will be automatically bounded and unbounded when
* script is unloaded.</p>
* </div>
*/
public void gameEventsSystem()
@@ -200,7 +200,7 @@ public class CarpetScriptServer
if (!fun.startsWith("__on_"))
continue;
String event = fun.replaceFirst("__on_","");
if (!events.eventHandlers.containsKey(event))
if (!CarpetEventServer.Event.byName.containsKey(event))
continue;
events.addEvent(event, hostName, fun);
}
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -94,7 +94,8 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
boolean isHex = false;
if (Character.isDigit(ch) || (ch == decimalSeparator && Character.isDigit(peekNextChar())))
if (Character.isDigit(ch)) // || (ch == decimalSeparator && Character.isDigit(peekNextChar())))
// decided to no support this notation to favour element access via . operator
{
if (ch == '0' && (peekNextChar() == 'x' || peekNextChar() == 'X'))
isHex = true;
@@ -1,9 +1,15 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import java.util.Iterator;
public abstract class AbstractListValue extends Value
{
public abstract Iterator<Value> iterator();
public void fatality() { }
public void append(Value v)
{
throw new InternalExpressionException("Cannot append a value to abstract list");
}
}
@@ -13,6 +13,7 @@ import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.GlobalPos;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
@@ -254,6 +255,14 @@ public class BlockValue extends Value
return new BlockValue(blockState, world, pos);
}
@Override
public int hashCode()
{
if (world != null && pos != null )
return GlobalPos.create(world.getDimension().getType(), pos).hashCode();
return ("b"+getString()).hashCode();
}
public BlockPos getPos()
{
return pos;
@@ -0,0 +1,13 @@
package carpet.script.value;
public interface ContainerValueInterface
{
public Value put(Value where, Value value);
default Value put(Value where, Value value, Value conditions)
{
return put(where, value);
}
public Value get(Value where);
public boolean has(Value where);
public Value delete(Value where);
}
@@ -101,13 +101,13 @@ public class EntityValue extends Value
}
@Override
public boolean equals(Value v)
public boolean equals(Object v)
{
if (v instanceof EntityValue)
{
return entity.getEntityId()==((EntityValue) v).entity.getEntityId();
}
return super.equals(v);
return super.equals((Value)v);
}
@Override
@@ -138,6 +138,13 @@ public class EntityValue extends Value
return "entity";
}
@Override
public int hashCode()
{
return entity.hashCode();
}
public static Pair<EntityType<?>, Predicate<? super Entity>> getPredicate(String who)
{
Pair<EntityType<?>, Predicate<? super Entity>> res = entityPredicates.get(who);
@@ -386,21 +393,11 @@ public class EntityValue extends Value
put("nbt",(e, a) -> {
CompoundTag nbttagcompound = e.toTag((new CompoundTag()));
if (a==null)
return new NBTSerializableValue(nbttagcompound);//StringValue(nbttagcompound.toString());
NbtPathArgumentType.NbtPath path = NBTSerializableValue.cachePath(a.getString());
try
{
List<Tag> tags = path.get(nbttagcompound);
if (tags.size()==0)
return Value.NULL;
if (tags.size()==1)
return NBTSerializableValue.decodeTag(tags.get(0));
return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList()));
}
catch (CommandSyntaxException ignored) { }
return Value.NULL;
return new NBTSerializableValue(nbttagcompound);
return new NBTSerializableValue(nbttagcompound).get(a);
});
}};
private static <Req extends Entity> Req assertEntityArgType(Class<Req> klass, Value arg)
{
if (!(arg instanceof EntityValue))
@@ -728,6 +725,6 @@ public class EntityValue extends Value
put("on_death", (c, e, f, l) -> ((EntityInterface)e).setDeathCallback(c, f, l));
put("on_removed", (c, e, f, l) -> ((EntityInterface)e).setRemovedCallback(c, f, l));
put("on_tick", (c, e, f, l) -> ((EntityInterface)e).setTickCallback(c, f, l));
put("on_damage", (c, e, f, l) -> ((EntityInterface)e).setDamageCallback(c, f, l));
put("on_damaged", (c, e, f, l) -> ((EntityInterface)e).setDamageCallback(c, f, l));
}};
}
@@ -43,5 +43,11 @@ public class FunctionSignatureValue extends Value
}
public List<String> getGlobals() {return globals;}
@Override
public int hashCode()
{
throw new UnsupportedOperationException("function signature value cannot be used as map key");
}
}
@@ -19,4 +19,10 @@ public class GlobalValue extends Value
{
return false;
}
@Override
public int hashCode()
{
throw new UnsupportedOperationException("global value cannot be used as map key");
}
}
@@ -44,6 +44,12 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
{
return stepp > 0?(current < limit):(current > limit);
}
@Override
public String getString()
{
return String.format("[%d, %d, ..., %d]",start, start+stepp, limit);
}
};
}
@@ -115,16 +121,11 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
}
@Override
public boolean equals(final Value o)
public boolean equals(final Object o)
{
return false;
}
@Override
public Value getElementAt(Value v2)
{
throw new InternalExpressionException("get element can only be obtained for regular lists");
}
@Override
public String getTypeString()
{
@@ -145,4 +146,10 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
((LazyListValue)copy).reset();
return copy;
}
@Override
public int hashCode()
{
return ("i"+getString()).hashCode();
}
}
@@ -4,16 +4,13 @@ import carpet.script.exception.InternalExpressionException;
import net.minecraft.item.ItemStack;
import net.minecraft.util.registry.Registry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static java.lang.Math.abs;
public class ListValue extends AbstractListValue
public class ListValue extends AbstractListValue implements ContainerValueInterface
{
protected List<Value> items;
@Override
@@ -102,6 +99,7 @@ public class ListValue extends AbstractListValue
}
return output;
}
@Override
public void append(Value v)
{
items.add(v);
@@ -215,18 +213,11 @@ public class ListValue extends AbstractListValue
}
@Override
public boolean equals(final Value o)
public boolean equals(final Object 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 getItems().equals(((ListValue) o).getItems());
}
return false;
}
@@ -275,6 +266,8 @@ public class ListValue extends AbstractListValue
super(list);
}
}
@Override
public int length()
{
return items.size();
@@ -313,7 +306,69 @@ public class ListValue extends AbstractListValue
}
@Override
public Value getElementAt(Value value)
public Value put(Value where, Value value, Value conditionValue)
{
String condition = conditionValue.getString();
if (condition.equalsIgnoreCase("insert"))
return put(where, value, false, false);
if (condition.equalsIgnoreCase("extend"))
return put(where, value, false, true);
if (condition.equalsIgnoreCase("replace"))
return put(where, value, true, false);
throw new InternalExpressionException("list put modifier could be either insert, or replace");
}
@Override
public Value put(Value ind, Value value)
{
return put(ind, value, true, false);
}
private Value put(Value ind, Value value, boolean replace, boolean extend)
{
if (ind == Value.NULL)
{
if (extend && value instanceof AbstractListValue)
{
((AbstractListValue) value).iterator().forEachRemaining((v)-> items.add(v));
}
else
{
items.add(value);
}
return items.get(items.size()-1);
}
else
{
int numitems = items.size();
int index = (int) NumericValue.asNumber(ind).getLong();
if (index < 0)
{
long range = abs(index) / numitems;
index += (range + 2) * numitems;
index = index % numitems;
}
if (replace)
{
while (index >= items.size()) items.add(Value.NULL);
return items.set(index, value);
}
while (index > items.size()) items.add(Value.NULL);
if (extend && value instanceof AbstractListValue)
{
Iterable<Value> iterable = ((AbstractListValue) value)::iterator;
List<Value> appendix = StreamSupport.stream( iterable.spliterator(), false).collect(Collectors.toList());
items.addAll(index, appendix );
return items.get(index+appendix.size()-1);
}
items.add(index, value);
return value;
}
}
@Override
public Value get(Value value)
{
long index = NumericValue.asNumber(value).getLong();
int numitems = items.size();
@@ -322,9 +377,34 @@ public class ListValue extends AbstractListValue
index = index % numitems;
return items.get((int)index);
}
@Override
public boolean has(Value where)
{
long index = NumericValue.asNumber(where).getLong();
return index >= 0 && index < items.size();
}
@Override
public Value delete(Value where)
{
long index = NumericValue.asNumber(where).getLong();
int numitems = items.size();
long range = abs(index)/numitems;
index += (range+2)*numitems;
index = index % numitems;
return items.remove((int)index);
}
@Override
public String getTypeString()
{
return "list";
}
@Override
public int hashCode()
{
return items.hashCode();
}
}
@@ -0,0 +1,208 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MapValue extends AbstractListValue implements ContainerValueInterface
{
private Map<Value, Value> map;
private MapValue()
{
map = new HashMap<>();
}
public MapValue(List<Value> kvPairs)
{
this();
for (Value v : kvPairs)
{
put(v);
}
}
@Override
public Iterator<Value> iterator()
{
return map.keySet().iterator();
}
@Override
public String getString()
{
return "{"+map.entrySet().stream().map((p) -> p.getKey().getString()+": "+p.getValue().getString()).collect(Collectors.joining(", "))+"}";
}
@Override
public String getPrettyString()
{
if (map.size()<6)
return "{"+map.entrySet().stream().map((p) -> p.getKey().getPrettyString()+": "+p.getValue().getPrettyString()).collect(Collectors.joining(", "))+"}";
List<Value> keys = new ArrayList<>(map.keySet());
int max = keys.size();
return "{"+keys.get(0).getPrettyString()+": "+map.get(keys.get(0)).getPrettyString()+", "+
keys.get(1).getPrettyString()+": "+map.get(keys.get(1)).getPrettyString()+", ..., "+
keys.get(max-2).getPrettyString()+": "+map.get(keys.get(max-2)).getPrettyString()+", "+
keys.get(max-1).getPrettyString()+": "+map.get(keys.get(max-1)).getPrettyString()+"}";
}
@Override
public boolean getBoolean()
{
return !map.isEmpty();
}
@Override
public Value clone()
{
return new MapValue(map);
}
public MapValue(Map<Value,Value> other)
{
map = new HashMap<>(other);
}
@Override
public Value add(Value o)
{
throw new InternalExpressionException("cannot add to a map value");
}
@Override
public Value subtract(Value v)
{
throw new InternalExpressionException("cannot subtract to a map value");
}
@Override
public Value multiply(Value v)
{
throw new InternalExpressionException("cannot multiply to a map value");
}
@Override
public Value divide(Value v)
{
throw new InternalExpressionException("cannot divide to a map value");
}
public void put(Value v)
{
if (!(v instanceof ListValue))
{
map.put(v, Value.NULL);
return;
}
ListValue pair = (ListValue)v;
if (pair.getItems().size() != 2)
{
throw new InternalExpressionException("map constructor requires elements that have two elements");
}
map.put(pair.getItems().get(0), pair.getItems().get(1));
}
@Override
public void append(Value v)
{
put(v);
}
@Override
public int compareTo(Value o)
{
throw new InternalExpressionException("cannot compare to a map value");
}
@Override
public boolean equals(final Object o)
{
if (o instanceof MapValue)
{
return map.equals(((MapValue) o).map);
}
return false;
}
public Map<Value, Value> getMap()
{
return map;
}
public void extend(List<Value> subList)
{
for (Value v: subList) put(v);
}
@Override
public int length()
{
return map.size();
}
@Override
public Value in(Value value1)
{
if (map.containsKey(value1)) return value1;
return Value.NULL;
}
@Override
public Value slice(long from, long to)
{
throw new InternalExpressionException("cannot slice a map value");
}
@Override
public double readNumber()
{
return (double)map.size();
}
@Override
public Value get(Value v2)
{
return map.getOrDefault(v2, Value.NULL);
}
@Override
public boolean has(Value where)
{
return map.containsKey(where);
}
@Override
public Value delete(Value where)
{
Value ret = map.remove(where);
if (ret == null)
return Value.NULL;
return ret;
}
@Override
public Value put(Value key, Value value)
{
Value ret = map.put(key, value);
if (ret == null)
return Value.NULL;
return ret;
}
@Override
public String getTypeString()
{
return "map";
}
@Override
public int hashCode()
{
return map.hashCode();
}
}
@@ -14,23 +14,28 @@ 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.AbstractListTag;
import net.minecraft.nbt.AbstractNumberTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringNbtReader;
import net.minecraft.nbt.Tag;
import net.minecraft.util.math.BlockPos;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class NBTSerializableValue extends Value
public class NBTSerializableValue extends Value implements ContainerValueInterface
{
private String nbtString = null;
private Tag nbtTag = null;
private Supplier<CompoundTag> nbtSupplier = null;
private Supplier<Tag> nbtSupplier = null;
public NBTSerializableValue(ItemStack stack)
{
@@ -43,7 +48,7 @@ public class NBTSerializableValue extends Value
{
try
{
return (new StringNbtReader(new StringReader(nbtString))).parseCompoundTag();
return (new StringNbtReader(new StringReader(nbtString))).parseTag();
}
catch (CommandSyntaxException e)
{
@@ -52,6 +57,26 @@ public class NBTSerializableValue extends Value
};
}
public static NBTSerializableValue parseString(String nbtString)
{
Tag tag;
try
{
tag = (new StringNbtReader(new StringReader(nbtString))).parseTag();
}
catch (CommandSyntaxException e)
{
return null;
}
NBTSerializableValue value = new NBTSerializableValue(tag);
value.nbtString = nbtString;
return value;
}
public Value clone()
{
return new NBTSerializableValue(getTag().copy());
}
public NBTSerializableValue(Tag tag)
{
nbtTag = tag;
@@ -156,7 +181,7 @@ public class NBTSerializableValue extends Value
return slot;
}
public static Value decodeTag(Tag t)
private static Value decodeTag(Tag t)
{
if (t instanceof CompoundTag)
return new NBTSerializableValue(t);
@@ -166,19 +191,26 @@ public class NBTSerializableValue extends Value
return new StringValue(t.asString());
}
public Tag getTag()
private Tag getTag()
{
if (nbtTag == null)
nbtTag = nbtSupplier.get();
return nbtTag;
}
private void replaceTag(Tag newTag)
{
nbtTag = newTag;
nbtString = null;
nbtSupplier = null; // just to be sure
}
@Override
public boolean equals(final Value o)
public boolean equals(final Object o)
{
if (o instanceof NBTSerializableValue)
return getTag().equals(((NBTSerializableValue) o).getTag());
return super.equals(o);
return super.equals((Value)o);
}
@Override
@@ -207,6 +239,188 @@ public class NBTSerializableValue extends Value
}
}
@Override
public Value put(Value where, Value value)
{
return put(where, value, new StringValue("replace"));
}
@Override
public Value put(Value where, Value value, Value conditions)
{
/// WIP
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
Tag tagToInsert = value instanceof NBTSerializableValue ?
((NBTSerializableValue) value).getTag() :
new NBTSerializableValue(value.getString()).getTag();
Tag modifiedTag;
if (conditions instanceof NumericValue)
{
modifiedTag = modify_insert((int)((NumericValue) conditions).getLong(), path, tagToInsert);
}
else
{
String ops = conditions.getString();
if (ops.equalsIgnoreCase("merge"))
{
modifiedTag = modify_merge(path, tagToInsert);
}
else if (ops.equalsIgnoreCase("replace"))
{
modifiedTag = modify_replace(path, tagToInsert);
}
else
{
return this;
}
}
if (modifiedTag != null)
{
replaceTag(modifiedTag);
}
return this;
}
private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement)
{
return modify_insert(index, nbtPath, newElement, this.getTag().copy());
}
private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement, Tag currentTag)
{
Collection<Tag> targets;
try
{
targets = nbtPath.putIfAbsent(currentTag, ListTag::new);
}
catch (CommandSyntaxException e)
{
return null;
}
boolean modified = false;
for (Tag target : targets)
{
if (!(target instanceof AbstractListTag))
{
continue;
}
try
{
AbstractListTag<?> targetList = (AbstractListTag) target;
if (!targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()))
return null;
modified = true;
}
catch (IndexOutOfBoundsException ignored)
{
}
}
return modified?currentTag:null;
}
private Tag modify_merge(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
{
if (!(replacement instanceof CompoundTag))
{
return getTag();
}
Tag originalTag = getTag().copy();
try
{
for (Tag target : nbtPath.putIfAbsent(originalTag, CompoundTag::new))
{
if (!(target instanceof CompoundTag))
{
continue;
}
((CompoundTag) target).copyFrom((CompoundTag) replacement);
}
}
catch (CommandSyntaxException ignored) { }
return originalTag;
}
private Tag modify_replace(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
{
Tag originalTag = getTag().copy();
String pathText = nbtPath.toString();
if (pathText.endsWith("]")) // workaround for array replacement or item in the array replacement
{
if (nbtPath.remove(originalTag)==0)
return null;
Pattern pattern = Pattern.compile("\\[[^\\[]*]$");
Matcher matcher = pattern.matcher(pathText);
if (!matcher.find()) // malformed path
{
return null;
}
String arrAccess = matcher.group();
int pos;
if (arrAccess.length()==2) // we just removed entire array
pos = 0;
else
{
try
{
pos = Integer.parseInt(arrAccess.substring(1, arrAccess.length() - 1));
}
catch (NumberFormatException e)
{
return null;
}
}
NbtPathArgumentType.NbtPath newPath = cachePath(pathText.substring(0, pathText.length()-arrAccess.length()));
return modify_insert(pos,newPath,replacement, originalTag);
}
try
{
nbtPath.put(originalTag, () -> replacement);
}
catch (CommandSyntaxException e)
{
return null;
}
return originalTag;
}
@Override
public Value get(Value value)
{
NbtPathArgumentType.NbtPath path = cachePath(value.getString());
try
{
List<Tag> tags = path.get(getTag());
if (tags.size()==0)
return Value.NULL;
if (tags.size()==1)
return NBTSerializableValue.decodeTag(tags.get(0));
return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList()));
}
catch (CommandSyntaxException ignored) { }
return Value.NULL;
}
@Override
public boolean has(Value where)
{
return cachePath(where.getString()).count(getTag()) > 0;
}
@Override
public Value delete(Value where)
{
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
Tag tag = getTag().copy();
int removed = path.remove(tag);
if (removed > 0) replaceTag(tag);
return new NumericValue(removed);
}
public static class InventoryLocator
{
public Object owner;
@@ -223,7 +437,7 @@ public class NBTSerializableValue extends Value
}
private static Map<String, NbtPathArgumentType.NbtPath> pathCache = new HashMap<>();
public static NbtPathArgumentType.NbtPath cachePath(String arg)
private static NbtPathArgumentType.NbtPath cachePath(String arg)
{
NbtPathArgumentType.NbtPath res = pathCache.get(arg);
if (res != null)
@@ -242,23 +456,6 @@ public class NBTSerializableValue extends Value
return res;
}
@Override
public Value getElementAt(Value value)
{
NbtPathArgumentType.NbtPath path = cachePath(value.getString());
try
{
List<Tag> tags = path.get(getTag());
if (tags.size()==0)
return Value.NULL;
if (tags.size()==1)
return NBTSerializableValue.decodeTag(tags.get(0));
return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList()));
}
catch (CommandSyntaxException ignored) { }
return Value.NULL;
}
@Override
public String getTypeString()
{
@@ -28,7 +28,7 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
public NullValue() {super(0.0D);}
@Override
public boolean equals(final Value o)
public boolean equals(final Object o)
{
return o instanceof NullValue;
}
@@ -44,4 +44,10 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
{
return "null";
}
@Override
public int hashCode()
{
return 0;
}
}
@@ -125,7 +125,7 @@ public class NumericValue extends Value
return getString().compareTo(o.getString());
}
@Override
public boolean equals(Value o)
public boolean equals(Object o)
{
if (o instanceof NullValue)
{
@@ -133,7 +133,7 @@ public class NumericValue extends Value
}
if (o instanceof NumericValue)
{
return !this.subtract(o).getBoolean();
return !this.subtract((Value) o).getBoolean();
}
return super.equals(o);
}
@@ -179,4 +179,13 @@ public class NumericValue extends Value
return "number";
}
@Override
public int hashCode()
{
if (Math.abs(Math.floor(value + 0.5D)-value) < epsilon) // is sufficiently close to the integer value
return Long.hashCode(getLong());
return Double.hashCode(value);
}
}
+18 -5
View File
@@ -115,6 +115,16 @@ public abstract class Value implements Comparable<Value>, Cloneable
}
return getString().compareTo(o.getString());
}
@Override // for hashmap key access
public boolean equals(final Object o)
{
if (o instanceof Value)
return this.compareTo((Value) o)==0;
return false;
}
// for general use
public boolean equals(final Value o)
{
return this.compareTo(o)==0;
@@ -173,13 +183,16 @@ public abstract class Value implements Comparable<Value>, Cloneable
return (long)readNumber();
}
public Value getElementAt(Value v2)
{
throw new InternalExpressionException("get element can only be obtained for lists and nbt values");
}
public String getTypeString()
{
throw new InternalExpressionException("How did you get here? Cannot get type of an intenal type.");
}
@Override
public int hashCode()
{
String stringVal = getString();
if (stringVal.isEmpty()) return 0;
return ("s"+stringVal).hashCode();
}
}