From 42e67f27b75245e6a63f9a85d1639425f0c22899 Mon Sep 17 00:00:00 2001 From: Ghoulboy Date: Sun, 6 Dec 2020 21:03:15 +0100 Subject: [PATCH] Adding absorption and xp API, and weather control (#540) * fixes #537 * fixes #547 --- docs/scarpet/api/Auxiliary.md | 3 +- docs/scarpet/api/BlocksAndWorldAccess.md | 10 ++++ docs/scarpet/api/Entities.md | 23 +++++++ .../carpet/fakes/ServerWorldInterface.java | 7 +++ ...xin.java => ServerWorld_scarpetMixin.java} | 10 +++- .../java/carpet/script/api/Auxiliary.java | 46 +++++++++----- .../java/carpet/script/api/WorldAccess.java | 60 +++++++++++++++++++ .../java/carpet/script/value/EntityValue.java | 43 ++++++++++++- src/main/resources/carpet.mixins.json | 2 +- 9 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 src/main/java/carpet/fakes/ServerWorldInterface.java rename src/main/java/carpet/mixins/{ServerWorld_scarpetEventMixin.java => ServerWorld_scarpetMixin.java} (86%) diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index af470d08..10544321 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -193,9 +193,10 @@ Other value types will only be converted to tags (including NBT tags) if `force` extra treatment when loading them back from NBT, but using `force` true will always produce output / never produce an exception. -### `print(expr)` +### `print(expr)`, `print(player/player_list, expr)` Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr. +Can optionally define player or list of players to send the message to. ### `format(components, ...)`, `format(l(components, ...))` diff --git a/docs/scarpet/api/BlocksAndWorldAccess.md b/docs/scarpet/api/BlocksAndWorldAccess.md index 8f295ee0..990ae027 100644 --- a/docs/scarpet/api/BlocksAndWorldAccess.md +++ b/docs/scarpet/api/BlocksAndWorldAccess.md @@ -212,6 +212,16 @@ Causes a block to be harvested by a specified player entity. Honors player item tool if applicable. If the entity is not a valid player, no block gets destroyed. If a player is not allowed to break that block, a block doesn't get destroyed either. +### `weather()`,`weather(type)`,`weather(type, ticks)` + +If called with no args, returns `'clear'`, `'rain` or `'thunder'` based on the current weather. If thundering, will +always return `'thunder'`, if not will return `'rain'` or `'clear'` based on the current weather. + +With one arg, (either `'clear'`, `'rain` or `'thunder'`), returns the number of remaining ticks for that weather type. +NB: It can thunder without there being a thunderstorm, there has to be both rain and thunder to form a storm. + +With two args, sets the weather to `type` for `ticks` ticks. + ## Block and World querying ### `pos(block), pos(entity)` diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index f6219452..1d053fbf 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -396,6 +396,18 @@ Number indicating remaining entity health, or `null` if not applicable. Retrieves player hunger related information. For non-players, returns `null`. +### `query(e, 'absorption')` + +Gets the absorption of the player (yellow hearts, e.g when having a golden apple.) + +### `query(e,'xp')` +### `query(e,'xp_level')` +### `query(e,'xp_progress')` +### `query(e,'score')` + +Numbers related to player's xp. `xp` is the overall xp in the bar, `xp_level` is the levels seen in the hotbar, +`xp_points` is a float between 0 and 1 indicating the percentage of the xp bar filled, and `score` is the score displayed upon death + ### `query(e, 'air')` Number indicating remaining entity health, or `null` if not applicable. @@ -706,6 +718,17 @@ Will set entity on fire for `ticks` ticks. Set to 0 to extinguish. Modifies directly player raw hunger components. Has no effect on non-players +### `modify(e, 'absorption', value)` + +Sets the absorption value for the player. Each point is half a yellow heart. + +### `modify(e, 'add_xp', value)` +### `modify(e, 'xp_level', value)` +### `modify(e, 'xp_score', value)` + +Manipulates player xp values - that's the method you probably want to use +to manipulate how much 'xp' an action should give. + ### `modify(e, 'air', ticks)` Modifies entity air diff --git a/src/main/java/carpet/fakes/ServerWorldInterface.java b/src/main/java/carpet/fakes/ServerWorldInterface.java new file mode 100644 index 00000000..67414706 --- /dev/null +++ b/src/main/java/carpet/fakes/ServerWorldInterface.java @@ -0,0 +1,7 @@ +package carpet.fakes; + +import net.minecraft.world.level.ServerWorldProperties; + +public interface ServerWorldInterface { + ServerWorldProperties getWorldPropertiesCM(); +} diff --git a/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java b/src/main/java/carpet/mixins/ServerWorld_scarpetMixin.java similarity index 86% rename from src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java rename to src/main/java/carpet/mixins/ServerWorld_scarpetMixin.java index 5c11338a..5e83e881 100644 --- a/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java +++ b/src/main/java/carpet/mixins/ServerWorld_scarpetMixin.java @@ -1,6 +1,7 @@ package carpet.mixins; import carpet.CarpetSettings; +import carpet.fakes.ServerWorldInterface; import carpet.script.CarpetEventServer; import net.minecraft.entity.Entity; import net.minecraft.entity.LightningEntity; @@ -9,7 +10,9 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.profiler.Profiler; import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.level.ServerWorldProperties; 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.callback.CallbackInfo; @@ -19,7 +22,7 @@ import static carpet.script.CarpetEventServer.Event.ENTITY_LOAD; import static carpet.script.CarpetEventServer.Event.LIGHTNING; @Mixin(ServerWorld.class) -public class ServerWorld_scarpetEventMixin +public class ServerWorld_scarpetMixin implements ServerWorldInterface { @Inject(method = "tickChunk", locals = LocalCapture.CAPTURE_FAILHARD, at = @At( value = "INVOKE", @@ -54,4 +57,9 @@ public class ServerWorld_scarpetEventMixin } } + @Shadow + private ServerWorldProperties worldProperties; + public ServerWorldProperties getWorldPropertiesCM(){ + return worldProperties; + } } diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index 227cc386..ec5499c0 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -469,28 +469,42 @@ public class Auxiliary { if (lv.size() == 0 || lv.size() > 2) throw new InternalExpressionException("'print' takes one or two arguments"); ServerCommandSource s = ((CarpetContext)c).s; Value res = lv.get(0).evalValue(c); - if (lv.size() == 2) - { - ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), res); - if (player == null) return LazyValue.NULL; - s = player.getCommandSource(); + Value playersVal = Value.NULL; + if (lv.size() == 2){ + playersVal = res; res = lv.get(1).evalValue(c); } - if (res instanceof FormattedTextValue) - { - s.sendFeedback(((FormattedTextValue) res).getText(), false); - } - else - { - if (s.getEntity() instanceof PlayerEntity) - { - Messenger.m((PlayerEntity) s.getEntity(), "w " + res.getString()); + + if(playersVal instanceof ListValue){ + + List players = ((ListValue) playersVal).getItems(); + for(Value playerVal:players){ + ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), playerVal); + if (player == null) return LazyValue.NULL; + if(res instanceof FormattedTextValue) + player.getCommandSource().sendFeedback(((FormattedTextValue) res).getText(), false); + else + Messenger.m(player, "w "+res.getString()); } + + } else if(playersVal instanceof EntityValue){ + + ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), playersVal); + if (player == null) return LazyValue.NULL; + if(res instanceof FormattedTextValue) + player.getCommandSource().sendFeedback(((FormattedTextValue) res).getText(), false); + else + Messenger.m(player, "w "+res.getString()); + + } else { + + if (res instanceof FormattedTextValue) + s.sendFeedback(((FormattedTextValue) res).getText(), false); else - { Messenger.m(s, "w " + res.getString()); - } + } + Value finalRes = res; return (_c, _t) -> finalRes; // pass through for variables }); diff --git a/src/main/java/carpet/script/api/WorldAccess.java b/src/main/java/carpet/script/api/WorldAccess.java index fa7a6361..f1888417 100644 --- a/src/main/java/carpet/script/api/WorldAccess.java +++ b/src/main/java/carpet/script/api/WorldAccess.java @@ -6,6 +6,7 @@ import carpet.fakes.BiomeArrayInterface; import carpet.fakes.ChunkGeneratorInterface; import carpet.fakes.ChunkTicketManagerInterface; import carpet.fakes.ServerChunkManagerInterface; +import carpet.fakes.ServerWorldInterface; import carpet.fakes.SpawnHelperInnerInterface; import carpet.fakes.ThreadedAnvilChunkStorageInterface; import carpet.helpers.FeatureGenerator; @@ -58,6 +59,7 @@ import net.minecraft.item.SwordItem; import net.minecraft.item.TridentItem; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ChunkTicket; import net.minecraft.server.world.ChunkTicketType; @@ -89,6 +91,7 @@ import net.minecraft.world.gen.ChunkRandom; import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.gen.feature.ConfiguredStructureFeature; import net.minecraft.world.gen.feature.StructureFeature; +import net.minecraft.world.level.ServerWorldProperties; import net.minecraft.world.poi.PointOfInterest; import net.minecraft.world.poi.PointOfInterestStorage; import net.minecraft.world.poi.PointOfInterestType; @@ -413,6 +416,63 @@ public class WorldAccess { }); + expression.addLazyFunction("weather",-1,(c, t, lv) -> { + ServerWorld world = ((CarpetContext) c).s.getWorld(); + + if(lv.size()==0)//cos it can thunder when raining or when clear. + return (_c,_t)->new StringValue( world.isThundering()? "thunder" : (world.isRaining()? "rain":"clear")); + + + Value weather = lv.get(0).evalValue(c); + ServerWorldProperties worldProperties = ((ServerWorldInterface) world).getWorldPropertiesCM(); + + if(lv.size()==1) { + int ticks; + + switch (weather.getString()) { + case "clear": + ticks = worldProperties.getClearWeatherTime(); + break; + case "rain": + ticks = world.isRaining()? worldProperties.getRainTime():0;//cos if not it gives 1 for some reason + break; + case "thunder": + ticks = world.isThundering()? worldProperties.getThunderTime():0;//same dealio here + break; + default: + throw new InternalExpressionException("Weather can only be 'clear', 'rain' or 'thunder'"); + } + return (_c,_t) -> new NumericValue(ticks); + } else if(lv.size()==2){ + int ticks = ((NumericValue)lv.get(1).evalValue(c)).getInt(); + + switch (weather.getString()){ + case "clear": + world.setWeather(ticks,0,false,false); + break; + + case "rain": + world.setWeather(0,ticks,true,false); + break; + + case "thunder": + world.setWeather( + 0, + ticks,//this is used to set thunder time, idk why... + true, + true + ); + break; + + default: + throw new InternalExpressionException("Weather can only be 'clear', 'rain' or 'thunder'"); + } + + return LazyValue.TRUE; + } else + throw new InternalExpressionException("'weather' between 0 and 2 arguments"); + }); + expression.addLazyFunction("pos", 1, (c, t, lv) -> { Value arg = lv.get(0).evalValue(c); diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index cb6aa894..ff863165 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -499,6 +499,31 @@ public class EntityValue extends Value return Value.NULL; }); + put("absorption",(e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getAbsorptionAmount()); + return Value.NULL; + }); + + put("xp",(e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).totalExperience); + return Value.NULL; + }); + + put("xp_level", (e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).experienceLevel); + return Value.NULL; + }); + + put("xp_progress", (e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).experienceProgress); + return Value.NULL; + }); + + put("score", (e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getScore()); + return Value.NULL; + }); + put("jumping", (e, a) -> { if (e instanceof LivingEntity) { @@ -1328,8 +1353,24 @@ public class EntityValue extends Value if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong()); }); + put("absorption", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).setAbsorptionAmount((float) NumericValue.asNumber(v).getLong()); + }); + + put("add_xp", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).addExperience(NumericValue.asNumber(v).getInt()); + }); + + put("xp_level", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).addExperienceLevels(NumericValue.asNumber(v).getInt()-((PlayerEntity) e).experienceLevel); + }); + + put("xp_score", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).setScore(NumericValue.asNumber(v).getInt()); + }); + put("saturation", (e, v)-> { - if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong()); + if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient(NumericValue.asNumber(v).getFloat()); }); put("air", (e, v) -> e.setAir(NumericValue.asNumber(v).getInt())); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 8a90de0a..024e8f8e 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -168,7 +168,7 @@ "ChunkTicketManager_scarpetMixin", "StatType_scarpetMixin", "PointOfInterest_scarpetMixin", - "ServerWorld_scarpetEventMixin", + "ServerWorld_scarpetMixin", "HungerManagerMixin_scarpetEventsMixin", "RecipeManager_scarpetMixin",