diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index 9b4ad2c5..e3220bcb 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -2781,6 +2781,53 @@ If `slot` is not specified, it defaults to the main hand. Number indicating the selected slot of entity's inventory. Currently only applicable to players. +### `query(e, 'active_block')` + +Returns currently mined block by the player, as registered by the game server. + +### `query(e, 'breaking_progress')` + +Returns current breaking progress of a current player mining block, or `null`, if no block is mined. +Breaking progress, it not null, is any number 0 or above, while 10 means that the block should already be +broken by the client. This value may tick above 10, if the client / connection is lagging + +Example: + +The following program provides custom breaking times, including nice block breaking animations, including instamine, for +blocks that otherwise would take longer to mine. + +[Video demo](https://youtu.be/zvEEuGxgCio) +```py +global_blocks = { + 'oak_planks' -> 0, + 'obsidian' -> 1, + 'end_portal_frame' -> 5, + 'bedrock' -> 10 +}; + +__on_player_clicks_block(player, block, face) -> +( + step = global_blocks:str(block); + if (step == 0, + destroy(block, -1); // instamine + , step != null, + schedule(0, '_break', player, pos(block), str(block), step, 0); + ) +); + +_break(player, pos, name, step, lvl) -> +( + current = player~'active_block'; + if (current != name || pos(current) != pos, + modify(player, 'breaking_progress', null); + , + modify(player, 'breaking_progress', lvl); + if (lvl >= 10, destroy(pos, -1)); + schedule(step, '_break', player, pos, name, step, lvl+1) + ); +) +``` + ### `query(e, 'facing', order?)` Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions @@ -3013,6 +3060,12 @@ Modifies directly player raw hunger components. Has no effect on non-players adds exhaustion value to the current player exhaustion level - that's the method you probably want to use to manipulate how much 'food' some action cost. +### `modify(e, 'breaking_progress', value)` + +Modifies a breaking progress of a player currently mined block. Value of `null`, `-1` make it reset. +Values `0` to `10` will show respective animation of a breaking block. Check `query(e, 'breaking_progress')` for +examples. + ### `modify(e, 'nbt_merge', partial_tag)` Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players @@ -3267,6 +3320,7 @@ __on_player_places_block(player, item_tuple, hand, block) // player have just pl __on_player_interacts_with_entity(player, entity, hand) __on_player_chooses_recipe(player, recipe, full_stack) __on_player_switches_slot(player, from, to) +__on_player_swaps_hands(player) __on_player_attacks_entity(player, entity) __on_player_takes_damage(player, amount, source, source_entity) __on_player_deals_damage(player, amount, entity) diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 2cdd80dc..8e80f1c2 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -347,6 +347,53 @@ If `slot` is not specified, it defaults to the main hand. Number indicating the selected slot of entity's inventory. Currently only applicable to players. +### `query(e, 'active_block')` + +Returns currently mined block by the player, as registered by the game server. + +### `query(e, 'breaking_progress')` + +Returns current breaking progress of a current player mining block, or `null`, if no block is mined. +Breaking progress, it not null, is any number 0 or above, while 10 means that the block should already be +broken by the client. This value may tick above 10, if the client / connection is lagging + +Example: + +The following program provides custom breaking times, including nice block breaking animations, including instamine, for +blocks that otherwise would take longer to mine. + +[Video demo](https://youtu.be/zvEEuGxgCio) +```py +global_blocks = { + 'oak_planks' -> 0, + 'obsidian' -> 1, + 'end_portal_frame' -> 5, + 'bedrock' -> 10 +}; + +__on_player_clicks_block(player, block, face) -> +( + step = global_blocks:str(block); + if (step == 0, + destroy(block, -1); // instamine + , step != null, + schedule(0, '_break', player, pos(block), str(block), step, 0); + ) +); + +_break(player, pos, name, step, lvl) -> +( + current = player~'active_block'; + if (current != name || pos(current) != pos, + modify(player, 'breaking_progress', null); + , + modify(player, 'breaking_progress', lvl); + if (lvl >= 10, destroy(pos, -1)); + schedule(step, '_break', player, pos, name, step, lvl+1) + ); +) +``` + ### `query(e, 'facing', order?)` Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions @@ -579,6 +626,12 @@ Modifies directly player raw hunger components. Has no effect on non-players adds exhaustion value to the current player exhaustion level - that's the method you probably want to use to manipulate how much 'food' some action cost. +### `modify(e, 'breaking_progress', value)` + +Modifies a breaking progress of a player currently mined block. Value of `null`, `-1` make it reset. +Values `0` to `10` will show respective animation of a breaking block. Check `query(e, 'breaking_progress')` for +examples. + ### `modify(e, 'nbt_merge', partial_tag)` Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players diff --git a/docs/scarpet/api/Events.md b/docs/scarpet/api/Events.md index 05b8c4cd..90b7a87a 100644 --- a/docs/scarpet/api/Events.md +++ b/docs/scarpet/api/Events.md @@ -34,6 +34,7 @@ __on_player_places_block(player, item_tuple, hand, block) // player have just pl __on_player_interacts_with_entity(player, entity, hand) __on_player_chooses_recipe(player, recipe, full_stack) __on_player_switches_slot(player, from, to) +__on_player_swaps_hands(player) __on_player_attacks_entity(player, entity) __on_player_takes_damage(player, amount, source, source_entity) __on_player_deals_damage(player, amount, entity) diff --git a/docs/scarpet/resources/editors/idea/4.txt b/docs/scarpet/resources/editors/idea/4.txt index 1f659f99..5014093f 100644 --- a/docs/scarpet/resources/editors/idea/4.txt +++ b/docs/scarpet/resources/editors/idea/4.txt @@ -23,6 +23,7 @@ __on_player_starts_sprinting __on_player_stops_sneaking __on_player_stops_sprinting __on_player_switches_slot +__on_player_swaps_hands __on_player_uses_item __on_player_wakes_up __on_statistic diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index ffb50e58..4147faeb 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -106,6 +106,7 @@ __on_player_stops_sneaking __on_player_stops_sprinting __on_player_switches_slot + __on_player_swaps_hands __on_player_uses_item __on_player_wakes_up __on_statistic diff --git a/src/main/java/carpet/fakes/ServerPlayerInteractionManagerInterface.java b/src/main/java/carpet/fakes/ServerPlayerInteractionManagerInterface.java new file mode 100644 index 00000000..ed43336a --- /dev/null +++ b/src/main/java/carpet/fakes/ServerPlayerInteractionManagerInterface.java @@ -0,0 +1,12 @@ +package carpet.fakes; + +import net.minecraft.util.math.BlockPos; + +public interface ServerPlayerInteractionManagerInterface +{ + BlockPos getCurrentBreakingBlock(); + + int getCurrentBlockBreakingProgress(); + + void setBlockBreakingProgress(int progress); +} diff --git a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java index 027236bd..03b35120 100644 --- a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java @@ -37,6 +37,7 @@ 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_SWAPS_HANDS; import static carpet.script.CarpetEventServer.Event.PLAYER_SWITCHES_SLOT; import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM; import static carpet.script.CarpetEventServer.Event.PLAYER_WAKES_UP; @@ -67,6 +68,17 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin PLAYER_DROPS_ITEM.onPlayerEvent(player); } + @Inject(method = "onPlayerAction", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;getStackInHand(Lnet/minecraft/util/Hand;)Lnet/minecraft/item/ItemStack;", + ordinal = 0, + shift = At.Shift.BEFORE + )) + private void onHandSwap(PlayerActionC2SPacket playerActionC2SPacket_1, CallbackInfo ci) + { + PLAYER_SWAPS_HANDS.onPlayerEvent(player); + } + @Inject(method = "onPlayerAction", at = @At( value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;dropSelectedItem(Z)Z", diff --git a/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java index 294551b9..72ed11e7 100644 --- a/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java @@ -1,5 +1,6 @@ package carpet.mixins; +import carpet.fakes.ServerPlayerInteractionManagerInterface; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; @@ -7,10 +8,12 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerInteractionManager; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -24,10 +27,18 @@ import static carpet.script.CarpetEventServer.Event.PLAYER_INTERACTS_WITH_BLOCK; @Mixin(ServerPlayerInteractionManager.class) -public class ServerPlayerInteractionManager_scarpetEventsMixin +public class ServerPlayerInteractionManager_scarpetEventsMixin implements ServerPlayerInteractionManagerInterface { @Shadow public ServerPlayerEntity player; + @Shadow private boolean mining; + + @Shadow private BlockPos miningPos; + + @Shadow private int blockBreakingProgress; + + @Shadow public ServerWorld world; + @Inject(method = "tryBreakBlock", locals = LocalCapture.CAPTURE_FAILHARD, at = @At( value = "INVOKE", target = "Lnet/minecraft/block/Block;onBreak(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/entity/player/PlayerEntity;)V", @@ -46,4 +57,25 @@ public class ServerPlayerInteractionManager_scarpetEventsMixin { PLAYER_INTERACTS_WITH_BLOCK.onBlockHit(player, hand, hitResult); } + + @Override + public BlockPos getCurrentBreakingBlock() + { + if (!mining) return null; + return miningPos; + } + + @Override + public int getCurrentBlockBreakingProgress() + { + if (!mining) return -1; + return blockBreakingProgress; + } + + @Override + public void setBlockBreakingProgress(int progress) + { + blockBreakingProgress = MathHelper.clamp(progress, -1, 10); + world.setBlockBreakingInfo(-1*this.player.getEntityId(), miningPos, blockBreakingProgress); + } } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index 7d2aa40c..15212424 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -538,6 +538,14 @@ public class CarpetEventServer }, player::getCommandSource); } }, + PLAYER_SWAPS_HANDS("player_swaps_hands", 1, false) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, PLAYER_TAKES_DAMAGE("player_takes_damage", 4, false) { @Override diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 09d3ddab..c68a8a8d 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -7,6 +7,7 @@ import carpet.fakes.LivingEntityInterface; import carpet.fakes.MemoryInterface; import carpet.fakes.MobEntityInterface; import carpet.fakes.HungerManagerInterface; +import carpet.fakes.ServerPlayerInteractionManagerInterface; import carpet.helpers.Tracer; import carpet.patches.EntityPlayerMPFake; import carpet.script.CarpetContext; @@ -471,9 +472,32 @@ public class EntityValue extends Value put("selected_slot", (e, a) -> { if (e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).inventory.selectedSlot); - return null; + return Value.NULL; }); + put("active_block", (e, a) -> { + if (e instanceof ServerPlayerEntity) + { + ServerPlayerInteractionManagerInterface manager = (ServerPlayerInteractionManagerInterface) (((ServerPlayerEntity) e).interactionManager); + BlockPos pos = manager.getCurrentBreakingBlock(); + if (pos == null) return Value.NULL; + return new BlockValue(null, ((ServerPlayerEntity) e).getServerWorld(), pos); + } + return Value.NULL; + }); + + put("breaking_progress", (e, a) -> { + if (e instanceof ServerPlayerEntity) + { + ServerPlayerInteractionManagerInterface manager = (ServerPlayerInteractionManagerInterface) (((ServerPlayerEntity) e).interactionManager); + int progress = manager.getCurrentBlockBreakingProgress(); + if (progress < 0) return Value.NULL; + return new NumericValue(progress); + } + return Value.NULL; + }); + + put("facing", (e, a) -> { int index = 0; if (a != null) @@ -1040,6 +1064,15 @@ public class EntityValue extends Value if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong()); }); + put("breaking_progress", (e, a) -> { + if (e instanceof ServerPlayerEntity) + { + int progress = (a == null || a == Value.NULL)?-1:NumericValue.asNumber(a).getInt(); + ServerPlayerInteractionManagerInterface manager = (ServerPlayerInteractionManagerInterface) (((ServerPlayerEntity) e).interactionManager); + manager.setBlockBreakingProgress(progress); + } + }); + put("nbt", (e, v) -> { if (!(e instanceof PlayerEntity)) { diff --git a/src/main/resources/assets/carpet/lang/zh_cn.json b/src/main/resources/assets/carpet/lang/zh_cn.json index 248faac4..f6dd3ad1 100644 --- a/src/main/resources/assets/carpet/lang/zh_cn.json +++ b/src/main/resources/assets/carpet/lang/zh_cn.json @@ -201,6 +201,46 @@ "rule.portalCreativeDelay.name": "创造玩家不被地狱门传送", "rule.portalCreativeDelay.desc": "创造模式的玩家将不会立即被地狱门传送", + "rule.commandProfile.name": "游戏性能监视器", + "rule.commandProfile.desc": "启用/profile命令以监视游戏性能", + "rule.commandProfile.extra.0": "/tick命令功能的部分增强版", + + "rule.commandScriptACE.name": "地毯脚本使用权限", + "rule.commandScriptACE.desc": "限制无权限的玩家执行地毯脚本", + "rule.commandScriptACE.extra.0": "无此权限的玩家无法执行/script run指令或者加载脚本", + + "rule.creativeFlyDrag.name": "创造飞行阻力系数", + "rule.creativeFlyDrag.desc": "允许使用本功能更改创造模式下飞行时的空气阻力系数", + "rule.creativeFlyDrag.extra.0": "阻力增加会降低您的飞行速度,\n所以需要相应地调整速度,\n使用 1.0 阻力系数的时候,使用 11 的速度系数似乎与原版速度相匹配.\n本功能仅在客户端也安装地毯MOD(版本>=服务器安装版本)的情况下生效", + + "rule.creativeFlySpeed.name": "创造飞行速度系数", + "rule.creativeFlySpeed.desc": "允许使用本功能更改创造模式下飞行速度系数", + "rule.creativeFlySpeed.extra.0": "本功能仅在客户端也安装地毯MOD(版本>=服务器安装版本)的情况下生效", + + "rule.creativeNoClip.name": "创造玩家无碰撞检测", + "rule.creativeNoClip.desc": "创造玩家可以穿过方块", + "rule.creativeNoClip.extra.0": "这个选项需要服务端和客户端同时支持才生效.\n仅在服务端设置是无效的,而仅在客户端设置的情况下\n你可以利用一些和活板门有关的技巧来穿墙", + + "rule.fogOff.name": "关闭雾气", + "rule.fogOff.desc": "启用本功能以关闭地狱与末地的雾气", + "rule.fogOff.extra.0": "提高了可见度,但看起来很奇怪", + + "rule.forceloadLimit.name": "永加载区块上限更改", + "rule.forceloadLimit.desc": "允许使用本功能更改/forceload命令标记的永久加载区块上限", + + "rule.interactionUpdates.name": "放置方块产生更新", + "rule.interactionUpdates.desc": "放置方块是否产生更新的开关,设为false以避免产生更新", + + "rule.language.name": "语言", + "rule.language.desc": "为地毯Mod设置所需语言", + + "rule.portalSurvivalDelay.name": "生存模式地狱门传送延迟", + "rule.portalSurvivalDelay.desc": "设置生存模式下地狱门传送的延迟(单位:tick)", + + "rule.spawnChunksSize.name": "出生点永加载区块半径", + "rule.spawnChunksSize.desc": "更改出生点永加载区块半径", + "rule.spawnChunksSize.extra.0": "定义新半径\n设为0以禁用出生点区块加载", + "category.bugfix": "BUG修复", "category.survival": "生存", diff --git a/src/main/resources/assets/carpet/lang/zh_tw.json b/src/main/resources/assets/carpet/lang/zh_tw.json index a3ede4a1..d45f71d0 100644 --- a/src/main/resources/assets/carpet/lang/zh_tw.json +++ b/src/main/resources/assets/carpet/lang/zh_tw.json @@ -201,6 +201,46 @@ "rule.portalCreativeDelay.name": "創造模式玩家地獄門傳送延遲", "rule.portalCreativeDelay.desc": "創造模式下使用地獄傳送門將同生存時產生延遲", + "rule.commandProfile.name": "遊戲性能監視器", + "rule.commandProfile.desc": "啟用/profile命令以監視遊戲性能", + "rule.commandProfile.extra.0": "/tick命令功能的部分增強版", + + "rule.commandScriptACE.name": "地毯腳本使用權限", + "rule.commandScriptACE.desc": "限制無權限的玩家執行地毯腳本", + "rule.commandScriptACE.extra.0": "無此權限的玩家無法執行/script run指令或者加載腳本", + + "rule.creativeFlyDrag.name": "創造飛行阻力系數", + "rule.creativeFlyDrag.desc": "允許使用本功能更改創造模式下飛行時的空氣阻力系數", + "rule.creativeFlyDrag.extra.0": "阻力增加會降低您的飛行速度,\n所以需要相應地調整速度,\n使用 1.0 阻力系數的時候,使用 11 的速度系數似乎與原版速度相匹配.\n本功能僅在游戏端也安裝地毯MOD(版本>=伺服器安裝版本)的情況下生效", + + "rule.creativeFlySpeed.name": "創造飛行速度系數", + "rule.creativeFlySpeed.desc": "允許使用本功能更改創造模式下飛行速度系數", + "rule.creativeFlySpeed.extra.0": "本功能僅在游戏端也安裝地毯MOD(版本>=服務器安裝版本)的情況下生效", + + "rule.creativeNoClip.name": "創造玩家無碰撞檢測", + "rule.creativeNoClip.desc": "創造玩家可以穿過方塊", + "rule.creativeNoClip.extra.0": "這個選項需要伺服器和游戏端同時支持才生效.\n僅在伺服器設置是無效的,而僅在游戏端設置的情況下\n妳可以利用壹些和活板門有關的技巧來穿墻", + + "rule.fogOff.name": "關閉霧氣", + "rule.fogOff.desc": "啟用本功能以關閉地獄與末地的霧氣", + "rule.fogOff.extra.0": "提高了可見度,但看起來很奇怪", + + "rule.forceloadLimit.name": "永加載區塊上限更改", + "rule.forceloadLimit.desc": "允許使用本功能更改/forceload命令標記的永久加載區塊上限", + + "rule.interactionUpdates.name": "放置方塊產生更新", + "rule.interactionUpdates.desc": "放置方塊是否產生更新的開關,設為false以避免產生更新", + + "rule.language.name": "語言", + "rule.language.desc": "為地毯Mod設置所需語言", + + "rule.portalSurvivalDelay.name": "生存模式地獄門傳送延遲", + "rule.portalSurvivalDelay.desc": "設置生存模式下地獄門傳送的延遲(單位:tick)", + + "rule.spawnChunksSize.name": "出生點永加載區塊半徑", + "rule.spawnChunksSize.desc": "更改出生點永加載區塊半徑", + "rule.spawnChunksSize.extra.0": "定義新半徑\n設為0以禁用出生點區塊加載", + "category.bugfix": "BUG修復", "category.survival": "生存用", diff --git a/src/main/resources/assets/carpet/scripts/event_test.sc b/src/main/resources/assets/carpet/scripts/event_test.sc index c6934911..9e2f14db 100644 --- a/src/main/resources/assets/carpet/scripts/event_test.sc +++ b/src/main/resources/assets/carpet/scripts/event_test.sc @@ -264,3 +264,9 @@ __on_player_switches_slot(player, from, to) -> print(' - from: '+ from); print(' - to: '+ to); ); + +__on_player_swaps_hands(player) -> ( + print(''); + print('__on_player_swaps_hands(player)'); + print(' Player '+player+' swaps hands.') +);