diff --git a/CHANGELOG.md b/CHANGELOG.md index 11ba364bdf..148f0295f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ Unless otherwise specified, any version comparison below is the comparison of se - (API) Added `BlockGrowEvent` which will be called when crops grow. - (API) Added two overloads `LightService#getInternalLight(Vector3ic)` and `LightService#getSkyLight(Vector3ic)`, they have the same functionality as `LightService#getXXXLight(int, int, int)`. +- (API) Implemented beacon block, and several related interfaces are added to api module. +- (API) `BlockContainer#getBlockPos` and `BlockContainer#setBlockPos` now return/require `Position3ic` instead of + `Vector3ic`, this enables us to get the dimension information of a `BlockContainer`. - Implemented trapdoor except redstone feature (Redstone feature requires the implementation of redstone system). - Implemented sponge and wet sponge. - Implemented farmland and hoe. @@ -50,6 +53,8 @@ Unless otherwise specified, any version comparison below is the comparison of se - (API) Removed `ScoreboardService#ServerEventListener` as it is not supposed to be touched by plugin. - Removed useless class `PackageClassLoaderUtils`, dependency `org.reflections.reflections` is also removed. - Added `-dev` suffix to api version in development build. +- Changed `ContainerActionProcessorHolder` to a final class instead of an interface, because this abstraction is + meaningless. ### Fixed @@ -57,6 +62,7 @@ Unless otherwise specified, any version comparison below is the comparison of se to be added. - Fixed the bug that interacting with door doesn't have any sound. - Waxing copper-made block using honeycomb won't call `BlockFadeEvent` now. +- Fixed the bug that player can still open enchant table even if he is sneaking. ## [0.1.2](https://github.com/AllayMC/Allay/releases/tag/0.1.2) (API 0.3.0) - 2024-12-31 diff --git a/api/src/main/java/org/allaymc/api/block/interfaces/BlockBeaconBehavior.java b/api/src/main/java/org/allaymc/api/block/interfaces/BlockBeaconBehavior.java index 4f3aed8ac0..352b2ceac8 100644 --- a/api/src/main/java/org/allaymc/api/block/interfaces/BlockBeaconBehavior.java +++ b/api/src/main/java/org/allaymc/api/block/interfaces/BlockBeaconBehavior.java @@ -1,6 +1,8 @@ package org.allaymc.api.block.interfaces; import org.allaymc.api.block.BlockBehavior; +import org.allaymc.api.block.component.BlockEntityHolderComponent; +import org.allaymc.api.blockentity.interfaces.BlockEntityBeacon; -public interface BlockBeaconBehavior extends BlockBehavior { +public interface BlockBeaconBehavior extends BlockBehavior, BlockEntityHolderComponent { } diff --git a/api/src/main/java/org/allaymc/api/block/tag/BlockCustomTags.java b/api/src/main/java/org/allaymc/api/block/tag/BlockCustomTags.java index ae4f75e9b0..f5caa48c64 100644 --- a/api/src/main/java/org/allaymc/api/block/tag/BlockCustomTags.java +++ b/api/src/main/java/org/allaymc/api/block/tag/BlockCustomTags.java @@ -14,6 +14,8 @@ public interface BlockCustomTags { BlockTag SOUL_FIRE_CONVERTER = create("allay:soul_fire_converter"); + BlockTag BEACON_BASE = create("allay:beacon_base"); + BlockTag REPLACEABLE = create("allay:replaceable"); BlockTag WOOL = create("allay:wool"); diff --git a/api/src/main/java/org/allaymc/api/blockentity/component/BlockEntityBeaconBaseComponent.java b/api/src/main/java/org/allaymc/api/blockentity/component/BlockEntityBeaconBaseComponent.java new file mode 100644 index 0000000000..6878c4a54d --- /dev/null +++ b/api/src/main/java/org/allaymc/api/blockentity/component/BlockEntityBeaconBaseComponent.java @@ -0,0 +1,64 @@ +package org.allaymc.api.blockentity.component; + +import org.allaymc.api.entity.effect.EffectType; + +/** + * @author daoge_cmd + */ +public interface BlockEntityBeaconBaseComponent extends BlockEntityBaseComponent { + /** + * Get the level of the beacon. + *

+ * level is the amount of the pyramid's levels, it is defined by the mineral blocks which build up the + * pyramid, and can be 0-4. + * + * @return the level of the beacon. + */ + int getLevel(); + + /** + * Get the primary effect of the beacon. + * + * @return the primary effect of the beacon, or {@code null} if no primary effect. + */ + EffectType getPrimaryEffect(); + + /** + * Set the primary effect of the beacon. + * + * @param effectType the primary effect of the beacon. + */ + void setPrimaryEffect(EffectType effectType); + + /** + * Check if the beacon has a primary effect. + * + * @return {@code true} if the beacon has a primary effect, otherwise {@code false}. + */ + default boolean hasPrimaryEffect() { + return getPrimaryEffect() != null; + } + + /** + * Get the secondary effect of the beacon. + * + * @return the secondary effect of the beacon, or {@code null} if no secondary effect. + */ + EffectType getSecondaryEffect(); + + /** + * Set the secondary effect of the beacon. + * + * @param effectType the secondary effect of the beacon. + */ + void setSecondaryEffect(EffectType effectType); + + /** + * Check if the beacon has a secondary effect. + * + * @return {@code true} if the beacon has a secondary effect, otherwise {@code false}. + */ + default boolean hasSecondaryEffect() { + return getSecondaryEffect() != null; + } +} diff --git a/api/src/main/java/org/allaymc/api/blockentity/interfaces/BlockEntityBeacon.java b/api/src/main/java/org/allaymc/api/blockentity/interfaces/BlockEntityBeacon.java new file mode 100644 index 0000000000..14603fc63a --- /dev/null +++ b/api/src/main/java/org/allaymc/api/blockentity/interfaces/BlockEntityBeacon.java @@ -0,0 +1,10 @@ +package org.allaymc.api.blockentity.interfaces; + +import org.allaymc.api.blockentity.BlockEntity; +import org.allaymc.api.blockentity.component.BlockEntityBeaconBaseComponent; + +/** + * @author daoge_cmd + */ +public interface BlockEntityBeacon extends BlockEntity, BlockEntityBeaconBaseComponent { +} diff --git a/api/src/main/java/org/allaymc/api/blockentity/type/BlockEntityTypes.java b/api/src/main/java/org/allaymc/api/blockentity/type/BlockEntityTypes.java index 98ea747c90..a2348267ed 100644 --- a/api/src/main/java/org/allaymc/api/blockentity/type/BlockEntityTypes.java +++ b/api/src/main/java/org/allaymc/api/blockentity/type/BlockEntityTypes.java @@ -16,4 +16,5 @@ public final class BlockEntityTypes { public static BlockEntityType HANGING_SIGN; public static BlockEntityType ENCHANT_TABLE; public static BlockEntityType JUKEBOX; + public static BlockEntityType BEACON; } diff --git a/api/src/main/java/org/allaymc/api/container/FullContainerType.java b/api/src/main/java/org/allaymc/api/container/FullContainerType.java index c914d496de..5863f25c83 100644 --- a/api/src/main/java/org/allaymc/api/container/FullContainerType.java +++ b/api/src/main/java/org/allaymc/api/container/FullContainerType.java @@ -122,6 +122,13 @@ public record FullContainerType( .mapRangedNetworkSlotIndex(14, 15, 0) .build(); + public static final FullContainerType BEACON = builder() + .id(ContainerType.BEACON) + .size(1) + .mapAllSlotToType(ContainerSlotType.BEACON_PAYMENT) + .mapNetworkSlotIndex(27, BeaconContainer.BEACON_PAYMENT_SLOT) + .build(); + public FullContainerType(int id, ContainerSlotType[] slotTypeTable, Set heldSlotTypes, BiMap networkSlotIndexMapper) { this.id = id; this.slotTypeTable = slotTypeTable; diff --git a/api/src/main/java/org/allaymc/api/container/impl/BeaconContainer.java b/api/src/main/java/org/allaymc/api/container/impl/BeaconContainer.java new file mode 100644 index 0000000000..73daef32fa --- /dev/null +++ b/api/src/main/java/org/allaymc/api/container/impl/BeaconContainer.java @@ -0,0 +1,24 @@ +package org.allaymc.api.container.impl; + +import org.allaymc.api.container.FullContainerType; +import org.allaymc.api.item.ItemStack; + +/** + * @author daoge_cmd + */ +public class BeaconContainer extends BlockContainer { + public static final int BEACON_PAYMENT_SLOT = 0; + + public BeaconContainer() { + super(FullContainerType.BEACON); + } + + /** + * Get the item stack in the payment slot. + * + * @return The item stack in the payment slot. + */ + public ItemStack getBeaconPayment() { + return this.getItemStack(BEACON_PAYMENT_SLOT); + } +} diff --git a/api/src/main/java/org/allaymc/api/container/impl/BlockContainer.java b/api/src/main/java/org/allaymc/api/container/impl/BlockContainer.java index 6110058799..cce14cd6b9 100644 --- a/api/src/main/java/org/allaymc/api/container/impl/BlockContainer.java +++ b/api/src/main/java/org/allaymc/api/container/impl/BlockContainer.java @@ -5,7 +5,7 @@ import org.allaymc.api.container.BaseContainer; import org.allaymc.api.container.Container; import org.allaymc.api.container.FullContainerType; -import org.joml.Vector3ic; +import org.allaymc.api.math.position.Position3ic; /** * @author IWareQ @@ -13,7 +13,7 @@ public class BlockContainer extends BaseContainer { @Getter @Setter - protected Vector3ic blockPos; + protected Position3ic blockPos; public BlockContainer(FullContainerType containerType) { super(containerType); diff --git a/api/src/main/java/org/allaymc/api/container/impl/CraftingGridContainer.java b/api/src/main/java/org/allaymc/api/container/impl/CraftingGridContainer.java index ebe4fc379e..01a3099bbc 100644 --- a/api/src/main/java/org/allaymc/api/container/impl/CraftingGridContainer.java +++ b/api/src/main/java/org/allaymc/api/container/impl/CraftingGridContainer.java @@ -3,7 +3,7 @@ import lombok.Getter; import org.allaymc.api.container.FullContainerType; import org.allaymc.api.item.recipe.input.CraftingInput; -import org.joml.Vector3ic; +import org.allaymc.api.math.position.Position3ic; /** * @author daoge_cmd @@ -32,12 +32,12 @@ public CraftingInput createCraftingInput() { } @Override - public Vector3ic getBlockPos() { + public Position3ic getBlockPos() { throw new UnsupportedOperationException(); } @Override - public void setBlockPos(Vector3ic blockPos) { + public void setBlockPos(Position3ic blockPos) { throw new UnsupportedOperationException(); } } diff --git a/api/src/main/java/org/allaymc/api/entity/effect/EffectType.java b/api/src/main/java/org/allaymc/api/entity/effect/EffectType.java index 272331e2d2..fb5ed33c9d 100644 --- a/api/src/main/java/org/allaymc/api/entity/effect/EffectType.java +++ b/api/src/main/java/org/allaymc/api/entity/effect/EffectType.java @@ -9,36 +9,21 @@ */ public interface EffectType { /** - * Creates a new instance of this effect with the given amplifier and default duration. - * - * @param amplifier The amplifier of the effect. - * - * @return A new instance of this effect. + * @see #createInstance(int, int, boolean, boolean) */ default EffectInstance createInstance(int amplifier) { return createInstance(amplifier, 15); } /** - * Creates a new instance of this effect with the given amplifier and duration. - * - * @param amplifier The amplifier of the effect. - * @param duration The duration of the effect. - * - * @return A new instance of this effect. + * @see #createInstance(int, int, boolean, boolean) */ default EffectInstance createInstance(int amplifier, int duration) { return createInstance(amplifier, duration, true); } /** - * Creates a new instance of this effect with the given amplifier, duration, and visibility. - * - * @param amplifier The amplifier of the effect. - * @param duration The duration of the effect. - * @param visible Whether the effect is visible. - * - * @return A new instance of this effect. + * @see #createInstance(int, int, boolean, boolean) */ default EffectInstance createInstance(int amplifier, int duration, boolean visible) { return createInstance(amplifier, duration, false, visible); @@ -48,8 +33,8 @@ default EffectInstance createInstance(int amplifier, int duration, boolean visib * Creates a new instance of this effect with the given amplifier, duration, ambient and visibility. * * @param amplifier The amplifier of the effect. - * @param duration The duration of the effect. - * @param ambient Whether the effect is ambient. + * @param duration The duration of the effect in ticks. + * @param ambient Whether the effect is ambient. Note that ambient 0 is level 1, and ambient 1 is level 2. * @param visible Whether the effect is visible. * * @return A new instance of this effect. diff --git a/api/src/main/java/org/allaymc/api/item/tag/ItemCustomTags.java b/api/src/main/java/org/allaymc/api/item/tag/ItemCustomTags.java index a2b5b8304b..e92050e8f4 100644 --- a/api/src/main/java/org/allaymc/api/item/tag/ItemCustomTags.java +++ b/api/src/main/java/org/allaymc/api/item/tag/ItemCustomTags.java @@ -12,6 +12,8 @@ public interface ItemCustomTags { Map NAME_TO_TAG = new HashMap<>(); + ItemTag BEACON_PAYMENT = create("allay:beacon_payment"); + static ItemTag create(String name) { var tag = new ItemTag(name); NAME_TO_TAG.put(name, tag); diff --git a/data/resources/block_tags_custom.json b/data/resources/block_tags_custom.json index ecccd5659e..2bceb7d46d 100644 --- a/data/resources/block_tags_custom.json +++ b/data/resources/block_tags_custom.json @@ -133,5 +133,12 @@ "allay:lava": [ "minecraft:lava", "minecraft:flowing_lava" + ], + "allay:beacon_base": [ + "minecraft:iron_block", + "minecraft:gold_block", + "minecraft:diamond_block", + "minecraft:emerald_block", + "minecraft:netherite_block" ] } \ No newline at end of file diff --git a/data/resources/item_tags_custom.json b/data/resources/item_tags_custom.json index 9e26dfeeb6..2836dce2cc 100644 --- a/data/resources/item_tags_custom.json +++ b/data/resources/item_tags_custom.json @@ -1 +1,9 @@ -{} \ No newline at end of file +{ + "allay:beacon_payment": [ + "minecraft:iron_ingot", + "minecraft:gold_ingot", + "minecraft:diamond", + "minecraft:emerald", + "minecraft:netherite_ingot" + ] +} \ No newline at end of file diff --git a/server/src/jmh/java/org/allaymc/server/ChunkJMHTest.java b/server/src/jmh/java/org/allaymc/server/ChunkJMHTest.java index 209e4417bc..b29c08e94b 100644 --- a/server/src/jmh/java/org/allaymc/server/ChunkJMHTest.java +++ b/server/src/jmh/java/org/allaymc/server/ChunkJMHTest.java @@ -26,6 +26,7 @@ public class ChunkJMHTest { @Setup public void init() throws MissingImplementationException { + Allay.initI18n(); Allay.initAllay(); chunk = AllayUnsafeChunk.builder().newChunk(0, 0, DimensionInfo.OVERWORLD).toSafeChunk(); for (int i = 0; i < 16; i++) { diff --git a/server/src/jmh/java/org/allaymc/server/LightServiceJMHTest.java b/server/src/jmh/java/org/allaymc/server/LightServiceJMHTest.java index 8fcef72b5b..a7e52d1ab9 100644 --- a/server/src/jmh/java/org/allaymc/server/LightServiceJMHTest.java +++ b/server/src/jmh/java/org/allaymc/server/LightServiceJMHTest.java @@ -1,5 +1,6 @@ package org.allaymc.server; +import org.allaymc.api.MissingImplementationException; import org.allaymc.api.world.DimensionInfo; import org.allaymc.api.world.Weather; import org.allaymc.server.world.chunk.AllayUnsafeChunk; @@ -23,7 +24,9 @@ public class LightServiceJMHTest { private AllayLightService lightService; @Setup - public void setup() { + public void setup() throws MissingImplementationException { + Allay.initI18n(); + Allay.initAllay(); lightService = new AllayLightService(DimensionInfo.THE_END, () -> 0, () -> Set.of(Weather.CLEAR)); for (int x = -3; x <= 3; x++) { for (int z = -3; z <= 3; z++) { diff --git a/server/src/jmh/java/org/allaymc/server/ThroughList2ArrayJMHTest.java b/server/src/jmh/java/org/allaymc/server/ThroughList2ArrayJMHTest.java index 9f0ff4a9b9..ac5a46a213 100644 --- a/server/src/jmh/java/org/allaymc/server/ThroughList2ArrayJMHTest.java +++ b/server/src/jmh/java/org/allaymc/server/ThroughList2ArrayJMHTest.java @@ -25,6 +25,7 @@ public class ThroughList2ArrayJMHTest { @Setup public void setup() throws MissingImplementationException { + Allay.initI18n(); Allay.initAllay(); data1[0] = BlockPropertyTypes.AGE_16.createValue(5); data1[1] = BlockPropertyTypes.ACTIVE.createValue(false); diff --git a/server/src/main/java/org/allaymc/server/block/component/BlockBeaconBaseComponentImpl.java b/server/src/main/java/org/allaymc/server/block/component/BlockBeaconBaseComponentImpl.java new file mode 100644 index 0000000000..7a5b3a6cda --- /dev/null +++ b/server/src/main/java/org/allaymc/server/block/component/BlockBeaconBaseComponentImpl.java @@ -0,0 +1,35 @@ +package org.allaymc.server.block.component; + +import org.allaymc.api.block.BlockBehavior; +import org.allaymc.api.block.dto.PlayerInteractInfo; +import org.allaymc.api.block.type.BlockType; +import org.allaymc.api.container.FullContainerType; +import org.allaymc.api.item.ItemStack; +import org.allaymc.api.math.position.Position3i; +import org.allaymc.api.world.Dimension; + +/** + * @author daoge_cmd + */ +public class BlockBeaconBaseComponentImpl extends BlockBaseComponentImpl { + public BlockBeaconBaseComponentImpl(BlockType blockType) { + super(blockType); + } + + @Override + public boolean onInteract(ItemStack itemStack, Dimension dimension, PlayerInteractInfo interactInfo) { + if (super.onInteract(itemStack, dimension, interactInfo)) { + return true; + } + + var player = interactInfo.player(); + if (player.isSneaking()) { + return false; + } + + var beaconContainer = player.getContainer(FullContainerType.BEACON); + beaconContainer.setBlockPos(new Position3i(interactInfo.clickedBlockPos(), interactInfo.player().getDimension())); + beaconContainer.addViewer(player); + return true; + } +} diff --git a/server/src/main/java/org/allaymc/server/block/component/BlockCraftingTableBaseComponentImpl.java b/server/src/main/java/org/allaymc/server/block/component/BlockCraftingTableBaseComponentImpl.java index ae7cd6df69..004a5ed7ae 100644 --- a/server/src/main/java/org/allaymc/server/block/component/BlockCraftingTableBaseComponentImpl.java +++ b/server/src/main/java/org/allaymc/server/block/component/BlockCraftingTableBaseComponentImpl.java @@ -5,6 +5,7 @@ import org.allaymc.api.block.type.BlockType; import org.allaymc.api.container.FullContainerType; import org.allaymc.api.item.ItemStack; +import org.allaymc.api.math.position.Position3i; import org.allaymc.api.world.Dimension; /** @@ -23,7 +24,7 @@ public boolean onInteract(ItemStack itemStack, Dimension dimension, PlayerIntera if (player.isSneaking()) return false; var craftingTableContainer = player.getContainer(FullContainerType.CRAFTING_TABLE); - craftingTableContainer.setBlockPos(interactInfo.clickedBlockPos()); + craftingTableContainer.setBlockPos(new Position3i(interactInfo.clickedBlockPos(), interactInfo.player().getDimension())); craftingTableContainer.addViewer(player); return true; } diff --git a/server/src/main/java/org/allaymc/server/block/component/BlockEnchantingTableBaseComponentImpl.java b/server/src/main/java/org/allaymc/server/block/component/BlockEnchantingTableBaseComponentImpl.java index 2906e7e67a..223be6f722 100644 --- a/server/src/main/java/org/allaymc/server/block/component/BlockEnchantingTableBaseComponentImpl.java +++ b/server/src/main/java/org/allaymc/server/block/component/BlockEnchantingTableBaseComponentImpl.java @@ -5,6 +5,7 @@ import org.allaymc.api.block.type.BlockType; import org.allaymc.api.container.FullContainerType; import org.allaymc.api.item.ItemStack; +import org.allaymc.api.math.position.Position3i; import org.allaymc.api.world.Dimension; /** @@ -20,8 +21,12 @@ public boolean onInteract(ItemStack itemStack, Dimension dimension, PlayerIntera if (super.onInteract(itemStack, dimension, interactInfo)) return true; var player = interactInfo.player(); + if (player.isSneaking()) { + return false; + } + var enchantTableContainer = player.getContainer(FullContainerType.ENCHANT_TABLE); - enchantTableContainer.setBlockPos(interactInfo.clickedBlockPos()); + enchantTableContainer.setBlockPos(new Position3i(interactInfo.clickedBlockPos(), interactInfo.player().getDimension())); enchantTableContainer.addViewer(player); return true; } diff --git a/server/src/main/java/org/allaymc/server/block/impl/BlockBeaconBehaviorImpl.java b/server/src/main/java/org/allaymc/server/block/impl/BlockBeaconBehaviorImpl.java index 014e1853f7..774c62ef13 100644 --- a/server/src/main/java/org/allaymc/server/block/impl/BlockBeaconBehaviorImpl.java +++ b/server/src/main/java/org/allaymc/server/block/impl/BlockBeaconBehaviorImpl.java @@ -1,12 +1,19 @@ package org.allaymc.server.block.impl; +import lombok.experimental.Delegate; +import org.allaymc.api.block.component.BlockEntityHolderComponent; import org.allaymc.api.block.interfaces.BlockBeaconBehavior; +import org.allaymc.api.blockentity.interfaces.BlockEntityBeacon; import org.allaymc.api.component.interfaces.Component; import org.allaymc.server.component.interfaces.ComponentProvider; import java.util.List; public class BlockBeaconBehaviorImpl extends BlockBehaviorImpl implements BlockBeaconBehavior { + + @Delegate + protected BlockEntityHolderComponent blockEntityHolderComponent; + public BlockBeaconBehaviorImpl( List> componentProviders) { super(componentProviders); diff --git a/server/src/main/java/org/allaymc/server/block/type/BlockTypeInitializer.java b/server/src/main/java/org/allaymc/server/block/type/BlockTypeInitializer.java index 81338fc39c..c199e2d3a6 100644 --- a/server/src/main/java/org/allaymc/server/block/type/BlockTypeInitializer.java +++ b/server/src/main/java/org/allaymc/server/block/type/BlockTypeInitializer.java @@ -1109,4 +1109,13 @@ public static void initMelonBlock() { .setBaseComponentSupplier(BlockPumpkinBaseComponentImpl::new) .build(); } + + public static void initBeacon() { + BlockTypes.BEACON = AllayBlockType + .builder(BlockBeaconBehaviorImpl.class) + .vanillaBlock(BlockId.BEACON) + .bindBlockEntity(BlockEntityTypes.BEACON) + .setBaseComponentSupplier(BlockBeaconBaseComponentImpl::new) + .build(); + } } diff --git a/server/src/main/java/org/allaymc/server/blockentity/component/BlockEntityBeaconBaseComponentImpl.java b/server/src/main/java/org/allaymc/server/blockentity/component/BlockEntityBeaconBaseComponentImpl.java new file mode 100644 index 0000000000..2b6d0d7c2f --- /dev/null +++ b/server/src/main/java/org/allaymc/server/blockentity/component/BlockEntityBeaconBaseComponentImpl.java @@ -0,0 +1,171 @@ +package org.allaymc.server.blockentity.component; + +import lombok.Getter; +import lombok.Setter; +import org.allaymc.api.block.tag.BlockCustomTags; +import org.allaymc.api.blockentity.component.BlockEntityBeaconBaseComponent; +import org.allaymc.api.blockentity.initinfo.BlockEntityInitInfo; +import org.allaymc.api.entity.effect.EffectInstance; +import org.allaymc.api.entity.effect.EffectType; +import org.allaymc.api.entity.effect.type.EffectTypes; +import org.allaymc.api.registry.Registries; +import org.cloudburstmc.nbt.NbtMap; + +/** + * @author daoge_cmd + */ +@Getter +public class BlockEntityBeaconBaseComponentImpl extends BlockEntityBaseComponentImpl implements BlockEntityBeaconBaseComponent { + + protected static final String TAG_LEVELS = "Levels"; + protected static final String TAG_PRIMARY = "Primary"; + protected static final String TAG_SECONDARY = "Secondary"; + + protected int level; + @Setter + protected EffectType primaryEffect; + @Setter + protected EffectType secondaryEffect; + + public BlockEntityBeaconBaseComponentImpl(BlockEntityInitInfo initInfo) { + super(initInfo); + } + + @Override + public NbtMap saveNBT() { + return super.saveNBT() + .toBuilder() + .putInt(TAG_LEVELS, level) + .putInt(TAG_PRIMARY, primaryEffect != null ? primaryEffect.getId() : 0) + .putInt(TAG_SECONDARY, secondaryEffect != null ? secondaryEffect.getId() : 0) + .build(); + } + + @Override + public void loadNBT(NbtMap nbt) { + super.loadNBT(nbt); + nbt.listenForInt(TAG_LEVELS, value -> level = value); + nbt.listenForInt(TAG_PRIMARY, value -> primaryEffect = Registries.EFFECTS.getByK1(value)); + nbt.listenForInt(TAG_SECONDARY, value -> secondaryEffect = Registries.EFFECTS.getByK1(value)); + } + + @Override + public void tick(long currentTick) { + super.tick(currentTick); + if (currentTick % 80 != 0) { + // Recalculating pyramid level and powering up players in range once every 4 seconds + return; + } + + var before = level; + this.level = calculateLevel(); + if (this.level != before) { + sendBlockEntityDataPacketToViewers(); + } + if (this.level != 0 && !isObstructed()) { + broadcastBeaconEffects(); + } + } + + /** + * Determine the players in range which could receive the beacon's powers, and determines the powers (effects) + * that these players could get. Afterward, the players in range that are beaconAffected get their according effect(s). + */ + protected void broadcastBeaconEffects() { + var ticks = (9 + level * 2) * 20; + if (level == 4) { + ticks -= 20; + } + + // Establishing what effects are active with the current amount of beacon levels + EffectType validPrimaryEffect = primaryEffect; + EffectType validSecondaryEffect; + switch (level) { + case 0: + validPrimaryEffect = null; + case 1: + if (validPrimaryEffect == EffectTypes.RESISTANCE || + validPrimaryEffect == EffectTypes.JUMP_BOOST || + validPrimaryEffect == EffectTypes.STRENGTH) { + validPrimaryEffect = null; + } + case 2: + if (validPrimaryEffect == EffectTypes.STRENGTH) { + validPrimaryEffect = null; + } + case 3: + // Accept all effects for primary, but leave secondary as null + default: + validSecondaryEffect = secondaryEffect; + } + + EffectInstance primaryEffectInstance = null; + EffectInstance secondaryEffectInstance = null; + if (validPrimaryEffect != null) { + primaryEffectInstance = validPrimaryEffect.createInstance(0, ticks); + // Secondary power can only be set if the primary power is set + if (validSecondaryEffect != null) { + // It is possible to select 2 primary powers if the beacon's level is 4. This then means that the effect + // should get a level of 2 + if (validPrimaryEffect == validSecondaryEffect) { + primaryEffectInstance = validPrimaryEffect.createInstance(1, ticks); + } else { + secondaryEffectInstance = validSecondaryEffect.createInstance(0, ticks); + } + } + } + + // Finding entities in range + var r = 10 + (level * 10); + var playersInRange = position.dimension() + .getPlayers() + .stream() + .filter(entity -> { + var ex = entity.getLocation().x(); + var ez = entity.getLocation().z(); + return Math.abs(ex - position.x()) <= r && Math.abs(ez - position.z()) <= r; + }) + .toList(); + + for (var player : playersInRange) { + if (primaryEffectInstance != null) { + player.addEffect(primaryEffectInstance); + } + if (secondaryEffectInstance != null) { + player.addEffect(secondaryEffectInstance); + } + } + } + + /** + * Calculate the level of the beacon's pyramid and returns it. The level can be 0-4. + * + * @return the level of the beacon's pyramid. + */ + protected int calculateLevel() { + var lvl = 0; + var iter = 1; + // This loop goes over all 4 possible pyramid levels + for (var y = position.y() - 1; y >= position.y() - 4; y--) { + for (var x = position.x() - iter; x <= position.x() + iter; x++) { + for (var z = position.z() - iter; z <= position.z() + iter; z++) { + if (!position.dimension().getBlockState(x, y, z).getBlockType().hasBlockTag(BlockCustomTags.BEACON_BASE)) { + return lvl; + } + } + } + iter++; + lvl++; + } + return lvl; + } + + /** + * Determine whether the beacon is currently obstructed. + * + * @return {@code true} if the beacon is obstructed, {@code false} otherwise. + */ + protected boolean isObstructed() { + return position.dimension().getLightService().getSkyLight(position.x(), position.y() + 1, position.z()) != 15; + } +} diff --git a/server/src/main/java/org/allaymc/server/blockentity/impl/BlockEntityBeaconImpl.java b/server/src/main/java/org/allaymc/server/blockentity/impl/BlockEntityBeaconImpl.java new file mode 100644 index 0000000000..d4590a0d48 --- /dev/null +++ b/server/src/main/java/org/allaymc/server/blockentity/impl/BlockEntityBeaconImpl.java @@ -0,0 +1,25 @@ +package org.allaymc.server.blockentity.impl; + +import lombok.experimental.Delegate; +import org.allaymc.api.blockentity.component.BlockEntityBeaconBaseComponent; +import org.allaymc.api.blockentity.initinfo.BlockEntityInitInfo; +import org.allaymc.api.blockentity.interfaces.BlockEntityBeacon; +import org.allaymc.api.component.interfaces.Component; +import org.allaymc.server.component.interfaces.ComponentProvider; + +import java.util.List; + +/** + * @author daoge_cmd + */ +public class BlockEntityBeaconImpl extends BlockEntityImpl implements BlockEntityBeacon { + public BlockEntityBeaconImpl(BlockEntityInitInfo initInfo, List> componentProviders) { + super(initInfo, componentProviders); + } + + @Delegate + @Override + public BlockEntityBeaconBaseComponent getBaseComponent() { + return (BlockEntityBeaconBaseComponent) super.getBaseComponent(); + } +} diff --git a/server/src/main/java/org/allaymc/server/blockentity/type/BlockEntityTypeInitializer.java b/server/src/main/java/org/allaymc/server/blockentity/type/BlockEntityTypeInitializer.java index 172daeb85c..e5c2320199 100644 --- a/server/src/main/java/org/allaymc/server/blockentity/type/BlockEntityTypeInitializer.java +++ b/server/src/main/java/org/allaymc/server/blockentity/type/BlockEntityTypeInitializer.java @@ -94,4 +94,12 @@ public static void initEnchantTable() { .addComponent(BlockEntityEnchantTableBaseComponentImpl::new, BlockEntityEnchantTableBaseComponentImpl.class) .build(); } + + public static void initBeacon() { + BlockEntityTypes.BEACON = AllayBlockEntityType + .builder(BlockEntityBeaconImpl.class) + .name(BlockEntityId.BEACON) + .addComponent(BlockEntityBeaconBaseComponentImpl::new, BlockEntityBeaconBaseComponentImpl.class) + .build(); + } } diff --git a/server/src/main/java/org/allaymc/server/container/SimpleContainerActionProcessorHolder.java b/server/src/main/java/org/allaymc/server/container/ContainerActionProcessorHolder.java similarity index 51% rename from server/src/main/java/org/allaymc/server/container/SimpleContainerActionProcessorHolder.java rename to server/src/main/java/org/allaymc/server/container/ContainerActionProcessorHolder.java index 494d12102c..078a3cae95 100644 --- a/server/src/main/java/org/allaymc/server/container/SimpleContainerActionProcessorHolder.java +++ b/server/src/main/java/org/allaymc/server/container/ContainerActionProcessorHolder.java @@ -1,7 +1,6 @@ package org.allaymc.server.container; -import org.allaymc.server.container.processor.ContainerActionProcessor; -import org.allaymc.server.container.processor.ContainerActionProcessorHolder; +import org.allaymc.server.container.processor.*; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestActionType; import org.jetbrains.annotations.UnmodifiableView; @@ -12,21 +11,33 @@ /** * @author daoge_cmd */ -public class SimpleContainerActionProcessorHolder implements ContainerActionProcessorHolder { +public final class ContainerActionProcessorHolder { private static final EnumMap> PROCESSORS = new EnumMap<>(ItemStackRequestActionType.class); - @Override + public ContainerActionProcessorHolder() { + registerProcessor(new CraftCreativeActionProcessor()); + registerProcessor(new PlaceActionProcessor()); + registerProcessor(new TakeActionProcessor()); + registerProcessor(new DestroyActionProcessor()); + registerProcessor(new DropActionProcessor()); + registerProcessor(new SwapActionProcessor()); + registerProcessor(new CraftRecipeActionProcessor()); + registerProcessor(new ConsumeActionProcessor()); + registerProcessor(new CreateActionProcessor()); + registerProcessor(new CraftResultDeprecatedActionProcessor()); + registerProcessor(new MineBlockActionProcessor()); + registerProcessor(new BeaconPaymentActionProcessor()); + } + public > R getProcessor(ItemStackRequestActionType type) { return (R) PROCESSORS.get(type); } - @Override public void registerProcessor(ContainerActionProcessor processor) { PROCESSORS.put(processor.getType(), processor); } @UnmodifiableView - @Override public Map> getProcessors() { return Collections.unmodifiableMap(PROCESSORS); } diff --git a/server/src/main/java/org/allaymc/server/container/processor/BeaconPaymentActionProcessor.java b/server/src/main/java/org/allaymc/server/container/processor/BeaconPaymentActionProcessor.java new file mode 100644 index 0000000000..aeebe00562 --- /dev/null +++ b/server/src/main/java/org/allaymc/server/container/processor/BeaconPaymentActionProcessor.java @@ -0,0 +1,70 @@ +package org.allaymc.server.container.processor; + +import lombok.extern.slf4j.Slf4j; +import org.allaymc.api.blockentity.interfaces.BlockEntityBeacon; +import org.allaymc.api.container.Container; +import org.allaymc.api.container.FullContainerType; +import org.allaymc.api.container.impl.BeaconContainer; +import org.allaymc.api.entity.interfaces.EntityPlayer; +import org.allaymc.api.item.tag.ItemCustomTags; +import org.allaymc.api.registry.Registries; +import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.BeaconPaymentAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DestroyAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction; +import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestActionType; + +import java.util.Map; + +/** + * @author daoge_cmd + */ +@Slf4j +public class BeaconPaymentActionProcessor implements ContainerActionProcessor { + @Override + public ActionResponse handle(BeaconPaymentAction action, EntityPlayer player, int currentActionIndex, ItemStackRequestAction[] actions, Map dataPool) { + var container = player.getContainer(FullContainerType.BEACON); + var itemType = container.getBeaconPayment().getItemType(); + if (!itemType.hasItemTag(ItemCustomTags.BEACON_PAYMENT)) { + log.warn("Invalid item type for beacon payment: {}", itemType.getIdentifier()); + return error(); + } + + if (actions.length == currentActionIndex + 1 || + !(actions[currentActionIndex + 1] instanceof DestroyAction destroyAction)) { + log.warn("Destroy action not found after beacon payment action"); + return error(); + } + + if (!validateDestoryAction(container, destroyAction)) { + log.warn("Invalid destroy action"); + return error(); + } + + var blockPos = container.getBlockPos(); + if (!(blockPos.dimension().getBlockEntity(blockPos) instanceof BlockEntityBeacon blockEntityBeacon)) { + log.warn("Beacon block entity not found at {}", blockPos); + return error(); + } + + if (action.getPrimaryEffect() != 0) { + blockEntityBeacon.setPrimaryEffect(Registries.EFFECTS.getByK1(action.getPrimaryEffect())); + } + if (action.getSecondaryEffect() != 0) { + blockEntityBeacon.setSecondaryEffect(Registries.EFFECTS.getByK1(action.getSecondaryEffect())); + } + return null; + } + + protected boolean validateDestoryAction(Container container, DestroyAction destroyAction) { + var source = destroyAction.getSource(); + return destroyAction.getCount() == 1 && + source.getContainerName().getContainer() == ContainerSlotType.BEACON_PAYMENT && + container.fromNetworkSlotIndex(source.getSlot()) == BeaconContainer.BEACON_PAYMENT_SLOT; + } + + @Override + public ItemStackRequestActionType getType() { + return ItemStackRequestActionType.BEACON_PAYMENT; + } +} diff --git a/server/src/main/java/org/allaymc/server/container/processor/ContainerActionProcessorHolder.java b/server/src/main/java/org/allaymc/server/container/processor/ContainerActionProcessorHolder.java deleted file mode 100644 index 999fed0fcb..0000000000 --- a/server/src/main/java/org/allaymc/server/container/processor/ContainerActionProcessorHolder.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.allaymc.server.container.processor; - -import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestActionType; -import org.jetbrains.annotations.UnmodifiableView; - -import java.util.Map; - -/** - * @author daoge_cmd - */ -public interface ContainerActionProcessorHolder { - static void registerDefaultContainerActionProcessors(ContainerActionProcessorHolder holder) { - holder.registerProcessor(new CraftCreativeActionProcessor()); - holder.registerProcessor(new PlaceActionProcessor()); - holder.registerProcessor(new TakeActionProcessor()); - holder.registerProcessor(new DestroyActionProcessor()); - holder.registerProcessor(new DropActionProcessor()); - holder.registerProcessor(new SwapActionProcessor()); - holder.registerProcessor(new CraftRecipeActionProcessor()); - holder.registerProcessor(new ConsumeActionProcessor()); - holder.registerProcessor(new CreateActionProcessor()); - holder.registerProcessor(new CraftResultDeprecatedActionProcessor()); - holder.registerProcessor(new MineBlockActionProcessor()); - } - - > R getProcessor(ItemStackRequestActionType type); - - void registerProcessor(ContainerActionProcessor processor); - - @UnmodifiableView - Map> getProcessors(); -} diff --git a/server/src/main/java/org/allaymc/server/container/processor/DestroyActionProcessor.java b/server/src/main/java/org/allaymc/server/container/processor/DestroyActionProcessor.java index 37b8d00c00..eac5cd8ea9 100644 --- a/server/src/main/java/org/allaymc/server/container/processor/DestroyActionProcessor.java +++ b/server/src/main/java/org/allaymc/server/container/processor/DestroyActionProcessor.java @@ -2,7 +2,6 @@ import lombok.extern.slf4j.Slf4j; import org.allaymc.api.entity.interfaces.EntityPlayer; -import org.cloudburstmc.protocol.bedrock.data.GameType; import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DestroyAction; import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction; @@ -23,11 +22,6 @@ public class DestroyActionProcessor implements ContainerActionProcessor { @Override public ActionResponse handle(DestroyAction action, EntityPlayer player, int currentActionIndex, ItemStackRequestAction[] actions, Map dataPool) { - if (player.getGameType() != GameType.CREATIVE) { - log.warn("only creative mode can destroy item"); - return error(); - } - var container = player.getContainerBySlotType(action.getSource().getContainerName().getContainer()); var count = action.getCount(); var slot = container.fromNetworkSlotIndex(action.getSource().getSlot()); diff --git a/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerContainerHolderComponentImpl.java b/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerContainerHolderComponentImpl.java index 07aa4978ca..b0e125d5f4 100644 --- a/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerContainerHolderComponentImpl.java +++ b/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerContainerHolderComponentImpl.java @@ -26,7 +26,8 @@ public EntityPlayerContainerHolderComponentImpl() { super( new PlayerCreatedOutputContainer(), new CraftingGridContainer(), - new CraftingTableContainer() + new CraftingTableContainer(), + new BeaconContainer() ); var enchantTableContainer = new EnchantTableContainer(); enchantTableContainer.addOnSlotChangeListener(EnchantTableContainer.INPUT_SLOT, item -> { diff --git a/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/ItemStackRequestPacketProcessor.java b/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/ItemStackRequestPacketProcessor.java index ee420cad87..5a98b3aa12 100644 --- a/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/ItemStackRequestPacketProcessor.java +++ b/server/src/main/java/org/allaymc/server/network/processor/impl/ingame/ItemStackRequestPacketProcessor.java @@ -4,10 +4,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import lombok.extern.slf4j.Slf4j; import org.allaymc.api.entity.interfaces.EntityPlayer; -import org.allaymc.server.container.SimpleContainerActionProcessorHolder; +import org.allaymc.server.container.ContainerActionProcessorHolder; import org.allaymc.server.container.processor.ActionResponse; import org.allaymc.server.container.processor.ContainerActionProcessor; -import org.allaymc.server.container.processor.ContainerActionProcessorHolder; import org.allaymc.server.network.processor.PacketProcessor; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType; import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName; @@ -28,11 +27,7 @@ */ @Slf4j public class ItemStackRequestPacketProcessor extends PacketProcessor { - protected final ContainerActionProcessorHolder processorHolder = new SimpleContainerActionProcessorHolder(); - - public ItemStackRequestPacketProcessor() { - ContainerActionProcessorHolder.registerDefaultContainerActionProcessors(this.processorHolder); - } + protected final ContainerActionProcessorHolder processorHolder = new ContainerActionProcessorHolder(); @Override public void handleSync(EntityPlayer player, ItemStackRequestPacket packet, long receiveTime) {