9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-29 03:49:15 +00:00
This commit is contained in:
XiaoMoMi
2025-03-21 03:08:39 +08:00
parent 14ef28d015
commit 75dd95b4c0
15 changed files with 243 additions and 20 deletions

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.block;
import io.papermc.paper.event.block.BlockBreakBlockEvent;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockBreakEvent;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
@@ -11,6 +12,7 @@ import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.util.Key;
@@ -88,11 +90,12 @@ public class BlockEventListener implements Listener {
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) // I forget why it's LOW before
public void onPlayerBreak(BlockBreakEvent event) {
org.bukkit.block.Block block = event.getBlock();
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
int stateId = BlockStateUtils.blockStateToId(blockState);
Player player = event.getPlayer();
if (!BlockStateUtils.isVanillaBlock(stateId)) {
ImmutableBlockState state = manager.getImmutableBlockStateUnsafe(stateId);
if (!state.isEmpty()) {
@@ -119,13 +122,11 @@ public class BlockEventListener implements Listener {
// play sound
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
world.playBlockSound(vec3d, state.sounds().breakSound(), 1f, 0.8f);
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE) {
return;
}
BukkitServerPlayer serverPlayer = plugin.adapt(player);
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
Key itemId = Optional.ofNullable(itemInHand).map(Item::id).orElse(ItemKeys.AIR);
// do not drop if it's not the correct tool
@@ -136,26 +137,83 @@ public class BlockEventListener implements Listener {
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(LootParameters.WORLD, world);
builder.withParameter(LootParameters.LOCATION, vec3d);
builder.withParameter(LootParameters.PLAYER, plugin.adapt(player));
builder.withParameter(LootParameters.PLAYER, serverPlayer);
builder.withParameter(LootParameters.TOOL, itemInHand);
for (Item<Object> item : state.getDrops(builder, world)) {
world.dropItemNaturally(vec3d, item);
}
}
} else if (ConfigManager.enableSoundSystem()) {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
if (manager.isBlockSoundRemoved(ownerBlock)) {
try {
Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock);
Object breakSound = Reflections.field$SoundType$breakSound.get(soundType);
block.getWorld().playSound(block.getLocation(), Reflections.field$SoundEvent$location.get(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get sound type", e);
} else {
// override vanilla block loots
if (player.getGameMode() != GameMode.CREATIVE) {
this.plugin.vanillaLootManager().getBlockLoot(stateId).ifPresent(it -> {
if (it.override()) {
event.setDropItems(false);
event.setExpToDrop(0);
}
Location location = block.getLocation();
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(player.getWorld());
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(LootParameters.WORLD, world);
builder.withParameter(LootParameters.LOCATION, vec3d);
builder.withParameter(LootParameters.PLAYER, serverPlayer);
builder.withParameter(LootParameters.TOOL, serverPlayer.getItemInHand(InteractionHand.MAIN_HAND));
ContextHolder contextHolder = builder.build();
for (LootTable<?> lootTable : it.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(contextHolder, world)) {
world.dropItemNaturally(vec3d, item);
}
}
});
}
// sound system
if (ConfigManager.enableSoundSystem()) {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
if (this.manager.isBlockSoundRemoved(ownerBlock)) {
try {
Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock);
Object breakSound = Reflections.field$SoundType$breakSound.get(soundType);
block.getWorld().playSound(block.getLocation(), Reflections.field$SoundEvent$location.get(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f);
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to get sound type", e);
}
}
}
}
}
// override vanilla block loots
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onBlockBreakBlock(BlockBreakBlockEvent event) {
Block block = event.getBlock();
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
int stateId = BlockStateUtils.blockStateToId(blockState);
if (!BlockStateUtils.isVanillaBlock(stateId)) {
return;
}
this.plugin.vanillaLootManager().getBlockLoot(stateId).ifPresent(it -> {
if (it.override()) {
event.getDrops().clear();
event.setExpToDrop(0);
}
Location location = block.getLocation();
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(location.getWorld());
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(LootParameters.WORLD, world);
builder.withParameter(LootParameters.LOCATION, vec3d);
ContextHolder contextHolder = builder.build();
for (LootTable<?> lootTable : it.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(contextHolder, world)) {
world.dropItemNaturally(vec3d, item);
}
}
});
}
@EventHandler(ignoreCancelled = true)
public void onStep(GenericGameEvent event) {
if (event.getEvent() != GameEvent.STEP) return;

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.bukkit.item;
import com.saicone.rtag.data.ComponentType;
import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BoneMealBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior;

View File

@@ -193,7 +193,7 @@ public class ItemEventListener implements Listener {
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockHitResult hitResult = new BlockHitResult(vec3d, direction, pos, false);
try {
BlockData craftBlockData = BlockStateUtils.createBlockData(againCustomBlock.vanillaBlockState().handle());
BlockData craftBlockData = BlockStateUtils.fromBlockData(againCustomBlock.vanillaBlockState().handle());
if (InteractUtils.isInteractable(Key.of(clickedBlock.getType().getKey().asString()), bukkitPlayer, craftBlockData, hitResult, itemInHand)) {
if (!player.isSecondaryUseActive()) {
player.setResendSound();

View File

@@ -71,7 +71,7 @@ public class AxeItemBehavior extends ItemBehavior {
org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
// Call bukkit event
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, block, BlockStateUtils.createBlockData(newState.customBlockState().handle()));
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, block, BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.PASS;
}

View File

@@ -46,7 +46,7 @@ public class WaterBucketItemBehavior extends ItemBehavior {
// TODO Refactor all of this because it's playing a trick with the server
ImmutableBlockState nextState = state.with(waterlogged, true);
block.setBlockData(BlockStateUtils.createBlockData(nextState.vanillaBlockState().handle()), false);
block.setBlockData(BlockStateUtils.fromBlockData(nextState.vanillaBlockState().handle()), false);
// actually we should broadcast this change
context.getPlayer().sendPacket(BlockStateUtils.createBlockUpdatePacket(pos, state), true);
BukkitCraftEngine.instance().scheduler().sync().runDelayed(() ->

View File

@@ -0,0 +1,73 @@
package net.momirealms.craftengine.bukkit.loot;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.loot.VanillaLoot;
import net.momirealms.craftengine.core.loot.VanillaLootManager;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.PreConditions;
import org.bukkit.Bukkit;
import java.nio.file.Path;
import java.util.*;
public class BukkitVanillaLootManager implements VanillaLootManager {
private final CraftEngine plugin;
private final Map<Integer, VanillaLoot> blockLoots;
public BukkitVanillaLootManager(CraftEngine plugin) {
this.plugin = plugin;
this.blockLoots = new HashMap<>();
}
@Override
public void unload() {
this.blockLoots.clear();
}
@Override
public Optional<VanillaLoot> getBlockLoot(int vanillaBlockState) {
return Optional.ofNullable(this.blockLoots.get(vanillaBlockState));
}
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
String type = (String) section.get("type");
if (PreConditions.isNull(type, () -> this.plugin.logger().warn(path, "`type` option is required for vanilla-loot " + id))) {
return;
}
VanillaLoot.Type typeEnum = VanillaLoot.Type.valueOf(type.toUpperCase(Locale.ENGLISH));
boolean override = (boolean) section.getOrDefault("override", false);
List<String> targets = MiscUtils.getAsStringList(section.getOrDefault("target", List.of()));
LootTable<?> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), false));
switch (typeEnum) {
case BLOCK -> {
for (String target : targets) {
if (target.endsWith("]") && target.contains("[")) {
java.lang.Object blockState = BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(target));
if (blockState == Reflections.instance$Blocks$AIR$defaultState) {
this.plugin.logger().warn(path, "Failed to load " + id + ". Invalid target " + target);
return;
}
VanillaLoot vanillaLoot = this.blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
vanillaLoot.addLootTable(lootTable);
} else {
for (Object blockState : BlockStateUtils.getAllBlockStates(Key.of(target))) {
if (blockState == Reflections.instance$Blocks$AIR$defaultState) {
this.plugin.logger().warn(path, "Failed to load " + id + ". Invalid target " + target);
return;
}
VanillaLoot vanillaLoot = this.blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK));
if (override) vanillaLoot.override(true);
vanillaLoot.addLootTable(lootTable);
}
}
}
}
}
}
}

View File

@@ -8,6 +8,7 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors;
import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager;
import net.momirealms.craftengine.bukkit.loot.BukkitVanillaLootManager;
import net.momirealms.craftengine.bukkit.pack.BukkitPackManager;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandManager;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitSenderFactory;
@@ -136,6 +137,7 @@ public class BukkitCraftEngine extends CraftEngine {
super.guiManager = new BukkitGuiManager(this);
super.worldManager = new BukkitWorldManager(this);
super.soundManager = new BukkitSoundManager(this);
super.vanillaLootManager = new BukkitVanillaLootManager(this);
super.enable();
// tick task
if (VersionHelper.isFolia()) {
@@ -204,6 +206,8 @@ public class BukkitCraftEngine extends CraftEngine {
// register sound parser
this.packManager.registerConfigSectionParser(this.soundManager);
this.packManager.registerConfigSectionParser(this.soundManager.jukeboxSongManager());
// register vanilla loot parser
this.packManager.registerConfigSectionParser(this.vanillaLootManager);
}
@Override

View File

@@ -376,7 +376,7 @@ public class PacketConsumers {
Key itemId = state.settings().itemId();
// no item available
if (itemId == null) return;
BlockData data = BlockStateUtils.createBlockData(state.vanillaBlockState().handle());
BlockData data = BlockStateUtils.fromBlockData(state.vanillaBlockState().handle());
// compare item
if (data == null || !data.getMaterial().equals(item.getType())) return;
ItemStack itemStack = BukkitCraftEngine.instance().itemManager().buildCustomItemStack(itemId, player);

View File

@@ -12,6 +12,7 @@ import org.bukkit.event.block.BlockPhysicsEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.IdentityHashMap;
import java.util.List;
public class BlockStateUtils {
public static final IdentityHashMap<Object, Object> CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>();
@@ -26,6 +27,17 @@ public class BlockStateUtils {
hasInit = true;
}
@SuppressWarnings("unchecked")
public static List<Object> getAllBlockStates(Key block) {
try {
Object blockIns = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$BLOCK, Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, block.namespace(), block.value()));
Object definition = Reflections.field$Block$StateDefinition.get(blockIns);
return (List<Object>) Reflections.field$StateDefinition$states.get(definition);
} catch (Exception e) {
throw new RuntimeException("Failed to get all block states for " + block, e);
}
}
public static Object createBlockUpdatePacket(BlockPos pos, ImmutableBlockState state) {
try {
return Reflections.constructor$ClientboundBlockUpdatePacket.newInstance(LocationUtils.toBlockPos(pos), state.customBlockState().handle());