block breaking / active block entity API

This commit is contained in:
gnembon committed 2020-07-21 05:03:19 -03:00
1 parent 786e282889
commit cfc97c5703
5 files changed
+185 -2

No files matched your search

+53
View File
@@ -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. 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?)` ### `query(e, 'facing', order?)`
Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions 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 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. 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)` ### `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 Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
+53
View File
@@ -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. 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?)` ### `query(e, 'facing', order?)`
Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions 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 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. 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)` ### `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 Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
@@ -0,0 +1,12 @@
package carpet.fakes;
import net.minecraft.util.math.BlockPos;
public interface ServerPlayerInteractionManagerInterface
{
BlockPos getCurrentBreakingBlock();
int getCurrentBlockBreakingProgress();
void setBlockBreakingProgress(int progress);
}
@@ -1,5 +1,6 @@
package carpet.mixins; package carpet.mixins;
import carpet.fakes.ServerPlayerInteractionManagerInterface;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
@@ -7,10 +8,12 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.network.ServerPlayerInteractionManager; import net.minecraft.server.network.ServerPlayerInteractionManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -24,10 +27,18 @@ import static carpet.script.CarpetEventServer.Event.PLAYER_INTERACTS_WITH_BLOCK;
@Mixin(ServerPlayerInteractionManager.class) @Mixin(ServerPlayerInteractionManager.class)
public class ServerPlayerInteractionManager_scarpetEventsMixin public class ServerPlayerInteractionManager_scarpetEventsMixin implements ServerPlayerInteractionManagerInterface
{ {
@Shadow public ServerPlayerEntity player; @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( @Inject(method = "tryBreakBlock", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(
value = "INVOKE", 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", 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, blockHitResult); PLAYER_INTERACTS_WITH_BLOCK.onBlockHit(player, hand, blockHitResult);
} }
@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);
}
} }
@@ -5,6 +5,7 @@ import carpet.fakes.ItemEntityInterface;
import carpet.fakes.LivingEntityInterface; import carpet.fakes.LivingEntityInterface;
import carpet.fakes.MobEntityInterface; import carpet.fakes.MobEntityInterface;
import carpet.fakes.HungerManagerInterface; import carpet.fakes.HungerManagerInterface;
import carpet.fakes.ServerPlayerInteractionManagerInterface;
import carpet.helpers.Tracer; import carpet.helpers.Tracer;
import carpet.patches.EntityPlayerMPFake; import carpet.patches.EntityPlayerMPFake;
import carpet.script.CarpetContext; import carpet.script.CarpetContext;
@@ -464,9 +465,32 @@ public class EntityValue extends Value
put("selected_slot", (e, a) -> { put("selected_slot", (e, a) -> {
if (e instanceof PlayerEntity) if (e instanceof PlayerEntity)
return new NumericValue(((PlayerEntity) e).inventory.selectedSlot); 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) -> { put("facing", (e, a) -> {
int index = 0; int index = 0;
if (a != null) if (a != null)
@@ -1033,6 +1057,15 @@ public class EntityValue extends Value
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong()); 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) -> { put("nbt", (e, v) -> {
if (!(e instanceof PlayerEntity)) if (!(e instanceof PlayerEntity))
{ {