From 7c5819741d59a3bc123cd598a40235fba17575a1 Mon Sep 17 00:00:00 2001 From: Dmitry Luk Date: Fri, 28 Jun 2024 20:20:33 +0500 Subject: [PATCH] feat: food system (WIP) --- .../api/client/data/AdventureSettings.java | 3 +- .../player/EntityPlayerBaseComponent.java | 6 +- .../player/EntityPlayerHungerComponent.java | 28 +++++++ .../api/entity/interfaces/EntityPlayer.java | 36 +++++---- .../command/defaults/GameTestCommand.java | 7 ++ .../player/EntityPlayerBaseComponentImpl.java | 4 + .../EntityPlayerHungerComponentImpl.java | 79 +++++++++++++++++++ .../entity/type/EntityTypeInitializer.java | 1 + .../InventoryTransactionPacketProcessor.java | 14 ++-- .../allaymc/server/world/AllayDimension.java | 5 +- 10 files changed, 157 insertions(+), 26 deletions(-) create mode 100644 Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerHungerComponent.java create mode 100644 Allay-Server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerHungerComponentImpl.java diff --git a/Allay-API/src/main/java/org/allaymc/api/client/data/AdventureSettings.java b/Allay-API/src/main/java/org/allaymc/api/client/data/AdventureSettings.java index e9a4c7b4b..9e9b6b432 100644 --- a/Allay-API/src/main/java/org/allaymc/api/client/data/AdventureSettings.java +++ b/Allay-API/src/main/java/org/allaymc/api/client/data/AdventureSettings.java @@ -26,6 +26,7 @@ public final class AdventureSettings { private boolean immutableWorld = false; private boolean showNameTags = true; private boolean autoJump = true; + private boolean dirty = false; public AdventureSettings(EntityPlayer player) { @@ -71,7 +72,7 @@ public void setAutoJump(boolean autoJump) { public void sync() { if (!dirty) return; - UpdateAdventureSettingsPacket packet = new UpdateAdventureSettingsPacket(); + var packet = new UpdateAdventureSettingsPacket(); packet.setAutoJump(autoJump); packet.setImmutableWorld(immutableWorld); packet.setNoMvP(noMVP); diff --git a/Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerBaseComponent.java b/Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerBaseComponent.java index c9b8a29a6..2c9ed4b4b 100644 --- a/Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerBaseComponent.java +++ b/Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerBaseComponent.java @@ -22,6 +22,8 @@ public interface EntityPlayerBaseComponent extends EntityBaseComponent, ChunkLoa double BLOCK_INTERACT_MAX_DV_DIFF = 2.0; + float DEFAULT_MOVEMENT_SPEED = 0.1f; + boolean isSprinting(); void setSprinting(boolean sprinting); @@ -147,9 +149,7 @@ default double getMaxInteractDistance() { return getGameType() == GameType.CREATIVE ? 13 : 7; } - float DEFAULT_MOVEMENT_SPEED = 0.1f; + float getMovementSpeed(); void setMovementSpeed(float speed); - - float getMovementSpeed(); } diff --git a/Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerHungerComponent.java b/Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerHungerComponent.java new file mode 100644 index 000000000..c9e219701 --- /dev/null +++ b/Allay-API/src/main/java/org/allaymc/api/entity/component/player/EntityPlayerHungerComponent.java @@ -0,0 +1,28 @@ +package org.allaymc.api.entity.component.player; + +import org.allaymc.api.entity.component.EntityComponent; + +/** + * Allay Project 28/06/2024 + * + * @author IWareQ + */ +public interface EntityPlayerHungerComponent extends EntityComponent { + void tick(); + + int getFoodLevel(); + + void setFoodLevel(int foodLevel); + + int getFoodSaturationLevel(); + + void setFoodSaturationLevel(int saturationLevel); + + float getFoodTickTimer(); + + void setFoodTickTimer(float foodTickTimer); + + float getFoodExhaustionLevel(); + + void setFoodExhaustionLevel(float foodExhaustionLevel); +} diff --git a/Allay-API/src/main/java/org/allaymc/api/entity/interfaces/EntityPlayer.java b/Allay-API/src/main/java/org/allaymc/api/entity/interfaces/EntityPlayer.java index 2ff1d897f..5712ed750 100644 --- a/Allay-API/src/main/java/org/allaymc/api/entity/interfaces/EntityPlayer.java +++ b/Allay-API/src/main/java/org/allaymc/api/entity/interfaces/EntityPlayer.java @@ -5,10 +5,7 @@ import org.allaymc.api.entity.Entity; import org.allaymc.api.entity.component.common.EntityContainerViewerComponent; import org.allaymc.api.entity.component.common.EntityDamageComponent; -import org.allaymc.api.entity.component.player.EntityPlayerAttributeComponent; -import org.allaymc.api.entity.component.player.EntityPlayerBaseComponent; -import org.allaymc.api.entity.component.player.EntityPlayerContainerHolderComponent; -import org.allaymc.api.entity.component.player.EntityPlayerNetworkComponent; +import org.allaymc.api.entity.component.player.*; import org.allaymc.api.eventbus.event.world.player.PlayerThrowItemEvent; import org.allaymc.api.item.ItemStack; import org.allaymc.api.utils.MathUtils; @@ -29,7 +26,13 @@ public interface EntityPlayer extends EntityPlayerAttributeComponent, EntityPlayerContainerHolderComponent, EntityContainerViewerComponent, - EntityDamageComponent { + EntityDamageComponent, + EntityPlayerHungerComponent { + + @Override + default void tick() { + Entity.super.tick(); + } default T getReachableContainer(FullContainerType slotType) { var container = getOpenedContainer(slotType); @@ -50,13 +53,11 @@ default boolean tryDropItemInHand(int count) { default boolean tryDropItem(FullContainerType containerType, int slot, int count) { var container = getReachableContainer(containerType); if (container == null) return false; + var item = container.getItemStack(slot); - if (item.getItemType() == AIR_TYPE) { - return false; - } - if (item.getCount() < count) { - return false; - } + if (item.getItemType() == AIR_TYPE) return false; + if (item.getCount() < count) return false; + forceDropItem(container, slot, count); return true; } @@ -65,9 +66,8 @@ default void forceDropItem(Container container, int slot, int count) { var item = container.getItemStack(slot); var event = new PlayerThrowItemEvent(this, item); getWorld().getEventBus().callEvent(event); - if (event.isCancelled()) { - return; - } + if (event.isCancelled()) return; + ItemStack droppedItemStack; if (item.getCount() > count) { item.setCount(item.getCount() - count); @@ -79,13 +79,19 @@ default void forceDropItem(Container container, int slot, int count) { item = EMPTY_SLOT_PLACE_HOLDER; container.setItemStack(slot, item); } + dropItemInPlayerPos(droppedItemStack); } default void dropItemInPlayerPos(ItemStack itemStack) { var playerLoc = getLocation(); var dimension = playerLoc.dimension(); - dimension.dropItem(itemStack, playerLoc.add(0, this.getEyeHeight() - 0.25f, 0, new Vector3f()), MathUtils.getDirectionVector(playerLoc.yaw(), playerLoc.pitch()).mul(0.5f), 40); + dimension.dropItem( + itemStack, + playerLoc.add(0, this.getEyeHeight() - 0.25f, 0, new Vector3f()), + MathUtils.getDirectionVector(playerLoc.yaw(), playerLoc.pitch()).mul(0.5f), + 40 + ); } default ItemStack getItemInHand() { diff --git a/Allay-Server/src/main/java/org/allaymc/server/command/defaults/GameTestCommand.java b/Allay-Server/src/main/java/org/allaymc/server/command/defaults/GameTestCommand.java index 0c8103765..aafc8cce1 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/command/defaults/GameTestCommand.java +++ b/Allay-Server/src/main/java/org/allaymc/server/command/defaults/GameTestCommand.java @@ -154,6 +154,13 @@ public void prepareCommandTree(CommandTree tree) { return context.success(); }, SenderType.PLAYER) .root() + .key("food") + .exec((context, player) -> { + player.setFoodLevel(Math.min(20, player.getFoodLevel() + 5)); // bread 5 food + player.setFoodSaturationLevel(player.getFoodSaturationLevel() + 6); // bread 6 saturation + return context.success(); + }, SenderType.PLAYER) + .root() .key("setperm") .str("perm") .bool("value") diff --git a/Allay-Server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java b/Allay-Server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java index 7f7f4db3d..d9dfde765 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java +++ b/Allay-Server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java @@ -21,6 +21,7 @@ import org.allaymc.api.entity.component.event.PlayerLoggedInEvent; import org.allaymc.api.entity.component.item.EntityItemBaseComponent; import org.allaymc.api.entity.component.player.EntityPlayerBaseComponent; +import org.allaymc.api.entity.component.player.EntityPlayerHungerComponent; import org.allaymc.api.entity.component.player.EntityPlayerNetworkComponent; import org.allaymc.api.entity.init.EntityInitInfo; import org.allaymc.api.entity.interfaces.EntityItem; @@ -78,6 +79,8 @@ public class EntityPlayerBaseComponentImpl extends EntityBaseComponentImplHunger + */ +@Getter +@Setter +public class EntityPlayerHungerComponentImpl implements EntityPlayerHungerComponent { + @ComponentIdentifier + public static final Identifier IDENTIFIER = new Identifier("minecraft:player_hunger_component"); + + @ComponentedObject + protected EntityPlayer player; + + private int foodLevel = 20; + private int foodSaturationLevel = 20; + + private float foodTickTimer; + private float foodExhaustionLevel = 5; + + @Override + public void tick() { + if (player.isSpawned()) { + var needSend = false; + + var hunger = player.getAttribute(AttributeType.PLAYER_HUNGER); + if (hunger.getCurrentValue() != foodLevel) { + hunger.setCurrentValue(foodLevel); + needSend = true; + } + + var saturation = player.getAttribute(AttributeType.PLAYER_SATURATION); + if (saturation.getCurrentValue() != foodSaturationLevel) { + saturation.setCurrentValue(foodSaturationLevel); + needSend = true; + } + + var exhaustion = player.getAttribute(AttributeType.PLAYER_EXHAUSTION); + if (exhaustion.getCurrentValue() != foodExhaustionLevel) { + exhaustion.setCurrentValue(foodExhaustionLevel); + needSend = true; + } + + if (needSend) player.sendAttributesToClient(); + } + } + + @Override + public void setFoodTickTimer(float foodTickTimer) { + this.foodTickTimer = Math.max(foodTickTimer, 0); + } + + @Override + public void setFoodExhaustionLevel(float foodExhaustionLevel) { + while (foodExhaustionLevel >= 4) { + foodExhaustionLevel -= 4; + + if (this.foodSaturationLevel > 0) { + this.foodSaturationLevel--; + } else { + this.foodLevel = Math.max(--this.foodLevel, 0); + } + } + + this.foodExhaustionLevel = foodExhaustionLevel; + } +} diff --git a/Allay-Server/src/main/java/org/allaymc/server/entity/type/EntityTypeInitializer.java b/Allay-Server/src/main/java/org/allaymc/server/entity/type/EntityTypeInitializer.java index 89f739d31..1e760b150 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/entity/type/EntityTypeInitializer.java +++ b/Allay-Server/src/main/java/org/allaymc/server/entity/type/EntityTypeInitializer.java @@ -41,6 +41,7 @@ public static void initPlayer() { .addComponent(EntityPlayerContainerHolderComponentImpl::new, EntityPlayerContainerHolderComponentImpl.class) .addComponent(EntityPlayerContainerViewerComponentImpl::new, EntityPlayerContainerViewerComponentImpl.class) .addComponent(EntityPlayerDamageComponentImpl::new, EntityDamageComponentImpl.class) + .addComponent(EntityPlayerHungerComponentImpl::new, EntityPlayerHungerComponentImpl.class) .build(); } diff --git a/Allay-Server/src/main/java/org/allaymc/server/network/processor/InventoryTransactionPacketProcessor.java b/Allay-Server/src/main/java/org/allaymc/server/network/processor/InventoryTransactionPacketProcessor.java index 87153f458..eb305270b 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/network/processor/InventoryTransactionPacketProcessor.java +++ b/Allay-Server/src/main/java/org/allaymc/server/network/processor/InventoryTransactionPacketProcessor.java @@ -69,7 +69,6 @@ public void handleSync(EntityPlayer player, InventoryTransactionPacket packet) { dimension.sendBlockUpdateTo(blockStateReplaced, placeBlockPos, 0, player); } } - } } case ITEM_USE_CLICK_AIR -> { @@ -114,11 +113,16 @@ public void handleSync(EntityPlayer player, InventoryTransactionPacket packet) { } // TODO: The current implementation is buggy - for (var action : packet.getActions()) { - if (!action.getSource().getType().equals(InventorySource.Type.WORLD_INTERACTION)) continue; - if (!action.getSource().getFlag().equals(InventorySource.Flag.DROP_ITEM)) continue; + var first = packet.getActions().getFirst(); + var last = packet.getActions().getLast(); + if ( + first.getSource().getType() == InventorySource.Type.WORLD_INTERACTION && + first.getSource().getFlag() == InventorySource.Flag.DROP_ITEM && + last.getSource().getType() == InventorySource.Type.CONTAINER && + last.getSource().getFlag() == InventorySource.Flag.NONE + ) { // Do not ask me why Mojang still use the old item transaction pk even the server-auth inv was enabled - var count = action.getToItem().getCount(); + var count = first.getToItem().getCount(); player.tryDropItemInHand(count); } } diff --git a/Allay-Server/src/main/java/org/allaymc/server/world/AllayDimension.java b/Allay-Server/src/main/java/org/allaymc/server/world/AllayDimension.java index 28db2761f..b1b137e17 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/world/AllayDimension.java +++ b/Allay-Server/src/main/java/org/allaymc/server/world/AllayDimension.java @@ -114,12 +114,13 @@ public void breakBlock(int x, int y, int z, ItemStack usedItem, EntityPlayer pla pk.setPosition(Vector3f.from(x + 0.5f, y + 0.5f, z + 0.5f)); pk.setData(block.blockStateHash()); getChunkService().getChunkByLevelPos(x, z).addChunkPacket(pk); + block.getBehavior().onBreak( new BlockStateWithPos(block, new Position3i(x, y, z, this), 0), - usedItem, - player + usedItem, player ); setBlockState(x, y, z, AIR_TYPE.getDefaultState()); + player.setFoodExhaustionLevel(player.getFoodExhaustionLevel() + 0.005f); } @Override