9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 01:49:30 +00:00

方块实体3

This commit is contained in:
XiaoMoMi
2025-09-05 07:06:30 +08:00
parent c33d18faf1
commit 8931433bb1
24 changed files with 612 additions and 33 deletions

View File

@@ -28,6 +28,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key PRESSURE_PLATE_BLOCK = Key.from("craftengine:pressure_plate_block");
public static final Key DOUBLE_HIGH_BLOCK = Key.from("craftengine:double_high_block");
public static final Key CHANGE_OVER_TIME_BLOCK = Key.from("craftengine:change_over_time_block");
public static final Key SIMPLE_STORAGE_BLOCK = Key.from("craftengine:simple_storage_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -54,5 +55,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(PRESSURE_PLATE_BLOCK, PressurePlateBlockBehavior.FACTORY);
register(DOUBLE_HIGH_BLOCK, DoubleHighBlockBehavior.FACTORY);
register(CHANGE_OVER_TIME_BLOCK, ChangeOverTimeBlockBehavior.FACTORY);
register(SIMPLE_STORAGE_BLOCK, SimpleStorageBlockBehavior.FACTORY);
}
}

View File

@@ -0,0 +1,186 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.gui.BukkitInventory;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior {
public static final Factory FACTORY = new Factory();
private final String containerTitle;
private final int rows;
private final SoundData openSound;
private final SoundData closeSound;
private final boolean hasAnalogOutputSignal;
@Nullable
private final Property<Boolean> openProperty;
public SimpleStorageBlockBehavior(CustomBlock customBlock,
String containerTitle,
int rows,
SoundData openSound,
SoundData closeSound,
boolean hasAnalogOutputSignal,
Property<Boolean> openProperty) {
super(customBlock);
this.containerTitle = containerTitle;
this.rows = rows;
this.openSound = openSound;
this.closeSound = closeSound;
this.hasAnalogOutputSignal = hasAnalogOutputSignal;
this.openProperty = openProperty;
}
@Override
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
CEWorld world = context.getLevel().storageWorld();
net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer();
BlockEntity blockEntity = world.getBlockEntityAtIfLoaded(context.getClickedPos());
if (player != null && blockEntity instanceof SimpleStorageBlockEntity entity) {
Player bukkitPlayer = (Player) player.platformPlayer();
Optional.ofNullable(entity.inventory()).ifPresent(inventory -> {
entity.onPlayerOpen(player);
bukkitPlayer.openInventory(inventory);
new BukkitInventory(inventory).open(player, AdventureHelper.miniMessage().deserialize(this.containerTitle, PlayerOptionalContext.of(player).tagResolvers()));
});
}
return InteractionResult.SUCCESS_AND_CANCEL;
}
// 1.21.5+
@Override
public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object level = args[1];
Object pos = args[2];
Object blockState = args[0];
FastNMS.INSTANCE.method$Level$updateNeighbourForOutputSignal(level, pos, BlockStateUtils.getBlockOwner(blockState));
}
@Override
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object world = args[1];
Object blockPos = args[2];
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world);
CEWorld ceWorld = BukkitWorldManager.instance().getWorld(bukkitWorld.getUID());
BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(pos);
if (blockEntity instanceof SimpleStorageBlockEntity entity) {
entity.checkOpeners(world, blockPos, args[0]);
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
return (BlockEntityType<T>) BukkitBlockEntityTypes.SIMPLE_STORAGE;
}
@Override
public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) {
return new SimpleStorageBlockEntity(pos, state);
}
@NotNull
public String containerTitle() {
return this.containerTitle;
}
@Nullable
public SoundData closeSound() {
return this.closeSound;
}
@Nullable
public SoundData openSound() {
return this.openSound;
}
public int rows() {
return this.rows;
}
public @Nullable Property<Boolean> openProperty() {
return openProperty;
}
@Override
public int getAnalogOutputSignal(Object thisBlock, Object[] args) {
if (!this.hasAnalogOutputSignal) return 0;
Object world = args[1];
Object blockPos = args[2];
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(world);
CEWorld ceWorld = BukkitWorldManager.instance().getWorld(bukkitWorld.getUID());
BlockEntity blockEntity = ceWorld.getBlockEntityAtIfLoaded(pos);
if (blockEntity instanceof SimpleStorageBlockEntity entity) {
Inventory inventory = entity.inventory();
if (inventory != null) {
float signal = 0.0F;
for (int i = 0; i < inventory.getSize(); i++) {
ItemStack item = inventory.getItem(i);
if (item != null) {
signal += (float) item.getAmount() / (float) (Math.min(inventory.getMaxStackSize(), item.getMaxStackSize()));
}
}
signal /= (float) inventory.getSize();
return MCUtils.lerpDiscrete(signal, 0, 15);
}
}
return 0;
}
@Override
public boolean hasAnalogOutputSignal(Object thisBlock, Object[] args) {
return this.hasAnalogOutputSignal;
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
String title = arguments.getOrDefault("title", "").toString();
int rows = MCUtils.clamp(ResourceConfigUtils.getAsInt(arguments.getOrDefault("rows", 1), "rows"), 1, 6);
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
boolean hasAnalogOutputSignal = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("has-signal", true), "has-signal");
SoundData openSound = null;
SoundData closeSound = null;
if (sounds != null) {
openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_0_5, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
}
Property<Boolean> property = (Property<Boolean>) block.getProperty("open");
return new SimpleStorageBlockBehavior(block, title, rows, openSound, closeSound, hasAnalogOutputSignal, property);
}
}
}

View File

@@ -0,0 +1,28 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
public class BlockEntityHolder implements InventoryHolder {
private final BlockEntity blockEntity;
private Inventory inventory;
public BlockEntityHolder(BlockEntity entity) {
this.blockEntity = entity;
}
public BlockEntity blockEntity() {
return blockEntity;
}
public void setInventory(Inventory inventory) {
this.inventory = inventory;
}
@Override
public @NotNull Inventory getInventory() {
return this.inventory;
}
}

View File

@@ -0,0 +1,9 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.BlockEntityTypeKeys;
import net.momirealms.craftengine.core.block.entity.BlockEntityTypes;
public class BukkitBlockEntityTypes extends BlockEntityTypes {
public static final BlockEntityType<SimpleStorageBlockEntity> SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new);
}

View File

@@ -0,0 +1,205 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.block.behavior.SimpleStorageBlockBehavior;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.ListTag;
import org.bukkit.Bukkit;
import org.bukkit.GameEvent;
import org.bukkit.GameMode;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
public class SimpleStorageBlockEntity extends BlockEntity {
private final SimpleStorageBlockBehavior behavior;
private final Inventory inventory;
private double maxInteractionDistance;
private boolean openState = false;
public SimpleStorageBlockEntity(BlockPos pos, ImmutableBlockState blockState) {
super(BukkitBlockEntityTypes.SIMPLE_STORAGE, pos, blockState);
this.behavior = super.blockState.behavior().getAs(SimpleStorageBlockBehavior.class).orElseThrow();
BlockEntityHolder holder = new BlockEntityHolder(this);
this.inventory = Bukkit.createInventory(holder, this.behavior.rows() * 9);
}
@Override
protected void saveCustomData(CompoundTag tag) {
// 保存前先把所有打开此容器的玩家界面关闭
this.inventory.close();
ListTag itemsTag = new ListTag();
@Nullable ItemStack[] storageContents = this.inventory.getStorageContents();
for (int i = 0; i < storageContents.length; i++) {
if (storageContents[i] != null) {
int slot = i;
CoreReflections.instance$ItemStack$CODEC.encodeStart(MRegistryOps.SPARROW_NBT, FastNMS.INSTANCE.field$CraftItemStack$handle(storageContents[i]))
.ifSuccess(success -> {
CompoundTag itemTag = (CompoundTag) success;
itemTag.putInt("slot", slot);
itemsTag.add(itemTag);
})
.ifError(error -> CraftEngine.instance().logger().severe("Error while saving storage item: " + error));
}
}
tag.put("items", itemsTag);
}
@Override
public void loadCustomData(CompoundTag tag) {
ListTag itemsTag = Optional.ofNullable(tag.getList("items")).orElseGet(ListTag::new);
for (int i = 0; i < itemsTag.size(); i++) {
CompoundTag itemTag = itemsTag.getCompound(i);
int slot = itemTag.getInt("slot");
if (slot < 0 || slot >= this.behavior.rows() * 9) {
continue;
}
CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemTag)
.resultOrPartial((s) -> CraftEngine.instance().logger().severe("Tried to load invalid item: '" + itemTag + "'. " + s))
.ifPresent(nmsStack -> this.inventory.setItem(slot, FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack)));
}
}
public Inventory inventory() {
if (!isValid()) return null;
return this.inventory;
}
public void onPlayerOpen(Player player) {
if (!isValidContainer()) return;
if (!player.isSpectatorMode()) {
// 有非观察者的人,那么就不触发开启音效和事件
if (!hasNoViewer(this.inventory.getViewers())) return;
this.maxInteractionDistance = Math.max(player.getCachedInteractionRange(), this.maxInteractionDistance);
this.setOpen(player);
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(super.world.world().serverWorld(), LocationUtils.toBlockPos(this.pos), BlockStateUtils.getBlockOwner(this.blockState.customBlockState().literalObject()), 5);
}
}
public void onPlayerClose(Player player) {
if (!isValidContainer()) return;
if (!player.isSpectatorMode()) {
// 有非观察者的人,那么就不触发关闭音效和事件
for (HumanEntity viewer : this.inventory.getViewers()) {
if (viewer.getGameMode() == GameMode.SPECTATOR || viewer == player.platformPlayer()) {
continue;
}
return;
}
this.maxInteractionDistance = 0;
this.setClose(player);
}
}
private void setOpen(@Nullable Player player) {
this.updateOpenBlockState(true);
org.bukkit.World bukkitWorld = (org.bukkit.World) super.world.world().platformWorld();
if (player != null) {
bukkitWorld.sendGameEvent((org.bukkit.entity.Player) player.platformPlayer(), GameEvent.CONTAINER_OPEN, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
} else {
bukkitWorld.sendGameEvent(null, GameEvent.CONTAINER_OPEN, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
}
this.openState = true;
SoundData soundData = this.behavior.openSound();
if (soundData != null) {
super.world.world().playBlockSound(Vec3d.atCenterOf(this.pos), soundData);
}
}
private void setClose(@Nullable Player player) {
this.updateOpenBlockState(false);
org.bukkit.World bukkitWorld = (org.bukkit.World) super.world.world().platformWorld();
if (player != null) {
bukkitWorld.sendGameEvent((org.bukkit.entity.Player) player.platformPlayer(), GameEvent.CONTAINER_CLOSE, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
} else {
bukkitWorld.sendGameEvent(null, GameEvent.CONTAINER_CLOSE, new Vector(this.pos.x(), this.pos.y(), this.pos.z()));
}
this.openState = false;
SoundData soundData = this.behavior.closeSound();
if (soundData != null) {
super.world.world().playBlockSound(Vec3d.atCenterOf(this.pos), soundData);
}
}
private boolean hasNoViewer(List<HumanEntity> viewers) {
for (HumanEntity viewer : viewers) {
if (viewer.getGameMode() != GameMode.SPECTATOR) {
return false;
}
}
return true;
}
private boolean isValidContainer() {
return this.isValid() && this.inventory != null && this.behavior != null;
}
public void updateOpenBlockState(boolean open) {
ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos);
if (state == null || state.behavior() != this.behavior) return;
Property<Boolean> property = this.behavior.openProperty();
if (property == null) return;
super.world.world().setBlockAt(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags());
}
public void checkOpeners(Object level, Object pos, Object blockState) {
if (!this.isValidContainer()) return;
double maxInteractionDistance = 0d;
List<HumanEntity> viewers = this.inventory.getViewers();
int validViewers = 0;
for (HumanEntity viewer : viewers) {
if (viewer instanceof org.bukkit.entity.Player player) {
maxInteractionDistance = Math.max(BukkitAdaptors.adapt(player).getCachedInteractionRange(), maxInteractionDistance);
if (player.getGameMode() != GameMode.SPECTATOR) {
validViewers++;
}
}
}
boolean shouldOpen = validViewers != 0;
if (shouldOpen && !this.openState) {
this.setOpen(null);
} else if (!shouldOpen && this.openState) {
this.setClose(null);
}
this.maxInteractionDistance = maxInteractionDistance;
if (!viewers.isEmpty()) {
FastNMS.INSTANCE.method$ScheduledTickAccess$scheduleBlockTick(level, pos, BlockStateUtils.getBlockOwner(blockState), 5);
}
}
@Override
public void preRemove() {
this.inventory.close();
Vec3d pos = Vec3d.atCenterOf(this.pos);
for (ItemStack stack : this.inventory.getContents()) {
if (stack != null) {
super.world.world().dropItemNaturally(pos, BukkitItemManager.instance().wrap(stack));
}
}
this.inventory.clear();
}
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.bukkit.plugin.gui;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.block.entity.BlockEntityHolder;
import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
@@ -18,16 +20,19 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.MenuType;
public class BukkitGuiManager implements GuiManager, Listener {
private static final boolean useNewOpenInventory = ReflectionUtils.getDeclaredMethod(InventoryView.class, void.class, new String[]{"open"}) != null;
private static BukkitGuiManager instance;
private final BukkitCraftEngine plugin;
public BukkitGuiManager(BukkitCraftEngine plugin) {
this.plugin = plugin;
instance = this;
}
@Override
@@ -83,7 +88,7 @@ public class BukkitGuiManager implements GuiManager, Listener {
@Override
public Inventory createInventory(Gui gui, int size) {
CraftEngineInventoryHolder holder = new CraftEngineInventoryHolder(gui);
CraftEngineGUIHolder holder = new CraftEngineGUIHolder(gui);
org.bukkit.inventory.Inventory inventory = Bukkit.createInventory(holder, size);
holder.holder().bindValue(inventory);
return new BukkitInventory(inventory);
@@ -95,10 +100,10 @@ public class BukkitGuiManager implements GuiManager, Listener {
if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory))) {
return;
}
if (!(inventory.getHolder() instanceof CraftEngineInventoryHolder craftEngineInventoryHolder)) {
if (!(inventory.getHolder() instanceof CraftEngineGUIHolder craftEngineGUIHolder)) {
return;
}
AbstractGui gui = (AbstractGui) craftEngineInventoryHolder.gui();
AbstractGui gui = (AbstractGui) craftEngineGUIHolder.gui();
Player player = (Player) event.getWhoClicked();
if (event.getClickedInventory() == player.getInventory()) {
gui.handleInventoryClick(new BukkitClick(event, gui, new BukkitInventory(player.getInventory())));
@@ -113,7 +118,7 @@ public class BukkitGuiManager implements GuiManager, Listener {
if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory))) {
return;
}
if (!(inventory.getHolder() instanceof CraftEngineInventoryHolder)) {
if (!(inventory.getHolder() instanceof CraftEngineGUIHolder)) {
return;
}
for (int raw : event.getRawSlots()) {
@@ -123,4 +128,23 @@ public class BukkitGuiManager implements GuiManager, Listener {
}
}
}
// 处理自定义容器的关闭音效
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void onInventoryClose(InventoryCloseEvent event) {
org.bukkit.inventory.Inventory inventory = event.getInventory();
if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory))) {
return;
}
if (!(inventory.getHolder() instanceof BlockEntityHolder holder)) {
return;
}
if (event.getPlayer() instanceof Player player && holder.blockEntity() instanceof SimpleStorageBlockEntity simpleStorageBlockEntity) {
simpleStorageBlockEntity.onPlayerClose(this.plugin.adapt(player));
}
}
public static BukkitGuiManager instance() {
return instance;
}
}

View File

@@ -6,11 +6,11 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
public class CraftEngineInventoryHolder implements InventoryHolder {
public class CraftEngineGUIHolder implements InventoryHolder {
private final ObjectHolder<Inventory> inventory;
private final Gui gui;
public CraftEngineInventoryHolder(Gui gui) {
public CraftEngineGUIHolder(Gui gui) {
this.inventory = new ObjectHolder<>();
this.gui = gui;
}

View File

@@ -15,7 +15,6 @@ import net.bytebuddy.implementation.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;
import net.momirealms.craftengine.bukkit.block.BukkitBlockShape;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.injector.BlockGenerator.*;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.NoteBlockChainUpdateUtils;

View File

@@ -3671,11 +3671,11 @@ public final class CoreReflections {
// 1.20.5+
public static final Field field$ItemStack$CODEC = ReflectionUtils.getDeclaredField(clazz$ItemStack, "CODEC", "b");
public static final Codec<?> instance$ItemStack$CODEC;
public static final Codec<Object> instance$ItemStack$CODEC;
static {
try {
instance$ItemStack$CODEC = VersionHelper.isOrAbove1_20_5() ? (Codec<?>) field$ItemStack$CODEC.get(null) : null;
instance$ItemStack$CODEC = VersionHelper.isOrAbove1_20_5() ? (Codec<Object>) field$ItemStack$CODEC.get(null) : null;
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init ItemStack$CODEC", e);
}

View File

@@ -7,10 +7,12 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.entity.BlockEntityHolder;
import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineInventoryHolder;
import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineGUIHolder;
import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
@@ -21,6 +23,7 @@ import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.entity.player.GameMode;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.Player;
@@ -36,13 +39,16 @@ import net.momirealms.craftengine.core.util.IntIdentityList;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.chunk.ChunkStatus;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.block.Block;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
@@ -467,7 +473,7 @@ public class BukkitServerPlayer extends Player {
} else {
this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick();
}
if (this.gameTicks % 30 == 0) {
if (this.gameTicks % 20 == 0) {
this.updateGUI();
}
if (this.isDestroyingBlock) {
@@ -491,11 +497,27 @@ public class BukkitServerPlayer extends Player {
if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(top))) {
return;
}
if (top.getHolder() instanceof CraftEngineInventoryHolder holder) {
if (top.getHolder() instanceof CraftEngineGUIHolder holder) {
holder.gui().onTimer();
} else if (top.getHolder() instanceof BlockEntityHolder holder) {
BlockEntity blockEntity = holder.blockEntity();
BlockPos blockPos = blockEntity.pos();
if (!canInteractWithBlock(blockPos, 4d)) {
platformPlayer().closeInventory();
}
}
}
public boolean canInteractWithBlock(BlockPos pos, double distance) {
double d = this.getCachedInteractionRange() + distance;
return (new AABB(pos)).distanceToSqr(this.getEyePosition()) < d * d;
}
public final Vec3d getEyePosition() {
Location eyeLocation = this.platformPlayer().getEyeLocation();
return new Vec3d(eyeLocation.getX(), eyeLocation.getY(), eyeLocation.getZ());
}
@Override
public float getDestroyProgress(Object blockState, BlockPos pos) {
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);

View File

@@ -4,7 +4,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public final class EnchantmentUtils {

View File

@@ -123,4 +123,9 @@ public class BukkitWorld implements World {
public void levelEvent(int id, BlockPos pos, int data) {
FastNMS.INSTANCE.method$LevelAccessor$levelEvent(serverWorld(), id, LocationUtils.toBlockPos(pos), data);
}
@Override
public CEWorld storageWorld() {
return BukkitWorldManager.instance().getWorld(uuid());
}
}

View File

@@ -464,11 +464,21 @@ items#misc:
hit: minecraft:block.stone.hit
place: minecraft:block.stone.place
step: minecraft:block.stone.step
behavior:
type: simple_storage_block
title: "<i18n:item.safe_block>"
rows: 1
sounds:
open: minecraft:block.iron_trapdoor.open
close: minecraft:block.iron_trapdoor.close
states:
properties:
facing:
type: 4-direction
default: north
open:
type: boolean
default: false
appearances:
east:
state: note_block:22
@@ -481,33 +491,70 @@ items#misc:
front: minecraft:block/custom/safe_block_front
side: minecraft:block/custom/safe_block_side
top: minecraft:block/custom/safe_block_top
north:
east_open:
state: note_block:23
model:
path: minecraft:block/custom/safe_block
south:
path: minecraft:block/custom/safe_block_open
y: 90
generation:
parent: minecraft:block/orientable
textures:
front: minecraft:block/custom/safe_block_front_open
side: minecraft:block/custom/safe_block_side
top: minecraft:block/custom/safe_block_top
north:
state: note_block:24
model:
path: minecraft:block/custom/safe_block
north_open:
state: note_block:25
model:
path: minecraft:block/custom/safe_block_open
south:
state: note_block:26
model:
path: minecraft:block/custom/safe_block
y: 180
south_open:
state: note_block:27
model:
path: minecraft:block/custom/safe_block_open
y: 180
west:
state: note_block:25
state: note_block:28
model:
path: minecraft:block/custom/safe_block
y: 270
west_open:
state: note_block:29
model:
path: minecraft:block/custom/safe_block_open
y: 270
variants:
facing=east:
facing=east,open=false:
appearance: east
id: 22
facing=north:
appearance: north
facing=east,open=true:
appearance: east_open
id: 23
facing=south:
appearance: south
facing=north,open=false:
appearance: north
id: 24
facing=west:
appearance: west
facing=north,open=true:
appearance: north_open
id: 25
facing=south,open=false:
appearance: south
id: 26
facing=south,open=true:
appearance: south_open
id: 27
facing=west,open=false:
appearance: west
id: 28
facing=west,open=true:
appearance: west_open
id: 29
recipes#misc:
default:chinese_lantern:
type: shaped

View File

@@ -6,7 +6,9 @@ import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
public interface EntityBlockBehavior {
<T extends BlockEntity> BlockEntityType<T> blockEntityType();

View File

@@ -0,0 +1,9 @@
package net.momirealms.craftengine.core.block.entity;
import net.momirealms.craftengine.core.util.Key;
public final class BlockEntityTypeKeys {
private BlockEntityTypeKeys() {}
public static final Key SIMPLE_STORAGE = Key.of("craftengine:simple_storage");
}

View File

@@ -1,5 +1,17 @@
package net.momirealms.craftengine.core.block.entity;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Registries;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
public class BlockEntityTypes {
public static <T extends BlockEntity> BlockEntityType<T> register(Key id, BlockEntity.Factory<T> factory) {
BlockEntityType<T> type = new BlockEntityType<>(id, factory);
((WritableRegistry<BlockEntityType<?>>) BuiltInRegistries.BLOCK_ENTITY_TYPE)
.register(ResourceKey.create(Registries.BLOCK_ENTITY_TYPE.location(), id), type);
return type;
}
}

View File

@@ -426,6 +426,7 @@ public abstract class AbstractPackManager implements PackManager {
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_bottom.png");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_side.png");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front.png");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/block/custom/safe_block_front_open.png");
// items
plugin.saveResource("resources/default/configuration/items.yml");
plugin.saveResource("resources/default/resourcepack/assets/minecraft/textures/item/custom/topaz_rod.png");

View File

@@ -1,13 +1,9 @@
package net.momirealms.craftengine.core.plugin.dependency;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.dependency.relocation.Relocation;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
public class Dependencies {

View File

@@ -31,6 +31,11 @@ public class MCUtils {
return value < (double) truncated ? truncated - 1 : truncated;
}
public static int lerpDiscrete(float delta, int start, int end) {
int i = end - start;
return start + fastFloor(delta * (float) (i - 1)) + (delta > 0.0F ? 1 : 0);
}
public static int murmurHash3Mixer(int value) {
value ^= value >>> 16;
value *= -2048144789;

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.core.world;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.sound.SoundData;
@@ -15,6 +16,8 @@ import java.util.UUID;
public interface World {
CEWorld storageWorld();
Object platformWorld();
Object serverWorld();
@@ -29,6 +32,10 @@ public interface World {
void setBlockAt(int x, int y, int z, BlockStateWrapper blockState, int flags);
default void setBlockAt(int x, int y, int z, ImmutableBlockState blockState, int flags) {
this.setBlockAt(x, y, z, blockState.customBlockState(), flags);
}
String name();
Path directory();

View File

@@ -65,11 +65,18 @@ public class CEChunk {
if (removedBlockEntity != null) {
removedBlockEntity.setValid(false);
}
this.removeBlockEntityTicker(blockPos);
}
public void clearAllBlockEntities() {
public void activateAllBlockEntities() {
for (BlockEntity blockEntity : this.blockEntities.values()) {
blockEntity.setValid(true);
replaceOrCreateTickingBlockEntity(blockEntity);
}
}
public void deactivateAllBlockEntities() {
this.blockEntities.values().forEach(e -> e.setValid(false));
this.blockEntities.clear();
this.tickingBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE));
this.tickingBlockEntitiesByPos.clear();
}
@@ -247,13 +254,14 @@ public class CEChunk {
public void load() {
if (this.loaded) return;
this.world.addLoadedChunk(this);
this.activateAllBlockEntities();
this.loaded = true;
}
public void unload() {
if (!this.loaded) return;
this.world.removeLoadedChunk(this);
this.clearAllBlockEntities();
this.deactivateAllBlockEntities();
this.loaded = false;
}
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.world.collision;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.EntityHitResult;
import net.momirealms.craftengine.core.world.Vec3d;
import org.jetbrains.annotations.Nullable;
@@ -34,6 +36,17 @@ public class AABB {
this.maxZ = Math.max(pos1.z, pos2.z);
}
public AABB(BlockPos pos) {
this(pos.x(), pos.y(), pos.z(), pos.x() + 1, pos.y() + 1, pos.z() + 1);
}
public double distanceToSqr(Vec3d vec) {
double x = Math.max(Math.max(this.minX - vec.x, vec.x - this.maxX), 0.0F);
double y = Math.max(Math.max(this.minY - vec.y, vec.y - this.maxY), 0.0F);
double z = Math.max(Math.max(this.minZ - vec.z, vec.z - this.maxZ), 0.0F);
return x * x + y * y + z * z;
}
public static AABB fromInteraction(Vec3d pos, double width, double height) {
return new AABB(
pos.x - width / 2,

View File

@@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=0.0.62.12
project_version=0.0.62.13
config_version=45
lang_version=25
project_group=net.momirealms
@@ -50,7 +50,7 @@ byte_buddy_version=1.17.5
ahocorasick_version=0.6.3
snake_yaml_version=2.4
anti_grief_version=0.20
nms_helper_version=1.0.74
nms_helper_version=1.0.75
evalex_version=3.5.0
reactive_streams_version=1.0.4
amazon_awssdk_version=2.33.1