9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2026-01-04 15:41:38 +00:00

Merge pull request #241 from Xiao-MoMi/main

更新上游
This commit is contained in:
XiaoMoMi
2025-06-22 05:11:00 +08:00
committed by GitHub
209 changed files with 11544 additions and 2810 deletions

View File

@@ -11,6 +11,9 @@ repositories {
maven("https://nexus.phoenixdevt.fr/repository/maven-public/") // mmoitems
maven("https://repo.viaversion.com") // via
maven("https://repo.skriptlang.org/releases/") // skript
maven("https://nexus.neetgames.com/repository/maven-releases/") // mcmmo
maven("https://repo.dmulloy2.net/repository/public/") // mcmmo required
maven("https://repo.auxilor.io/repository/maven-public/") // eco
}
dependencies {
@@ -49,6 +52,21 @@ dependencies {
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false }
// MythicMobs
compileOnly("io.lumine:Mythic-Dist:5.9.0")
// McMMO
compileOnly("com.gmail.nossr50.mcMMO:mcMMO:2.2.038")
// MMOCore
compileOnly("net.Indyuce:MMOCore-API:1.12.1-SNAPSHOT")
// JobsReborn
compileOnly("com.github.Zrips:Jobs:v5.2.2.3")
// CustomFishing
compileOnly("net.momirealms:custom-fishing:2.3.3")
// eco
compileOnly("com.willfp:eco:6.70.1")
compileOnly("com.willfp:EcoJobs:3.56.1")
compileOnly("com.willfp:EcoSkills:3.46.1")
compileOnly("com.willfp:libreforge:4.58.1")
// AureliumSkills
compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21")
}
java {

View File

@@ -1,14 +1,15 @@
package net.momirealms.craftengine.bukkit.compatibility;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.compatibility.bettermodel.BetterModelModel;
import net.momirealms.craftengine.bukkit.compatibility.item.CustomFishingProvider;
import net.momirealms.craftengine.bukkit.compatibility.item.MMOItemsProvider;
import net.momirealms.craftengine.bukkit.compatibility.item.MythicMobsProvider;
import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider;
import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor;
import net.momirealms.craftengine.bukkit.compatibility.leveler.AuraSkillsLevelerProvider;
import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineModel;
import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineUtils;
import net.momirealms.craftengine.bukkit.compatibility.leveler.*;
import net.momirealms.craftengine.bukkit.compatibility.model.bettermodel.BetterModelModel;
import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineModel;
import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineUtils;
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicMobsListener;
import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils;
import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners;
@@ -30,10 +31,7 @@ import net.momirealms.craftengine.core.world.WorldManager;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
public class BukkitCompatibilityManager implements CompatibilityManager {
private final BukkitCraftEngine plugin;
@@ -108,6 +106,29 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
this.registerLevelerProvider("AuraSkills", new AuraSkillsLevelerProvider());
logHook("AuraSkills");
}
if (this.isPluginEnabled("AureliumSkills")) {
this.registerLevelerProvider("AureliumSkills", new AureliumSkillsLevelerProvider());
}
if (this.isPluginEnabled("McMMO")) {
this.registerLevelerProvider("mcMMO", new McMMOLevelerProvider());
logHook("McMMO");
}
if (this.isPluginEnabled("MMOCore")) {
this.registerLevelerProvider("MMOCore", new MMOCoreLevelerProvider());
logHook("MMOCore");
}
if (this.isPluginEnabled("Jobs")) {
registerLevelerProvider("Jobs", new JobsRebornLevelerProvider());
logHook("Jobs");
}
if (this.isPluginEnabled("EcoSkills")) {
registerLevelerProvider("EcoSkills", new EcoSkillsLevelerProvider());
logHook("EcoSkills");
}
if (this.isPluginEnabled("EcoJobs")) {
registerLevelerProvider("EcoJobs", new EcoJobsLevelerProvider());
logHook("EcoJobs");
}
if (this.isPluginEnabled("MythicMobs")) {
BukkitItemManager.instance().registerExternalItemProvider(new MythicMobsProvider());
new MythicMobsListener(this.plugin);
@@ -189,7 +210,18 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
Plugin fastAsyncWorldEdit = Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit");
String version = VersionHelper.isPaper() ? fastAsyncWorldEdit.getPluginMeta().getVersion() : fastAsyncWorldEdit.getDescription().getVersion();
if (!this.fastAsyncWorldEditVersionCheck(version)) {
this.plugin.logger().warn("[Compatibility] Please update FastAsyncWorldEdit to 2.13.0 or newer for better compatibility");
if (VersionHelper.isOrAbove1_20_3()) {
this.plugin.logger().severe("");
if (Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) {
this.plugin.logger().severe("[Compatibility] 插件需要更新 FastAsyncWorldEdit 到 2.13.0 或更高版本,以获得更好的兼容性。(当前版本: " + version + ")");
this.plugin.logger().severe("[Compatibility] 请前往 https://ci.athion.net/job/FastAsyncWorldEdit/ 下载最新版本");
} else {
this.plugin.logger().severe("[Compatibility] Update FastAsyncWorldEdit to v2.13.0+ for better compatibility (Current: " + version + ")");
this.plugin.logger().severe("[Compatibility] Download latest version: https://ci.athion.net/job/FastAsyncWorldEdit/");
}
this.plugin.logger().severe("");
}
}
new WorldEditBlockRegister(BukkitBlockManager.instance(), true);
}
@@ -223,6 +255,10 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
itemManager.registerExternalItemProvider(new MMOItemsProvider());
logHook("MMOItems");
}
if (this.isPluginEnabled("CustomFishing")) {
itemManager.registerExternalItemProvider(new CustomFishingProvider());
logHook("CustomFishing");
}
}
private Plugin getPlugin(String name) {

View File

@@ -0,0 +1,23 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import net.momirealms.craftengine.core.item.ExternalItemProvider;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.mechanic.context.Context;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class CustomFishingProvider implements ExternalItemProvider<ItemStack> {
@Override
public String plugin() {
return "CustomFishing";
}
@Nullable
@Override
public ItemStack build(String id, ItemBuildContext context) {
return BukkitCustomFishingPlugin.getInstance().getItemManager()
.buildInternal(Context.player(((Player) context.player().platformPlayer())), id);
}
}

View File

@@ -0,0 +1,27 @@
package net.momirealms.craftengine.bukkit.compatibility.leveler;
import com.archyx.aureliumskills.api.AureliumAPI;
import com.archyx.aureliumskills.leveler.Leveler;
import com.archyx.aureliumskills.skills.SkillRegistry;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
public class AureliumSkillsLevelerProvider implements LevelerProvider {
private final Leveler leveler;
private final SkillRegistry skillRegistry;
public AureliumSkillsLevelerProvider() {
this.leveler = AureliumAPI.getPlugin().getLeveler();
this.skillRegistry = AureliumAPI.getPlugin().getSkillRegistry();
}
@Override
public void addExp(Player player, String target, double amount) {
this.leveler.addXp(((org.bukkit.entity.Player) player.platformPlayer()), this.skillRegistry.getSkill(target), amount);
}
@Override
public int getLevel(Player player, String target) {
return AureliumAPI.getSkillLevel(((org.bukkit.entity.Player) player.platformPlayer()), this.skillRegistry.getSkill(target));
}
}

View File

@@ -0,0 +1,28 @@
package net.momirealms.craftengine.bukkit.compatibility.leveler;
import com.willfp.ecojobs.api.EcoJobsAPI;
import com.willfp.ecojobs.jobs.Job;
import com.willfp.ecojobs.jobs.Jobs;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
public class EcoJobsLevelerProvider implements LevelerProvider {
@Override
public void addExp(Player player, String target, double amount) {
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
for (Job job : EcoJobsAPI.getActiveJobs(bukkitPlayer)) {
if (job.getId().equals(target)) {
EcoJobsAPI.giveJobExperience(bukkitPlayer, job, amount);
return;
}
}
}
@Override
public int getLevel(Player player, String target) {
Job job = Jobs.getByID(target);
if (job == null) return 0;
return EcoJobsAPI.getJobLevel(((org.bukkit.entity.Player) player.platformPlayer()), job);
}
}

View File

@@ -0,0 +1,21 @@
package net.momirealms.craftengine.bukkit.compatibility.leveler;
import com.willfp.ecoskills.api.EcoSkillsAPI;
import com.willfp.ecoskills.skills.Skills;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
import java.util.Objects;
public class EcoSkillsLevelerProvider implements LevelerProvider {
@Override
public void addExp(Player player, String target, double amount) {
EcoSkillsAPI.gainSkillXP(((org.bukkit.entity.Player) player.platformPlayer()), Objects.requireNonNull(Skills.INSTANCE.getByID(target)), amount);
}
@Override
public int getLevel(Player player, String target) {
return EcoSkillsAPI.getSkillLevel(((org.bukkit.entity.Player) player.platformPlayer()), Objects.requireNonNull(Skills.INSTANCE.getByID(target)));
}
}

View File

@@ -0,0 +1,30 @@
package net.momirealms.craftengine.bukkit.compatibility.leveler;
import com.gamingmesh.jobs.Jobs;
import com.gamingmesh.jobs.container.Job;
import com.gamingmesh.jobs.container.JobProgression;
import com.gamingmesh.jobs.container.JobsPlayer;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
public class JobsRebornLevelerProvider implements LevelerProvider {
@Override
public void addExp(Player player, String target, double amount) {
JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player.uuid());
Job job = Jobs.getJob(target);
if (jobsPlayer != null && jobsPlayer.isInJob(job)) {
Jobs.getPlayerManager().addExperience(jobsPlayer, job, amount);
}
}
@Override
public int getLevel(Player player, String target) {
JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player.uuid());
if (jobsPlayer != null) {
JobProgression jobProgression = jobsPlayer.getJobProgression(Jobs.getJob(target));
return jobProgression.getLevel();
}
return 0;
}
}

View File

@@ -0,0 +1,20 @@
package net.momirealms.craftengine.bukkit.compatibility.leveler;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.EXPSource;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
public class MMOCoreLevelerProvider implements LevelerProvider {
@Override
public void addExp(Player player, String target, double amount) {
MMOCore.plugin.professionManager.get(target).giveExperience(PlayerData.get(player.uuid()), amount, null , EXPSource.OTHER);
}
@Override
public int getLevel(Player player, String target) {
return PlayerData.get(player.uuid()).getCollectionSkills().getLevel(MMOCore.plugin.professionManager.get(target));
}
}

View File

@@ -0,0 +1,19 @@
package net.momirealms.craftengine.bukkit.compatibility.leveler;
import com.gmail.nossr50.api.ExperienceAPI;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
public class McMMOLevelerProvider implements LevelerProvider {
@Override
public void addExp(Player player, String target, double amount) {
ExperienceAPI.addRawXP((org.bukkit.entity.Player) player.platformPlayer(), target, (float) amount, "UNKNOWN");
}
@Override
public int getLevel(Player player, String target) {
return ExperienceAPI.getLevel((org.bukkit.entity.Player) player.platformPlayer(), PrimarySkillType.valueOf(target));
}
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.bukkit.compatibility.bettermodel;
package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
import net.momirealms.craftengine.core.entity.AbstractEntity;
import net.momirealms.craftengine.core.entity.furniture.AbstractExternalModel;

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.bukkit.compatibility.bettermodel;
package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
import kr.toxicity.model.api.BetterModel;
import kr.toxicity.model.api.data.renderer.BlueprintRenderer;

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.bukkit.compatibility.modelengine;
package net.momirealms.craftengine.bukkit.compatibility.model.modelengine;
import net.momirealms.craftengine.core.entity.AbstractEntity;
import net.momirealms.craftengine.core.entity.furniture.AbstractExternalModel;

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.bukkit.compatibility.modelengine;
package net.momirealms.craftengine.bukkit.compatibility.model.modelengine;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel;

View File

@@ -3,11 +3,16 @@ package net.momirealms.craftengine.bukkit.compatibility.worldedit;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.util.ProcessorTraverser;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.MaskingExtent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.pattern.Pattern;
@@ -41,12 +46,14 @@ import static java.util.Objects.requireNonNull;
public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
private static int[] ordinalToIbdID;
private final Extent extent;
private final Set<CEChunk> chunksToSave;
private final CEWorld ceWorld;
private final Set<ChunkPos> brokenChunks = Collections.synchronizedSet(new HashSet<>());
protected FastAsyncWorldEditDelegate(EditSessionEvent event) {
super(event.getExtent());
protected FastAsyncWorldEditDelegate(EditSessionEvent event, Extent extent) {
super(extent);
this.extent = extent;
this.chunksToSave = new HashSet<>();
World weWorld = event.getWorld();
org.bukkit.World world = Bukkit.getWorld(requireNonNull(weWorld).getName());
@@ -71,7 +78,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
World weWorld = event.getWorld();
if (weWorld == null) return;
if (event.getStage() == EditSession.Stage.BEFORE_CHANGE) {
event.setExtent(new FastAsyncWorldEditDelegate(event));
event.setExtent(new FastAsyncWorldEditDelegate(event, event.getExtent()));
}
}
});
@@ -95,52 +102,75 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
@Override
public int setBlocks(final Set<BlockVector3> vset, final Pattern pattern) {
this.processBlocks(vset, pattern);
this.processBlocks(vset, pattern, getMask(), null);
return super.setBlocks(vset, pattern);
}
@Override
public int setBlocks(final Region region, final Pattern pattern) {
this.processBlocks(region, pattern);
this.processBlocks(region, pattern, getMask(), null);
return super.setBlocks(region, pattern);
}
@Override
public <B extends BlockStateHolder<B>> int setBlocks(final Region region, final B block) {
this.processBlocks(region, block);
this.processBlocks(region, block, getMask(), null);
return super.setBlocks(region, block);
}
@Override
public int replaceBlocks(Region region, Mask mask, Pattern pattern) {
this.processBlocks(region, pattern);
this.processBlocks(region, pattern, mask, null);
return super.replaceBlocks(region, mask, pattern);
}
@Override
public <B extends BlockStateHolder<B>> int replaceBlocks(final Region region, final Set<BaseBlock> filter, final B replacement) {
this.processBlocks(region, replacement);
this.processBlocks(region, replacement, null, filter);
return super.replaceBlocks(region, filter, replacement);
}
@Override
public int replaceBlocks(final Region region, final Set<BaseBlock> filter, final Pattern pattern) {
this.processBlocks(region, pattern);
this.processBlocks(region, pattern, null, filter);
return super.replaceBlocks(region, filter, pattern);
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T block) {
BaseBlock oldBlockState = getBlock(x, y, z).toBaseBlock();
this.processBlock(x, y, z, block.toBaseBlock(), oldBlockState);
return super.setBlock(x, y, z, block);
boolean result = extent.setBlock(x, y, z, block);
if (result) {
Mask mask = getMask();
if (mask != null && !mask.test(BlockVector3.at(x, y, z))) return true;
BaseBlock oldBlockState = getBlock(x, y, z).toBaseBlock();
this.processBlock(x, y, z, block.toBaseBlock(), oldBlockState);
}
return result;
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 position, T block) {
BaseBlock oldBlockState = getBlock(position).toBaseBlock();
this.processBlock(position.x(), position.y(), position.z(), block.toBaseBlock(), oldBlockState);
return super.setBlock(position, block);
boolean result = super.setBlock(position, block);
if (result) {
Mask mask = getMask();
if (mask != null && !mask.test(position)) return true;
BaseBlock oldBlockState = getBlock(position).toBaseBlock();
this.processBlock(position.x(), position.y(), position.z(), block.toBaseBlock(), oldBlockState);
}
return result;
}
public Mask getMask() {
MaskingExtent maskingExtent = new ExtentTraverser<>(getExtent()).findAndGet(MaskingExtent.class);
if (maskingExtent == null) {
ExtentBatchProcessorHolder processorExtent =
new ExtentTraverser<>(getExtent()).findAndGet(ExtentBatchProcessorHolder.class);
if (processorExtent != null) {
maskingExtent =
new ProcessorTraverser<>(processorExtent.getProcessor()).find(MaskingExtent.class);
}
}
return maskingExtent != null ? maskingExtent.getMask() : null;
}
@Override
@@ -160,9 +190,13 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
return operation;
}
private void processBlocks(Iterable<BlockVector3> region, Pattern pattern) {
private void processBlocks(Iterable<BlockVector3> region, Pattern pattern, @Nullable Mask mask, @Nullable Set<BaseBlock> filter) {
final boolean hasMask = mask != null;
final boolean hasFilter = filter != null;
for (BlockVector3 position : region) {
if (hasMask && !mask.test(position)) continue;
BaseBlock blockState = pattern.applyBlock(position);
if (hasFilter && filter.contains(blockState)) continue;
BaseBlock oldBlockState = getBlock(position).toBaseBlock();
int blockX = position.x();
int blockY = position.y();

View File

@@ -77,14 +77,22 @@ paper {
// external models
register("ModelEngine") { required = false }
register("BetterModel") { required = false }
register("FreeMinecraftModels") { required = false }
// external items
register("NeigeItems") { required = false }
register("MMOItems") { required = false }
register("MythicMobs") { required = false }
register("CustomFishing") { required = false }
// leveler
register("AuraSkills") { required = false }
register("AureliumSkills") { required = false }
register("McMMO") { required = false }
register("MMOCore") { required = false }
register("Jobs") { required = false }
register("EcoSkills") { required = false }
register("EcoJobs") { required = false }
// anti grief lib
register("Dominion") { required = false }

View File

@@ -13,6 +13,7 @@ import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldEvents;
@@ -116,7 +117,8 @@ public final class CraftEngineBlocks {
if (success) {
FastNMS.INSTANCE.method$BlockStateBase$onPlace(blockState, worldServer, blockPos, oldBlockState, false);
if (playSound) {
location.getWorld().playSound(location, block.sounds().placeSound().toString(), SoundCategory.BLOCKS, block.sounds().placeSound().volume(), block.sounds().placeSound().pitch());
SoundData data = block.sounds().placeSound();
location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get());
}
}
return success;

View File

@@ -0,0 +1,56 @@
package net.momirealms.craftengine.bukkit.api.event;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
public class FurnitureAttemptBreakEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled;
private final BukkitFurniture furniture;
public FurnitureAttemptBreakEvent(@NotNull Player player,
@NotNull BukkitFurniture furniture) {
super(player);
this.furniture = furniture;
}
@NotNull
public Player player() {
return getPlayer();
}
@NotNull
public BukkitFurniture furniture() {
return this.furniture;
}
@NotNull
public Location location() {
return this.furniture.location();
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@NotNull
public HandlerList getHandlers() {
return getHandlerList();
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
}

View File

@@ -311,7 +311,7 @@ public class BlockEventListener implements Listener {
event.setCancelled(true);
return;
}
player.playSound(location, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume(), state.sounds().stepSound().pitch());
player.playSound(location, state.sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.sounds().stepSound().volume().get(), state.sounds().stepSound().pitch().get());
} else if (Config.enableSoundSystem()) {
Object ownerBlock = BlockStateUtils.getBlockOwner(blockState);
if (manager.isBlockSoundRemoved(ownerBlock)) {
@@ -347,7 +347,7 @@ public class BlockEventListener implements Listener {
Block block = blocks.get(i);
Location location = block.getLocation();
BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
ImmutableBlockState state = manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
ImmutableBlockState state = this.manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state != null && !state.isEmpty()) {
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(blockPos));
ContextHolder.Builder builder = ContextHolder.builder()

View File

@@ -34,11 +34,14 @@ import net.momirealms.craftengine.core.pack.ResourceLocation;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.sound.Sounds;
import net.momirealms.craftengine.core.util.*;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -48,11 +51,19 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class BukkitBlockManager extends AbstractBlockManager {
private static BukkitBlockManager instance;
@@ -74,6 +85,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
private Map<Key, Integer> registeredRealBlockSlots;
// A set of blocks that sounds have been removed
private Set<Object> affectedSoundBlocks;
private Map<Object, Pair<SoundData, SoundData>> affectedOpenableBlockSounds;
private Map<Key, Key> soundMapper;
// A list to record the order of registration
private List<Key> blockRegisterOrder = new ObjectArrayList<>();
@@ -285,7 +297,8 @@ public class BukkitBlockManager extends AbstractBlockManager {
ImmutableMap.Builder<Key, Integer> builder1 = ImmutableMap.builder();
ImmutableMap.Builder<Integer, Object> builder2 = ImmutableMap.builder();
ImmutableMap.Builder<Key, List<Integer>> builder3 = ImmutableMap.builder();
Set<Object> affectedSounds = new HashSet<>();
Set<Object> affectedBlockSounds = new HashSet<>();
Map<Object, Pair<SoundData, SoundData>> affectedDoors = new IdentityHashMap<>();
Set<Object> affectedBlocks = new HashSet<>();
List<Key> order = new ArrayList<>();
@@ -293,7 +306,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
int counter = 0;
for (Map.Entry<Key, Integer> baseBlockAndItsCount : this.registeredRealBlockSlots.entrySet()) {
counter = registerBlockVariants(baseBlockAndItsCount, counter, builder1, builder2, builder3, affectedSounds, order);
counter = registerBlockVariants(baseBlockAndItsCount, counter, builder1, builder2, builder3, affectedBlockSounds, order);
}
freezeRegistry();
@@ -306,7 +319,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
for (Object block : (Iterable<Object>) MBuiltInRegistries.BLOCK) {
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(block);
if (affectedSounds.contains(soundType)) {
if (affectedBlockSounds.contains(soundType)) {
Object state = getOnlyBlockState(block);
if (BlockStateUtils.isVanillaBlock(state)) {
affectedBlocks.add(block);
@@ -320,7 +333,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
this.affectedSoundBlocks = ImmutableSet.copyOf(affectedBlocks);
ImmutableMap.Builder<Key, Key> soundMapperBuilder = ImmutableMap.builder();
for (Object soundType : affectedSounds) {
for (Object soundType : affectedBlockSounds) {
for (Field field : List.of(CoreReflections.field$SoundType$placeSound, CoreReflections.field$SoundType$fallSound, CoreReflections.field$SoundType$hitSound, CoreReflections.field$SoundType$stepSound, CoreReflections.field$SoundType$breakSound)) {
Object soundEvent = field.get(soundType);
Key previousId = Key.of(FastNMS.INSTANCE.field$SoundEvent$location(soundEvent).toString());
@@ -328,12 +341,53 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
Predicate<Key> predicate = it -> this.realBlockArranger.containsKey(it);
Consumer<Key> soundCallback = s -> soundMapperBuilder.put(s, Key.of("replaced." + s.value()));
BiConsumer<Object, Pair<SoundData, SoundData>> affectedBlockCallback = affectedDoors::put;
Function<Key, SoundData> soundMapper = (k) -> SoundData.of(k, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f));
collectDoorSounds(predicate, Sounds.WOODEN_TRAPDOOR_OPEN, Sounds.WOODEN_TRAPDOOR_CLOSE, Sounds.WOODEN_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.NETHER_WOOD_TRAPDOOR_OPEN, Sounds.NETHER_WOOD_TRAPDOOR_CLOSE, Sounds.NETHER_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_TRAPDOOR_OPEN, Sounds.BAMBOO_WOOD_TRAPDOOR_CLOSE, Sounds.BAMBOO_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.CHERRY_WOOD_TRAPDOOR_OPEN, Sounds.CHERRY_WOOD_TRAPDOOR_CLOSE, Sounds.CHERRY_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.COPPER_TRAPDOOR_OPEN, Sounds.COPPER_TRAPDOOR_CLOSE, Sounds.COPPER_TRAPDOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.WOODEN_DOOR_OPEN, Sounds.WOODEN_DOOR_CLOSE, Sounds.WOODEN_DOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.NETHER_WOOD_DOOR_OPEN, Sounds.NETHER_WOOD_DOOR_CLOSE, Sounds.NETHER_DOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_DOOR_OPEN, Sounds.BAMBOO_WOOD_DOOR_CLOSE, Sounds.BAMBOO_DOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.CHERRY_WOOD_DOOR_OPEN, Sounds.CHERRY_WOOD_DOOR_CLOSE, Sounds.CHERRY_DOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.COPPER_DOOR_OPEN, Sounds.COPPER_DOOR_CLOSE, Sounds.COPPER_DOORS, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.WOODEN_FENCE_GATE_OPEN, Sounds.WOODEN_FENCE_GATE_CLOSE, Sounds.WOODEN_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.NETHER_WOOD_FENCE_GATE_OPEN, Sounds.NETHER_WOOD_FENCE_GATE_CLOSE, Sounds.NETHER_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.BAMBOO_WOOD_FENCE_GATE_OPEN, Sounds.BAMBOO_WOOD_FENCE_GATE_CLOSE, Sounds.BAMBOO_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
collectDoorSounds(predicate, Sounds.CHERRY_WOOD_FENCE_GATE_OPEN, Sounds.CHERRY_WOOD_FENCE_GATE_CLOSE, Sounds.CHERRY_FENCE_GATES, soundMapper, soundCallback, affectedBlockCallback);
this.affectedOpenableBlockSounds = ImmutableMap.copyOf(affectedDoors);
this.soundMapper = soundMapperBuilder.buildKeepingLast();
} catch (Throwable e) {
plugin.logger().warn("Failed to inject blocks.", e);
}
}
private void collectDoorSounds(Predicate<Key> isUsedForCustomBlock,
Key openSound,
Key closeSound,
List<Key> doors,
Function<Key, SoundData> soundMapper,
Consumer<Key> soundCallback,
BiConsumer<Object, Pair<SoundData, SoundData>> affectedBlockCallback) {
for (Key d : doors) {
if (isUsedForCustomBlock.test(d)) {
soundCallback.accept(openSound);
soundCallback.accept(closeSound);
for (Key door : doors) {
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(door));
if (block != null) {
affectedBlockCallback.accept(block, Pair.of(soundMapper.apply(Key.of("replaced." + openSound.value())), soundMapper.apply(Key.of("replaced." + closeSound.value()))));
}
}
break;
}
}
}
public class BlockParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"};
@@ -556,22 +610,29 @@ public class BukkitBlockManager extends AbstractBlockManager {
private void loadMappingsAndAdditionalBlocks() {
this.plugin.logger().info("Loading mappings.yml.");
File mappingFile = new File(plugin.dataFolderFile(), "mappings.yml");
YamlDocument mappings = Config.instance().loadOrCreateYamlData("mappings.yml");
Map<String, String> blockStateMappings = loadBlockStateMappings(mappings);
this.validateBlockStateMappings(mappingFile, blockStateMappings);
Map<Integer, String> stateMap = new Int2ObjectOpenHashMap<>();
Map<Key, Integer> blockTypeCounter = new LinkedHashMap<>();
Map<Integer, Integer> appearanceMapper = new Int2IntOpenHashMap();
Map<Key, List<Integer>> appearanceArranger = new HashMap<>();
for (Map.Entry<String, String> entry : blockStateMappings.entrySet()) {
this.processBlockStateMapping(mappingFile, entry, stateMap, blockTypeCounter, appearanceMapper, appearanceArranger);
Path mappingsFile = this.plugin.dataFolderPath().resolve("mappings.yml");
if (!Files.exists(mappingsFile)) {
this.plugin.saveResource("mappings.yml");
}
Yaml yaml = new Yaml(new StringKeyConstructor(mappingsFile, new LoaderOptions()));
try (InputStream inputStream = Files.newInputStream(mappingsFile)) {
Map<String, String> blockStateMappings = loadBlockStateMappings(yaml.load(inputStream));
this.validateBlockStateMappings(mappingsFile, blockStateMappings);
Map<Integer, String> stateMap = new Int2ObjectOpenHashMap<>();
Map<Key, Integer> blockTypeCounter = new LinkedHashMap<>();
Map<Integer, Integer> appearanceMapper = new Int2IntOpenHashMap();
Map<Key, List<Integer>> appearanceArranger = new HashMap<>();
for (Map.Entry<String, String> entry : blockStateMappings.entrySet()) {
this.processBlockStateMapping(mappingsFile, entry, stateMap, blockTypeCounter, appearanceMapper, appearanceArranger);
}
this.blockAppearanceMapper = ImmutableMap.copyOf(appearanceMapper);
this.blockAppearanceArranger = ImmutableMap.copyOf(appearanceArranger);
this.plugin.logger().info("Freed " + this.blockAppearanceMapper.size() + " block state appearances.");
YamlDocument additionalYaml = Config.instance().loadOrCreateYamlData("additional-real-blocks.yml");
this.registeredRealBlockSlots = this.buildRegisteredRealBlockSlots(blockTypeCounter, additionalYaml);
} catch (IOException e) {
throw new RuntimeException("Failed to init mappings.yml", e);
}
this.blockAppearanceMapper = ImmutableMap.copyOf(appearanceMapper);
this.blockAppearanceArranger = ImmutableMap.copyOf(appearanceArranger);
this.plugin.logger().info("Freed " + this.blockAppearanceMapper.size() + " block state appearances.");
YamlDocument additionalYaml = Config.instance().loadOrCreateYamlData("additional-real-blocks.yml");
this.registeredRealBlockSlots = this.buildRegisteredRealBlockSlots(blockTypeCounter, additionalYaml);
}
private void recordVanillaNoteBlocks() {
@@ -598,9 +659,17 @@ public class BukkitBlockManager extends AbstractBlockManager {
return this.affectedSoundBlocks.contains(block);
}
private Map<String, String> loadBlockStateMappings(YamlDocument mappings) {
public boolean isOpenableBlockSoundRemoved(Object block) {
return this.affectedOpenableBlockSounds.containsKey(block);
}
public SoundData getRemovedOpenableBlockSound(Object block, boolean open) {
return open ? this.affectedOpenableBlockSounds.get(block).left() : this.affectedOpenableBlockSounds.get(block).right();
}
private Map<String, String> loadBlockStateMappings(Map<String, Object> mappings) {
Map<String, String> blockStateMappings = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : mappings.getStringRouteMappedValues(false).entrySet()) {
for (Map.Entry<String, Object> entry : mappings.entrySet()) {
if (entry.getValue() instanceof String afterValue) {
blockStateMappings.put(entry.getKey(), afterValue);
}
@@ -608,7 +677,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
return blockStateMappings;
}
private void validateBlockStateMappings(File mappingFile, Map<String, String> blockStateMappings) {
private void validateBlockStateMappings(Path mappingFile, Map<String, String> blockStateMappings) {
Map<String, String> temp = new HashMap<>(blockStateMappings);
for (Map.Entry<String, String> entry : temp.entrySet()) {
String state = entry.getValue();
@@ -619,7 +688,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
private void processBlockStateMapping(File mappingFile,
private void processBlockStateMapping(Path mappingFile,
Map.Entry<String, String> entry,
Map<Integer, String> stateMap,
Map<Key, Integer> counter,
@@ -654,7 +723,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
private Object createBlockState(File mappingFile, String state) {
private Object createBlockState(Path mappingFile, String state) {
try {
Object registryOrLookUp = MBuiltInRegistries.BLOCK;
if (CoreReflections.method$Registry$asLookup != null) {

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.block;
import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBehavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
@@ -10,6 +11,8 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.SoundUtils;
import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviors;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -46,6 +49,21 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
super(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable);
}
@Override
protected BlockBehavior setupBehavior(List<Map<String, Object>> behaviorConfig) {
if (behaviorConfig.isEmpty()) {
return new EmptyBlockBehavior();
} else if (behaviorConfig.size() == 1) {
return BlockBehaviors.fromMap(this, behaviorConfig.get(0));
} else {
List<AbstractBlockBehavior> behaviors = new ArrayList<>();
for (Map<String, Object> config : behaviorConfig) {
behaviors.add((AbstractBlockBehavior) BlockBehaviors.fromMap(this, config));
}
return new UnsafeCompositeBlockBehavior(this, behaviors);
}
}
@SuppressWarnings("unchecked")
@Nullable
@Override
@@ -74,12 +92,27 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
BlockStateUtils.setHardness(mcBlockState, settings.hardness());
BlockStateUtils.setPushReaction(mcBlockState, settings.pushReaction());
BlockStateUtils.setReplaceable(mcBlockState, settings.replaceable());
boolean canOcclude;
if (settings.canOcclude() == Tristate.TRUE) {
BlockStateUtils.setCanOcclude(mcBlockState, true);
canOcclude = true;
} else if (settings.canOcclude() == Tristate.FALSE) {
BlockStateUtils.setCanOcclude(mcBlockState, false);
canOcclude = false;
} else {
BlockStateUtils.setCanOcclude(mcBlockState, BlockStateUtils.isOcclude(state.vanillaBlockState().handle()));
canOcclude = BlockStateUtils.isOcclude(state.vanillaBlockState().handle());
BlockStateUtils.setCanOcclude(mcBlockState, canOcclude);
}
boolean useShapeForLightOcclusion;
if (settings.useShapeForLightOcclusion() == Tristate.TRUE) {
BlockStateUtils.setUseShapeForLightOcclusion(mcBlockState, true);
useShapeForLightOcclusion = true;
} else if (settings.useShapeForLightOcclusion() == Tristate.FALSE) {
BlockStateUtils.setUseShapeForLightOcclusion(mcBlockState, false);
useShapeForLightOcclusion = false;
} else {
useShapeForLightOcclusion = CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(state.vanillaBlockState().handle());
BlockStateUtils.setUseShapeForLightOcclusion(mcBlockState, useShapeForLightOcclusion);
}
if (settings.isRedstoneConductor() == Tristate.TRUE) {
BlockStateUtils.setIsRedstoneConductor(mcBlockState, ALWAYS_TRUE);
@@ -126,26 +159,48 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
// set block side properties
CoreReflections.field$BlockBehaviour$explosionResistance.set(mcBlock, settings.resistance());
CoreReflections.field$BlockBehaviour$soundType.set(mcBlock, SoundUtils.toSoundType(settings.sounds()));
// 1.21.2以前要在init cache之前设定 isConditionallyFullOpaque
if (!VersionHelper.isOrAbove1_21_2()) {
boolean isConditionallyFullOpaque = canOcclude & useShapeForLightOcclusion;
CoreReflections.field$BlockStateBase$isConditionallyFullOpaque.set(mcBlockState, isConditionallyFullOpaque);
}
// init cache
CoreReflections.method$BlockStateBase$initCache.invoke(mcBlockState);
// set block light
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(state.vanillaBlockState().handle());
// modify cache
if (VersionHelper.isOrAbove1_21_2()) {
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$lightBlock.getInt(state.vanillaBlockState().handle());
// set block light
CoreReflections.field$BlockStateBase$lightBlock.set(mcBlockState, blockLight);
// set propagates skylight
if (settings.propagatesSkylightDown() == Tristate.TRUE) {
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(mcBlockState, true);
} else if (settings.propagatesSkylightDown() == Tristate.FALSE) {
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(mcBlockState, false);
} else {
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(mcBlockState, CoreReflections.field$BlockStateBase$propagatesSkylightDown.getBoolean(state.vanillaBlockState().handle()));
}
} else {
Object cache = CoreReflections.field$BlockStateBase$cache.get(mcBlockState);
int blockLight = settings.blockLight() != -1 ? settings.blockLight() : CoreReflections.field$BlockStateBase$Cache$lightBlock.getInt(CoreReflections.field$BlockStateBase$cache.get(state.vanillaBlockState().handle()));
// set block light
CoreReflections.field$BlockStateBase$Cache$lightBlock.set(cache, blockLight);
// set propagates skylight
if (settings.propagatesSkylightDown() == Tristate.TRUE) {
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, true);
} else if (settings.propagatesSkylightDown() == Tristate.FALSE) {
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, false);
} else {
CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.set(cache, CoreReflections.field$BlockStateBase$Cache$propagatesSkylightDown.getBoolean(CoreReflections.field$BlockStateBase$cache.get(state.vanillaBlockState().handle())));
}
}
// set fluid later
if (settings.fluidState()) {
CoreReflections.field$BlockStateBase$fluidState.set(mcBlockState, CoreReflections.method$FlowingFluid$getSource.invoke(MFluids.instance$Fluids$WATER, false));
CoreReflections.field$BlockStateBase$fluidState.set(mcBlockState, CoreReflections.method$FlowingFluid$getSource.invoke(MFluids.WATER, false));
} else {
CoreReflections.field$BlockStateBase$fluidState.set(mcBlockState, MFluids.instance$Fluids$EMPTY$defaultState);
CoreReflections.field$BlockStateBase$fluidState.set(mcBlockState, MFluids.EMPTY$defaultState);
}
// set random tick later
BlockStateUtils.setIsRandomlyTicking(mcBlockState, settings.isRandomlyTicking());
// set propagates skylight
BlockStateUtils.setPropagatesSkylightDown(mcBlockState, settings.propagatesSkylightDown());
// bind tags
Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId());
Set<Object> tags = new HashSet<>();

View File

@@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
@@ -62,10 +61,10 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio
}
@Override
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object world = args[1];
Object blockPos = args[2];
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(world, blockPos, thisBlock, 2);
}
@Override
@@ -86,7 +85,7 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio
return state;
}
if (this.delay != 0) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(level, blockPos, thisBlock, this.delay);
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(level, blockPos, thisBlock, this.delay);
return state;
}
if (!canSurvive(thisBlock, new Object[] {state, level, blockPos}, () -> true)) {
@@ -100,7 +99,7 @@ public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavio
}
world.playBlockSound(position, previousState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
return CoreReflections.method$Block$defaultBlockState.invoke(MBlocks.AIR);
return MBlocks.AIR$defaultState;
}
return state;
}

View File

@@ -1,6 +1,10 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MItems;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.MirrorUtils;
import net.momirealms.craftengine.bukkit.util.RotationUtils;
@@ -8,10 +12,8 @@ import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.Mirror;
import net.momirealms.craftengine.core.util.Rotation;
import net.momirealms.craftengine.core.util.*;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -75,16 +77,20 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
});
}
@Nullable
private MirrorFunction mirrorFunction;
@Nullable
private RotateFunction rotateFunction;
@Nullable
protected final Property<Boolean> waterloggedProperty;
@SuppressWarnings("unchecked")
public BukkitBlockBehavior(CustomBlock customBlock) {
super(customBlock);
for (Property<?> property : customBlock.properties()) {
Optional.ofNullable(HARD_CODED_PROPERTY_DATA.get(property.name())).ifPresent(
c -> c.accept(this, property)
);
Optional.ofNullable(HARD_CODED_PROPERTY_DATA.get(property.name())).ifPresent(c -> c.accept(this, property));
}
this.waterloggedProperty = (Property<Boolean>) customBlock.getProperty("waterlogged");
}
@Override
@@ -118,4 +124,48 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
Object rotate(Object thisBlock, ImmutableBlockState state, Rotation rotation) throws Exception;
}
private static final int pickupBlock$world = VersionHelper.isOrAbove1_20_2() ? 1 : 0;
private static final int pickupBlock$pos = VersionHelper.isOrAbove1_20_2() ? 2 : 1;
private static final int pickupBlock$blockState = VersionHelper.isOrAbove1_20_2() ? 3 : 2;
@Override
public Object pickupBlock(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.waterloggedProperty == null) return CoreReflections.instance$ItemStack$EMPTY;
Object blockState = args[pickupBlock$blockState];
Object world = args[pickupBlock$world];
Object pos = args[pickupBlock$pos];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState != null) {
if (immutableBlockState.get(this.waterloggedProperty)) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().handle(), 3);
return FastNMS.INSTANCE.constructor$ItemStack(MItems.WATER_BUCKET, 1);
}
}
return CoreReflections.instance$ItemStack$EMPTY;
}
@Override
public boolean placeLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.waterloggedProperty == null) return false;
Object blockState = args[2];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState != null) {
Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(args[3]);
if (!immutableBlockState.get(this.waterloggedProperty) && fluidType == MFluids.WATER) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().handle(), 3);
FastNMS.INSTANCE.method$LevelAccessor$scheduleFluidTick(args[0], args[1], fluidType, 5);
return true;
}
}
return false;
}
private static final int canPlaceLiquid$liquid = VersionHelper.isOrAbove1_20_2() ? 4 : 3;
@Override
public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.waterloggedProperty == null) return false;
return args[canPlaceLiquid$liquid] == MFluids.WATER;
}
}

View File

@@ -13,12 +13,19 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key SAPLING_BLOCK = Key.from("craftengine:sapling_block");
public static final Key ON_LIQUID_BLOCK = Key.from("craftengine:on_liquid_block");
public static final Key NEAR_LIQUID_BLOCK = Key.from("craftengine:near_liquid_block");
public static final Key WATERLOGGED_BLOCK = Key.from("craftengine:waterlogged_block");
public static final Key CONCRETE_POWDER_BLOCK = Key.from("craftengine:concrete_powder_block");
public static final Key VERTICAL_CROP_BLOCK = Key.from("craftengine:vertical_crop_block");
public static final Key CROP_BLOCK = Key.from("craftengine:crop_block");
public static final Key GRASS_BLOCK = Key.from("craftengine:grass_block");
public static final Key LAMP_BLOCK = Key.from("craftengine:lamp_block");
public static final Key TRAPDOOR_BLOCK = Key.from("craftengine:trapdoor_block");
public static final Key DOOR_BLOCK = Key.from("craftengine:door_block");
public static final Key STACKABLE_BLOCK = Key.from("craftengine:stackable_block");
public static final Key STURDY_BASE_BLOCK = Key.from("craftengine:sturdy_base_block");
public static final Key FENCE_GATE_BLOCK = Key.from("craftengine:fence_gate_block");
public static final Key SLAB_BLOCK = Key.from("craftengine:slab_block");
public static final Key STAIRS_BLOCK = Key.from("craftengine:stairs_block");
public static final Key PRESSURE_PLATE_BLOCK = Key.from("craftengine:pressure_plate_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -30,11 +37,18 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY);
register(ON_LIQUID_BLOCK, OnLiquidBlockBehavior.FACTORY);
register(NEAR_LIQUID_BLOCK, NearLiquidBlockBehavior.FACTORY);
register(WATERLOGGED_BLOCK, WaterLoggedBlockBehavior.FACTORY);
register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY);
register(VERTICAL_CROP_BLOCK, VerticalCropBlockBehavior.FACTORY);
register(CROP_BLOCK, CropBlockBehavior.FACTORY);
register(GRASS_BLOCK, GrassBlockBehavior.FACTORY);
register(LAMP_BLOCK, LampBlockBehavior.FACTORY);
register(TRAPDOOR_BLOCK, TrapDoorBlockBehavior.FACTORY);
register(DOOR_BLOCK, DoorBlockBehavior.FACTORY);
register(STACKABLE_BLOCK, StackableBlockBehavior.FACTORY);
register(STURDY_BASE_BLOCK, SturdyBaseBlockBehavior.FACTORY);
register(FENCE_GATE_BLOCK, FenceGateBlockBehavior.FACTORY);
register(SLAB_BLOCK, SlabBlockBehavior.FACTORY);
register(STAIRS_BLOCK, StairsBlockBehavior.FACTORY);
register(PRESSURE_PLATE_BLOCK, PressurePlateBlockBehavior.FACTORY);
}
}

View File

@@ -59,6 +59,7 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior {
return this.defaultBlockState;
}
@SuppressWarnings("UnstableApiUsage")
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
Object level = context.getLevel().serverWorld();
@@ -126,8 +127,8 @@ public class ConcretePowderBlockBehavior extends BukkitBlockBehavior {
private static boolean canSolidify(Object state) throws ReflectiveOperationException {
Object fluidState = CoreReflections.field$BlockStateBase$fluidState.get(state);
if (fluidState == null) return false;
Object fluidType = CoreReflections.method$FluidState$getType.invoke(fluidState);
return fluidType == MFluids.instance$Fluids$WATER || fluidType == MFluids.instance$Fluids$FLOWING_WATER;
Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(fluidState);
return fluidType == MFluids.WATER || fluidType == MFluids.FLOWING_WATER;
}
private static boolean touchesLiquid(Object level, Object pos) throws ReflectiveOperationException {

View File

@@ -0,0 +1,331 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
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.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.DoorHinge;
import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.*;
import org.bukkit.Bukkit;
import org.bukkit.GameEvent;
import org.bukkit.block.Block;
import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Door;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.util.Vector;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<DoubleBlockHalf> halfProperty;
private final Property<HorizontalDirection> facingProperty;
private final Property<DoorHinge> hingeProperty;
private final Property<Boolean> poweredProperty;
private final Property<Boolean> openProperty;
private final boolean canOpenWithHand;
private final boolean canOpenByWindCharge;
private final SoundData openSound;
private final SoundData closeSound;
public DoorBlockBehavior(CustomBlock block,
Property<DoubleBlockHalf> halfProperty,
Property<HorizontalDirection> facingProperty,
Property<DoorHinge> hingeProperty,
Property<Boolean> poweredProperty,
Property<Boolean> openProperty,
boolean canOpenWithHand,
boolean canOpenByWindCharge,
SoundData openSound,
SoundData closeSound) {
super(block, 0);
this.halfProperty = halfProperty;
this.facingProperty = facingProperty;
this.hingeProperty = hingeProperty;
this.poweredProperty = poweredProperty;
this.openProperty = openProperty;
this.canOpenWithHand = canOpenWithHand;
this.canOpenByWindCharge = canOpenByWindCharge;
this.openSound = openSound;
this.closeSound = closeSound;
}
public boolean isOpen(ImmutableBlockState state) {
return state.get(this.openProperty);
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object level;
Object blockPos;
Object blockState = args[0];
if (VersionHelper.isOrAbove1_21_2()) {
level = args[1];
blockPos = args[3];
} else {
level = args[3];
blockPos = args[4];
}
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState == null || immutableBlockState.isEmpty()) return blockState;
DoubleBlockHalf half = immutableBlockState.get(this.halfProperty);
Object direction = VersionHelper.isOrAbove1_21_2() ? args[4] : args[1];
if (DirectionUtils.isYAxis(direction) && half == DoubleBlockHalf.LOWER == (direction == CoreReflections.instance$Direction$UP)) {
ImmutableBlockState neighborState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(VersionHelper.isOrAbove1_21_2() ? args[6] : args[2]));
if (neighborState == null || neighborState.isEmpty()) {
return MBlocks.AIR$defaultState;
}
Optional<DoorBlockBehavior> anotherDoorBehavior = neighborState.behavior().getAs(DoorBlockBehavior.class);
if (anotherDoorBehavior.isEmpty()) {
return MBlocks.AIR$defaultState;
}
if (neighborState.get(anotherDoorBehavior.get().halfProperty) != half) {
return neighborState.with(anotherDoorBehavior.get().halfProperty, half).customBlockState().handle();
}
return MBlocks.AIR$defaultState;
} else {
if (half == DoubleBlockHalf.LOWER && direction == CoreReflections.instance$Direction$DOWN
&& !canSurvive(thisBlock, blockState, level, blockPos)) {
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(position, item);
}
world.playBlockSound(position, immutableBlockState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
return MBlocks.AIR$defaultState;
}
return blockState;
}
}
@Override
public void onExplosionHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.canOpenByWindCharge && FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3])) {
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(args[0]));
if (state == null || state.isEmpty()) return;
if (state.get(this.poweredProperty)) return;
if (state.get(this.halfProperty) == DoubleBlockHalf.LOWER) {
this.setOpen(null, args[1], state, LocationUtils.fromBlockPos(args[2]), !this.isOpen(state));
}
}
}
@Override
public void setPlacedBy(BlockPlaceContext context, ImmutableBlockState state) {
BlockPos pos = context.getClickedPos();
context.getLevel().setBlockAt(pos.x(), pos.y() + 1, pos.z(), state.with(this.halfProperty, DoubleBlockHalf.UPPER).customBlockState(), UpdateOption.UPDATE_ALL.flags());
}
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
World world = context.getLevel();
Object level = world.serverWorld();
BlockPos pos = context.getClickedPos();
if (pos.y() < context.getLevel().worldHeight().getMaxBuildHeight() && world.getBlockAt(pos.above()).canBeReplaced(context)) {
boolean hasSignal = FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, LocationUtils.toBlockPos(pos)) || FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, LocationUtils.toBlockPos(pos.above()));
return state.with(this.poweredProperty, hasSignal)
.with(this.facingProperty, context.getHorizontalDirection().toHorizontalDirection())
.with(this.openProperty, hasSignal)
.with(this.halfProperty, DoubleBlockHalf.LOWER)
.with(this.hingeProperty, getHinge(context));
}
return null;
}
private DoorHinge getHinge(BlockPlaceContext context) {
Object serverLevel = context.getLevel().serverWorld();
BlockPos clickedPos = context.getClickedPos();
Direction horizontalDirection = context.getHorizontalDirection();
BlockPos blockPos = clickedPos.above();
Direction counterClockWise = horizontalDirection.counterClockWise();
Object blockPos1 = LocationUtils.toBlockPos(clickedPos.relative(counterClockWise));
Object blockState1 = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, blockPos1);
Object blockPos2 = LocationUtils.toBlockPos(blockPos.relative(counterClockWise));
Object blockState2 = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, blockPos2);
Direction clockWise = horizontalDirection.clockWise();
Object blockPos3 = LocationUtils.toBlockPos(clickedPos.relative(clockWise));
Object blockState3 = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, blockPos3);
Object blockPos4 = LocationUtils.toBlockPos(blockPos.relative(clockWise));
Object blockState4 = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, blockPos4);
int i = (FastNMS.INSTANCE.method$BlockStateBase$isCollisionShapeFullBlock(blockState1, serverLevel, blockPos1) ? -1 : 0) +
(FastNMS.INSTANCE.method$BlockStateBase$isCollisionShapeFullBlock(blockState2, serverLevel, blockPos2) ? -1 : 0) +
(FastNMS.INSTANCE.method$BlockStateBase$isCollisionShapeFullBlock(blockState3, serverLevel, blockPos3) ? 1 : 0) +
(FastNMS.INSTANCE.method$BlockStateBase$isCollisionShapeFullBlock(blockState4, serverLevel, blockPos4) ? 1 : 0);
boolean anotherDoor1 = isAnotherDoor(blockState1);
boolean anotherDoor2 = isAnotherDoor(blockState3);
if ((!anotherDoor1 || anotherDoor2) && i <= 0) {
if ((!anotherDoor2 || anotherDoor1) && i == 0) {
int stepX = horizontalDirection.stepX();
int stepZ = horizontalDirection.stepZ();
Vec3d clickLocation = context.getClickLocation();
double d = clickLocation.x - (double) clickedPos.x();
double d1 = clickLocation.z - (double) clickedPos.z();
return stepX < 0 && d1 < (double) 0.5F || stepX > 0 && d1 > (double) 0.5F || stepZ < 0 && d > (double) 0.5F || stepZ > 0 && d < (double) 0.5F ? DoorHinge.RIGHT : DoorHinge.LEFT;
} else {
return DoorHinge.LEFT;
}
} else {
return DoorHinge.RIGHT;
}
}
private boolean isAnotherDoor(Object blockState) {
int id = BlockStateUtils.blockStateToId(blockState);
if (BlockStateUtils.isVanillaBlock(id)) {
BlockData blockData = BlockStateUtils.fromBlockData(blockState);
return blockData instanceof Door door && door.getHalf() == Bisected.Half.BOTTOM;
} else {
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id);
if (state.isEmpty()) return false;
Optional<DoorBlockBehavior> optional = state.behavior().getAs(DoorBlockBehavior.class);
return optional.isPresent() && state.get(optional.get().halfProperty) == DoubleBlockHalf.LOWER;
}
}
public void setOpen(@Nullable Player player, Object serverLevel, ImmutableBlockState state, BlockPos pos, boolean isOpen) {
if (isOpen(state) != isOpen) {
org.bukkit.World world = FastNMS.INSTANCE.method$Level$getCraftWorld(serverLevel);
FastNMS.INSTANCE.method$LevelWriter$setBlock(serverLevel, LocationUtils.toBlockPos(pos), state.with(this.openProperty, isOpen).customBlockState().handle(), UpdateOption.builder().updateImmediate().updateClients().build().flags());
world.sendGameEvent(player == null ? null : (org.bukkit.entity.Player) player.platformPlayer(), isOpen ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, new Vector(pos.x(), pos.y(), pos.z()));
SoundData soundData = isOpen ? this.openSound : this.closeSound;
if (soundData != null) {
new BukkitWorld(world).playBlockSound(
new Vec3d(pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5),
soundData
);
}
}
}
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
if (!this.canOpenWithHand) {
return InteractionResult.PASS;
}
setOpen(context.getPlayer(), context.getLevel().serverWorld(), state, context.getClickedPos(), !state.get(this.openProperty));
return InteractionResult.SUCCESS_AND_CANCEL;
}
@Override
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object type = VersionHelper.isOrAbove1_20_5() ? args[1] : args[3];
Object blockState = args[0];
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state == null || state.isEmpty()) return false;
if (type == CoreReflections.instance$PathComputationType$LAND || type == CoreReflections.instance$PathComputationType$AIR) {
return state.get(this.openProperty);
}
return false;
}
@SuppressWarnings("UnstableApiUsage")
@Override
public void neighborChanged(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object blockPos = args[2];
Object level = args[1];
Object blockState = args[0];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
Object anotherHalfPos = immutableBlockState.get(this.halfProperty) == DoubleBlockHalf.LOWER ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos);
Block bukkitBlock = FastNMS.INSTANCE.method$CraftBlock$at(level, blockPos);
Block anotherBukkitBlock = FastNMS.INSTANCE.method$CraftBlock$at(level, anotherHalfPos);
int power = Math.max(bukkitBlock.getBlockPower(), anotherBukkitBlock.getBlockPower());
int oldPower = immutableBlockState.get(this.poweredProperty) ? 15 : 0;
if (oldPower == 0 ^ power == 0) {
BlockRedstoneEvent event = new BlockRedstoneEvent(bukkitBlock, oldPower, power);
Bukkit.getPluginManager().callEvent(event);
boolean flag = event.getNewCurrent() > 0;
if (flag != immutableBlockState.get(this.openProperty)) {
org.bukkit.World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
world.sendGameEvent(null, flag ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, new Vector(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ()));
SoundData soundData = flag ? this.openSound : this.closeSound;
if (soundData != null) {
new BukkitWorld(world).playBlockSound(
new Vec3d(FastNMS.INSTANCE.field$Vec3i$x(blockPos) + 0.5, FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 0.5, FastNMS.INSTANCE.field$Vec3i$z(blockPos) + 0.5),
soundData
);
}
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, immutableBlockState.with(this.poweredProperty, flag).with(this.openProperty, flag).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS);
}
}
@Override
public boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception {
ImmutableBlockState customBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (customBlockState == null || customBlockState.isEmpty()) return false;
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos) - 1;
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z);
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
if (customBlockState.get(this.halfProperty) == DoubleBlockHalf.UPPER) {
ImmutableBlockState belowCustomState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(belowState));
if (belowCustomState == null || belowCustomState.isEmpty()) return false;
return belowCustomState.owner().value() == super.customBlock;
} else {
return (boolean) CoreReflections.method$BlockStateBase$isFaceSturdy.invoke(
belowState, world, belowPos, CoreReflections.instance$Direction$UP,
CoreReflections.instance$SupportType$FULL
);
}
}
@SuppressWarnings("unchecked")
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<DoubleBlockHalf> half = (Property<DoubleBlockHalf>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.door.missing_half");
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.door.missing_facing");
Property<DoorHinge> hinge = (Property<DoorHinge>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("hinge"), "warning.config.block.behavior.door.missing_hinge");
Property<Boolean> open = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("open"), "warning.config.block.behavior.door.missing_open");
Property<Boolean> powered = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.door.missing_powered");
boolean canOpenWithHand = (boolean) arguments.getOrDefault("can-open-with-hand", true);
boolean canOpenByWindCharge = (boolean) arguments.getOrDefault("can-open-by-wind-charge", true);
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
SoundData openSound = null;
SoundData closeSound = null;
if (sounds != null) {
openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
}
return new DoorBlockBehavior(block, half, facing, hinge, powered, open, canOpenWithHand, canOpenByWindCharge, openSound, closeSound);
}
}
}

View File

@@ -33,14 +33,14 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
}
@Override
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object world = args[1];
Object blockPos = args[2];
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(world, blockPos, thisBlock, 2);
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object world;
Object blockPos;
if (VersionHelper.isOrAbove1_21_2()) {
@@ -50,7 +50,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
world = args[3];
blockPos = args[4];
}
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 2);
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(world, blockPos, thisBlock, 2);
return args[0];
}
@@ -113,11 +113,11 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
Object pos = args[1];
Object entityData = CoreReflections.field$Entity$entityData.get(fallingBlock);
boolean isSilent = (boolean) CoreReflections.method$SynchedEntityData$get.invoke(entityData, CoreReflections.instance$Entity$DATA_SILENT);
Object blockState = args[2];
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
if (!isSilent) {
Object blockState = args[2];
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), immutableBlockState.sounds().landSound());
}

View File

@@ -0,0 +1,290 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
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.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MTagKeys;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.InteractUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
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.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
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 org.bukkit.Bukkit;
import org.bukkit.GameEvent;
import org.bukkit.block.Block;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class FenceGateBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<HorizontalDirection> facingProperty;
private final Property<Boolean> inWallProperty;
private final Property<Boolean> openProperty;
private final Property<Boolean> poweredProperty;
private final boolean canOpenWithHand;
private final boolean canOpenByWindCharge;
private final SoundData openSound;
private final SoundData closeSound;
public FenceGateBlockBehavior(
CustomBlock customBlock,
Property<HorizontalDirection> facing,
Property<Boolean> inWall,
Property<Boolean> open,
Property<Boolean> powered,
boolean canOpenWithHand,
boolean canOpenByWindCharge,
SoundData openSound,
SoundData closeSound
) {
super(customBlock);
this.facingProperty = facing;
this.inWallProperty = inWall;
this.openProperty = open;
this.poweredProperty = powered;
this.canOpenWithHand = canOpenWithHand;
this.canOpenByWindCharge = canOpenByWindCharge;
this.openSound = openSound;
this.closeSound = closeSound;
}
public boolean isOpen(ImmutableBlockState state) {
if (state == null || state.isEmpty() || !state.contains(this.openProperty)) return false;
return state.get(this.openProperty);
}
public boolean isWall(Object state) {
if (state == null) return false;
return FastNMS.INSTANCE.method$BlockStateBase$isTagKeyBlock(state, MTagKeys.Block$WALLS);
}
private Object getBlockState(Object level, BlockPos blockPos) {
return FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(blockPos));
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object blockState = args[0];
Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]);
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state == null || state.isEmpty()) return blockState;
if (state.get(this.facingProperty).toDirection().clockWise().axis() != direction.axis()) {
return superMethod.call();
}
Object neighborState = VersionHelper.isOrAbove1_21_2() ? args[6] : args[2];
Object level = VersionHelper.isOrAbove1_21_2() ? args[1] : args[3];
BlockPos blockPos = LocationUtils.fromBlockPos(VersionHelper.isOrAbove1_21_2() ? args[3] : args[4]);
Object relativeState = getBlockState(level, blockPos.relative(direction.opposite()));
boolean neighborStateIsWall = this.isWall(neighborState);
boolean relativeStateIsWall = this.isWall(relativeState);
boolean flag = neighborStateIsWall || relativeStateIsWall;
if (neighborStateIsWall) {
// TODO: 连接原版方块
}
if (relativeStateIsWall) {
// TODO: 连接原版方块
}
return state.with(this.inWallProperty, flag).customBlockState().handle();
}
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
Object level = context.getLevel().serverWorld();
BlockPos clickedPos = context.getClickedPos();
boolean hasNeighborSignal = FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, LocationUtils.toBlockPos(clickedPos));
Direction horizontalDirection = context.getHorizontalDirection();
Direction.Axis axis = horizontalDirection.axis();
boolean flag = axis == Direction.Axis.Z && (this.isWall(getBlockState(level, clickedPos.relative(Direction.WEST))))
|| this.isWall(getBlockState(level, clickedPos.relative(Direction.EAST)))
|| axis == Direction.Axis.X && (this.isWall(getBlockState(level, clickedPos.relative(Direction.NORTH)))
|| this.isWall(getBlockState(level, clickedPos.relative(Direction.SOUTH))));
// TODO: 连接原版方块
return state.owner().value().defaultState()
.with(this.facingProperty, horizontalDirection.toHorizontalDirection())
.with(this.openProperty, hasNeighborSignal)
.with(this.poweredProperty, hasNeighborSignal)
.with(this.inWallProperty, flag);
}
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
if (!this.canOpenWithHand) {
return InteractionResult.PASS;
}
if (context.getItem() == null) {
playerToggle(context, state);
return InteractionResult.SUCCESS;
} else if (!context.getPlayer().isSecondaryUseActive()) {
playerToggle(context, state);
return InteractionResult.SUCCESS_AND_CANCEL;
} else {
return InteractionResult.PASS;
}
}
@SuppressWarnings("unchecked")
private void playerToggle(UseOnContext context, ImmutableBlockState state) {
Player player = context.getPlayer();
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
player.swingHand(context.getHand());
}
}
@Override
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object type = VersionHelper.isOrAbove1_20_5() ? args[1] : args[3];
Object blockState = args[0];
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state == null || state.isEmpty()) return false;
if (type == CoreReflections.instance$PathComputationType$LAND || type == CoreReflections.instance$PathComputationType$AIR) {
return isOpen(state);
}
return false;
}
@Override
public void onExplosionHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.canOpenByWindCharge && FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3])) {
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(args[0]));
if (state == null || state.isEmpty()) return;
this.toggle(state, new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1])), LocationUtils.fromBlockPos(args[2]), null);
}
}
@SuppressWarnings("UnstableApiUsage")
@Override
public void neighborChanged(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object blockState = args[0];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
Object level = args[1];
Object blockPos = args[2];
boolean hasSignal = FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, blockPos);
if (hasSignal == immutableBlockState.get(this.poweredProperty)) return;
Block bblock = FastNMS.INSTANCE.method$CraftBlock$at(level, blockPos);
int power = bblock.getBlockPower();
int oldPower = isOpen(immutableBlockState) ? 15 : 0;
Object neighborBlock = args[3];
if (oldPower == 0 ^ power == 0 || FastNMS.INSTANCE.method$BlockStateBase$isSignalSource(FastNMS.INSTANCE.method$Block$defaultState(neighborBlock))) {
BlockRedstoneEvent event = new BlockRedstoneEvent(bblock, oldPower, power);
Bukkit.getPluginManager().callEvent(event);
hasSignal = event.getNewCurrent() > 0;
}
World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
boolean changed = isOpen(immutableBlockState) != hasSignal;
if (hasSignal && changed) {
Object abovePos = LocationUtils.above(blockPos);
Object aboveBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, abovePos);
if (CoreReflections.clazz$RedStoneWireBlock.isInstance(FastNMS.INSTANCE.method$BlockState$getBlock(aboveBlockState))) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, abovePos, MBlocks.AIR$defaultState, UpdateOption.UPDATE_ALL.flags());
world.dropItemNaturally(
new Vec3d(FastNMS.INSTANCE.field$Vec3i$x(abovePos) + 0.5, FastNMS.INSTANCE.field$Vec3i$y(abovePos) + 0.5, FastNMS.INSTANCE.field$Vec3i$z(abovePos) + 0.5),
BukkitItemManager.instance().createWrappedItem(ItemKeys.REDSTONE, null)
);
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos) != blockPos) {
return;
}
}
}
if (changed) {
immutableBlockState = immutableBlockState.with(this.openProperty, hasSignal);
FastNMS.INSTANCE.method$Level$getCraftWorld(level).sendGameEvent(null,
hasSignal ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE,
new Vector(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos), FastNMS.INSTANCE.field$Vec3i$z(blockPos))
);
this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, immutableBlockState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS);
}
private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) {
ImmutableBlockState newState;
if (state.get(this.openProperty)) {
newState = state.with(this.openProperty, false);
} else {
ImmutableBlockState blockState = state;
if (player != null) {
Direction direction = player.getDirection();
if (state.get(this.facingProperty).toDirection() == direction.opposite()) {
blockState = blockState.with(this.facingProperty, direction.toHorizontalDirection());
}
}
newState = blockState.with(this.openProperty, true);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
boolean open = isOpen(newState);
((org.bukkit.World) world.platformWorld()).sendGameEvent(
player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null,
open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE,
new Vector(pos.x(), pos.y(), pos.z())
);
this.playSound(pos, world, open);
}
private void playSound(BlockPos pos, World world, boolean open) {
if (open) {
if (this.openSound != null) {
world.playBlockSound(new Vec3d(pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5), this.openSound);
}
} else {
if (this.closeSound != null) {
world.playBlockSound(new Vec3d(pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5), this.closeSound);
}
}
}
public static class Factory implements BlockBehaviorFactory {
@Override
@SuppressWarnings("unchecked")
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.fence_gate.missing_facing");
Property<Boolean> inWall = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("in_wall"), "warning.config.block.behavior.fence_gate.missing_in_wall");
Property<Boolean> open = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("open"), "warning.config.block.behavior.fence_gate.missing_open");
Property<Boolean> powered = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.fence_gate.missing_powered");
boolean canOpenWithHand = (boolean) arguments.getOrDefault("can-open-with-hand", true);
boolean canOpenByWindCharge = (boolean) arguments.getOrDefault("can-open-by-wind-charge", true);
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
SoundData openSound = null;
SoundData closeSound = null;
if (sounds != null) {
openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
}
return new FenceGateBlockBehavior(block, facing, inWall, open, powered, canOpenWithHand, canOpenByWindCharge, openSound, closeSound);
}
}
}

View File

@@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.BlockBehavior;
@@ -53,7 +52,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
}
@Override
public void neighborChanged(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
public void neighborChanged(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object blockState = args[0];
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state == null || state.isEmpty()) return;
@@ -62,7 +61,7 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
boolean lit = state.get(this.litProperty);
if (lit != FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
if (lit) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 4);
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(world, blockPos, thisBlock, 4);
} else {
// TODO Call Event
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2);

View File

@@ -27,27 +27,26 @@ import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.event.block.LeavesDecayEvent;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
public class LeavesBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private static final Object LOG_TAG = BlockTags.getOrCreate(Key.of("minecraft", "logs"));
private final int maxDistance;
private final Property<Integer> distanceProperty;
private final Property<Boolean> persistentProperty;
@Nullable
private final Property<Boolean> waterloggedProperty;
public LeavesBlockBehavior(CustomBlock block, int maxDistance, Property<Integer> distanceProperty, Property<Boolean> persistentProperty, @Nullable Property<Boolean> waterloggedProperty) {
super(block, waterloggedProperty);
public LeavesBlockBehavior(CustomBlock block,
int maxDistance,
Property<Integer> distanceProperty,
Property<Boolean> persistentProperty) {
super(block);
this.maxDistance = maxDistance;
this.distanceProperty = distanceProperty;
this.persistentProperty = persistentProperty;
this.waterloggedProperty = waterloggedProperty;
}
public int getDistance(ImmutableBlockState state) {
@@ -85,7 +84,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
LeavesBlockBehavior behavior = optionalBehavior.get();
int distance = behavior.getDistanceAt(neighborState) + 1;
if (distance != 1 || behavior.getDistance(thisState) != distance) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(world, blockPos, thisBlock, 1);
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(world, blockPos, thisBlock, 1);
}
}
}
@@ -115,7 +114,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
}
@Override
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object level = args[1];
Object blockPos = args[2];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(args[0]));
@@ -190,9 +189,8 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<Boolean> persistent = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("persistent"), "warning.config.block.behavior.leaves.missing_persistent");
Property<Integer> distance = (Property<Integer>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("distance"), "warning.config.block.behavior.leaves.missing_distance");
Property<Boolean> waterlogged = (Property<Boolean>) block.getProperty("waterlogged");
int actual = distance.possibleValues().get(distance.possibleValues().size() - 1);
return new LeavesBlockBehavior(block, actual, distance, persistent, waterlogged);
return new LeavesBlockBehavior(block, actual, distance, persistent);
}
}
}

View File

@@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
@@ -19,8 +18,8 @@ import java.util.List;
import java.util.Map;
public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
private static final List<Object> WATER = List.of(MFluids.instance$Fluids$WATER, MFluids.instance$Fluids$FLOWING_WATER);
private static final List<Object> LAVA = List.of(MFluids.instance$Fluids$LAVA, MFluids.instance$Fluids$FLOWING_LAVA);
private static final List<Object> WATER = List.of(MFluids.WATER, MFluids.FLOWING_WATER);
private static final List<Object> LAVA = List.of(MFluids.LAVA, MFluids.FLOWING_LAVA);
public static final Factory FACTORY = new Factory();
private final boolean onWater;
private final boolean onLava;
@@ -64,7 +63,7 @@ public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
}
@Override
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) {
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
@@ -89,16 +88,16 @@ public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
return false;
}
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
Object fluidState = CoreReflections.method$Level$getFluidState.invoke(world, belowPos);
Object fluidStateAbove = CoreReflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos));
if (CoreReflections.method$FluidState$getType.invoke(fluidStateAbove) != MFluids.instance$Fluids$EMPTY) {
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) {
Object fluidState = FastNMS.INSTANCE.method$Level$getFluidState(world, belowPos);
Object fluidStateAbove = FastNMS.INSTANCE.method$Level$getFluidState(world, LocationUtils.above(belowPos));
if (FastNMS.INSTANCE.method$FluidState$getType(fluidStateAbove) != MFluids.EMPTY) {
return false;
}
if (this.onWater && (WATER.contains(CoreReflections.method$FluidState$getType.invoke(fluidState)) || FastNMS.INSTANCE.method$BlockState$getBlock(belowState) == MBlocks.ICE)) {
if (this.onWater && (WATER.contains(FastNMS.INSTANCE.method$FluidState$getType(fluidState)) || FastNMS.INSTANCE.method$BlockState$getBlock(belowState) == MBlocks.ICE)) {
return true;
}
if (this.onLava && LAVA.contains(CoreReflections.method$FluidState$getType.invoke(fluidState))) {
if (this.onLava && LAVA.contains(FastNMS.INSTANCE.method$FluidState$getType(fluidState))) {
return true;
}
return false;

View File

@@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
@@ -49,7 +48,7 @@ public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
}
@Override
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) {
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
@@ -58,7 +57,7 @@ public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
return mayPlaceOn(belowState, world, belowPos);
}
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) {
if (this.stackable) {
int id = BlockStateUtils.blockStateToId(belowState);
if (!BlockStateUtils.isVanillaBlock(id)) {
@@ -68,15 +67,15 @@ public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
}
}
}
Object fluidState = CoreReflections.method$Level$getFluidState.invoke(world, belowPos);
Object fluidStateAbove = CoreReflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos));
if (CoreReflections.method$FluidState$getType.invoke(fluidStateAbove) != MFluids.instance$Fluids$EMPTY) {
Object fluidState = FastNMS.INSTANCE.method$Level$getFluidState(world, belowPos);
Object fluidStateAbove = FastNMS.INSTANCE.method$Level$getFluidState(world, LocationUtils.above(belowPos));
if (FastNMS.INSTANCE.method$FluidState$getType(fluidStateAbove) != MFluids.EMPTY) {
return false;
}
if (this.onWater && (CoreReflections.method$FluidState$getType.invoke(fluidState) == MFluids.instance$Fluids$WATER || FastNMS.INSTANCE.method$BlockState$getBlock(belowState) == MBlocks.ICE)) {
if (this.onWater && (FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER || FastNMS.INSTANCE.method$BlockState$getBlock(belowState) == MBlocks.ICE)) {
return true;
}
if (this.onLava && CoreReflections.method$FluidState$getType.invoke(fluidState) == MFluids.instance$Fluids$LAVA) {
if (this.onLava && FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.LAVA) {
return true;
}
return false;

View File

@@ -0,0 +1,238 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import io.papermc.paper.event.entity.EntityInsideBlockEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.EventUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
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.properties.Property;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.PressurePlateSensitivity;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.*;
import org.bukkit.GameEvent;
import org.bukkit.util.Vector;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class PressurePlateBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<Boolean> poweredProperty;
private final SoundData onSound;
private final SoundData offSound;
private final PressurePlateSensitivity pressurePlateSensitivity;
public PressurePlateBlockBehavior(
CustomBlock block,
Property<Boolean> poweredProperty,
SoundData onSound,
SoundData offSound,
PressurePlateSensitivity pressurePlateSensitivity
) {
super(block);
this.poweredProperty = poweredProperty;
this.onSound = onSound;
this.offSound = offSound;
this.pressurePlateSensitivity = pressurePlateSensitivity;
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object state = args[0];
Object level;
Object blockPos;
if (VersionHelper.isOrAbove1_21_2()) {
level = args[1];
blockPos = args[3];
} else {
level = args[3];
blockPos = args[4];
}
Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]);
if (direction == Direction.DOWN && !FastNMS.INSTANCE.method$BlockStateBase$canSurvive(state, level, blockPos)) {
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position);
int stateId = BlockStateUtils.blockStateToId(state);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState == null || immutableBlockState.isEmpty()) return MBlocks.AIR$defaultState;
for (Item<Object> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(position, item);
}
world.playBlockSound(position, immutableBlockState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
return MBlocks.AIR$defaultState;
}
return state;
}
@Override
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object blockPos = LocationUtils.below(args[2]);
Object level = args[1];
return FastNMS.INSTANCE.method$Block$canSupportRigidBlock(level, blockPos)
|| FastNMS.INSTANCE.method$Block$canSupportCenter(level, blockPos, CoreReflections.instance$Direction$UP);
}
@Override
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object state = args[0];
int signalForState = this.getSignalForState(state);
if (signalForState > 0) {
this.checkPressed(null, args[1], args[2], state, signalForState, thisBlock);
}
}
@Override
@SuppressWarnings("UnstableApiUsage")
public void entityInside(Object thisBlock, Object[] args, Callable<Object> superMethod) {
EntityInsideBlockEvent event = new EntityInsideBlockEvent(FastNMS.INSTANCE.method$Entity$getBukkitEntity(args[3]), FastNMS.INSTANCE.method$CraftBlock$at(args[1], args[2]));
if (EventUtils.fireAndCheckCancel(event)) {
return;
}
Object state = args[0];
int signalForState = this.getSignalForState(state);
if (signalForState == 0) {
this.checkPressed(args[3], args[1], args[2], state, signalForState, thisBlock);
} else {
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(args[1], args[2], thisBlock, 20);
}
}
protected int getSignalStrength(Object level, Object pos) {
Class<?> clazz = switch (this.pressurePlateSensitivity) {
case EVERYTHING -> CoreReflections.clazz$Entity;
case MOBS -> CoreReflections.clazz$LivingEntity;
};
Object box = FastNMS.INSTANCE.method$AABB$move(CoreReflections.instance$BasePressurePlateBlock$TOUCH_AABB, pos);
return FastNMS.INSTANCE.method$BasePressurePlateBlock$getEntityCount(level, box, clazz) > 0 ? 15 : 0;
}
private Object setSignalForState(Object state, int strength) {
ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (blockState == null || blockState.isEmpty()) return state;
return blockState.with(this.poweredProperty, strength > 0).customBlockState().handle();
}
private void checkPressed(@Nullable Object entity, Object level, Object pos, Object state, int currentSignal, Object thisBlock) {
int signalStrength = this.getSignalStrength(level, pos);
boolean wasActive = currentSignal > 0;
boolean isActive = signalStrength > 0;
if (currentSignal != signalStrength) {
Object blockState = this.setSignalForState(state, signalStrength);
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, blockState, 2);
this.updateNeighbours(level, pos, thisBlock);
FastNMS.INSTANCE.method$Level$setBlocksDirty(level, pos, state, blockState);
}
org.bukkit.World craftWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
int y = FastNMS.INSTANCE.field$Vec3i$y(pos);
int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
Vector positionVector = new Vector(x, y, z);
if (!isActive && wasActive) {
handleDeactivation(entity, craftWorld, pos, positionVector);
} else if (isActive && !wasActive) {
handleActivation(entity, craftWorld, pos, positionVector);
}
if (isActive) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(level, pos, thisBlock, 20);
}
}
private void handleDeactivation(Object entity, org.bukkit.World craftWorld, Object pos, Vector positionVector) {
World world = BukkitWorldManager.instance().getWorld(craftWorld).world();
world.playBlockSound(LocationUtils.toVec3d(LocationUtils.fromBlockPos(pos)), this.offSound);
craftWorld.sendGameEvent(
entity != null ? FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity) : null,
GameEvent.BLOCK_DEACTIVATE,
positionVector
);
}
private void handleActivation(Object entity, org.bukkit.World craftWorld, Object pos, Vector positionVector) {
World world = BukkitWorldManager.instance().getWorld(craftWorld).world();
world.playBlockSound(LocationUtils.toVec3d(LocationUtils.fromBlockPos(pos)), this.onSound);
craftWorld.sendGameEvent(
entity != null ? FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity) : null,
GameEvent.BLOCK_ACTIVATE,
positionVector
);
}
@Override
public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable<Object> superMethod) {
boolean movedByPiston = (boolean) args[3];
if (!movedByPiston && this.getSignalForState(args[0]) > 0) {
this.updateNeighbours(args[1], args[2], thisBlock);
}
}
private void updateNeighbours(Object level, Object pos, Object thisBlock) {
FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, pos, thisBlock);
FastNMS.INSTANCE.method$Level$updateNeighborsAt(level, LocationUtils.below(pos), thisBlock);
}
@Override
public int getSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
return this.getSignalForState(args[0]);
}
private int getSignalForState(Object state) {
ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (blockState == null || blockState.isEmpty()) return 0;
return blockState.get(this.poweredProperty) ? 15 : 0;
}
@Override
public int getDirectSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Direction direction = DirectionUtils.fromNMSDirection(args[3]);
return direction == Direction.UP ? this.getSignalForState(args[0]) : 0;
}
@Override
public boolean isSignalSource(Object thisBlock, Object[] args, Callable<Object> superMethod) {
return true;
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<Boolean> powered = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.pressure_plate.missing_powered");
PressurePlateSensitivity pressurePlateSensitivity = PressurePlateSensitivity.byName(arguments.getOrDefault("sensitivity", "everything").toString());
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
SoundData onSound = null;
SoundData offSound = null;
if (sounds != null) {
onSound = Optional.ofNullable(sounds.get("on")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
offSound = Optional.ofNullable(sounds.get("off")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
}
return new PressurePlateBlockBehavior(block, powered, onSound, offSound, pressurePlateSensitivity);
}
}
}

View File

@@ -87,7 +87,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
}
Object chunkGenerator = CoreReflections.method$ServerChunkCache$getGenerator.invoke(FastNMS.INSTANCE.method$ServerLevel$getChunkSource(world));
Object configuredFeature = CoreReflections.method$Holder$value.invoke(holder.get());
Object fluidState = CoreReflections.method$Level$getFluidState.invoke(world, blockPos);
Object fluidState = FastNMS.INSTANCE.method$Level$getFluidState(world, blockPos);
Object legacyState = CoreReflections.method$FluidState$createLegacyBlock.invoke(fluidState);
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, legacyState, UpdateOption.UPDATE_NONE.flags());
if ((boolean) CoreReflections.method$ConfiguredFeature$place.invoke(configuredFeature, world, chunkGenerator, randomSource, blockPos)) {

View File

@@ -0,0 +1,130 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
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.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.SlabType;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.behavior.BlockBoundItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class SlabBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<SlabType> typeProperty;
public SlabBlockBehavior(CustomBlock block, Property<SlabType> typeProperty) {
super(block);
this.typeProperty = typeProperty;
}
@SuppressWarnings("unchecked")
@Override
public boolean canBeReplaced(BlockPlaceContext context, ImmutableBlockState state) {
SlabType type = state.get(this.typeProperty);
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
if (type == SlabType.DOUBLE || item == null) return false;
Optional<CustomItem<ItemStack>> itemInHand = item.getCustomItem();
if (itemInHand.isEmpty()) return false;
CustomItem<ItemStack> customItem = itemInHand.get();
Key blockId = null;
for (ItemBehavior itemBehavior : customItem.behaviors()) {
if (itemBehavior instanceof BlockBoundItemBehavior behavior) {
blockId = behavior.block();
}
}
if (blockId == null || !blockId.equals(super.customBlock.id())) return false;
if (!context.replacingClickedOnBlock()) return true;
boolean upper = context.getClickLocation().y - (double) context.getClickedPos().y() > (double) 0.5F;
Direction clickedFace = context.getClickedFace();
return type == SlabType.BOTTOM ?
clickedFace == Direction.UP || (upper && clickedFace.axis().isHorizontal()) :
clickedFace == Direction.DOWN || (!upper && clickedFace.axis().isHorizontal());
}
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
BlockPos clickedPos = context.getClickedPos();
ImmutableBlockState blockState = context.getLevel().getBlockAt(clickedPos).customBlockState();
if (blockState != null && blockState.owner().value() == super.customBlock) {
if (super.waterloggedProperty != null)
blockState = blockState.with(super.waterloggedProperty, false);
return blockState.with(this.typeProperty, SlabType.DOUBLE);
} else {
Object fluidState = FastNMS.INSTANCE.method$Level$getFluidState(context.getLevel().serverWorld(), LocationUtils.toBlockPos(clickedPos));
if (super.waterloggedProperty != null)
state = state.with(super.waterloggedProperty, FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER);
Direction clickedFace = context.getClickedFace();
return clickedFace == Direction.DOWN || clickedFace != Direction.UP && context.getClickLocation().y - (double) clickedPos.y() > (double) 0.5F ? state.with(this.typeProperty, SlabType.TOP) : state.with(this.typeProperty, SlabType.BOTTOM);
}
}
@Override
public boolean placeLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object blockState = args[2];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) return false;
return immutableBlockState.get(this.typeProperty) != SlabType.DOUBLE && super.placeLiquid(thisBlock, args, superMethod);
}
@Override
public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object blockState = VersionHelper.isOrAbove1_20_2() ? args[3] : args[2];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) return false;
return immutableBlockState.get(this.typeProperty) != SlabType.DOUBLE && super.canPlaceLiquid(thisBlock, args, superMethod);
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object blockState = args[0];
if (super.waterloggedProperty == null) return blockState;
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) return blockState;
if (immutableBlockState.get(super.waterloggedProperty)) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleFluidTick(VersionHelper.isOrAbove1_21_2() ? args[2] : args[3], VersionHelper.isOrAbove1_21_2() ? args[3] : args[4], MFluids.WATER, 5);
}
return blockState;
}
@Override
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object type = VersionHelper.isOrAbove1_20_5() ? args[1] : args[3];
Object blockState = args[0];
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state == null || state.isEmpty()) return false;
if (type == CoreReflections.instance$PathComputationType$WATER) {
return super.waterloggedProperty != null && state.get(this.typeProperty) != SlabType.DOUBLE && state.get(super.waterloggedProperty);
}
return false;
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<SlabType> type = (Property<SlabType>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("type"), "warning.config.block.behavior.slab.missing_type");
return new SlabBlockBehavior(block, type);
}
}
}

View File

@@ -0,0 +1,99 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
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.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class StackableBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final IntegerProperty amountProperty;
private final List<Key> items;
private final SoundData stackSound;
public StackableBlockBehavior(CustomBlock block, IntegerProperty amountProperty, List<Key> items, SoundData stackSound) {
super(block);
this.amountProperty = amountProperty;
this.items = items;
this.stackSound = stackSound;
}
@Override
@SuppressWarnings("unchecked")
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
Player player = context.getPlayer();
if (player.isSecondaryUseActive()) {
return InteractionResult.PASS;
}
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
if (item == null) {
return InteractionResult.PASS;
}
if (!this.items.contains(item.id())) {
return InteractionResult.PASS;
}
BlockPos pos = context.getClickedPos();
World world = context.getLevel();
if (state.get(this.amountProperty) >= this.amountProperty.max) {
return InteractionResult.SUCCESS_AND_CANCEL;
}
updateStackableBlock(state, pos, world, item, player, context.getHand());
return InteractionResult.SUCCESS_AND_CANCEL;
}
private void updateStackableBlock(ImmutableBlockState state, BlockPos pos, World world, Item<ItemStack> item, Player player, InteractionHand hand) {
ImmutableBlockState nextStage = state.cycle(this.amountProperty);
Location location = new Location((org.bukkit.World) world.platformWorld(), pos.x(), pos.y(), pos.z());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
if (this.stackSound != null) {
world.playBlockSound(new Vec3d(location.getX(), location.getY(), location.getZ()), this.stackSound);
}
if (!player.isCreativeMode()) {
item.count(item.count() - 1);
}
player.swingHand(hand);
}
public static class Factory implements BlockBehaviorFactory {
@Override
@SuppressWarnings("unchecked")
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
String propertyName = String.valueOf(arguments.getOrDefault("property", "amount"));
IntegerProperty amount = (IntegerProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty(propertyName), () -> {
throw new LocalizedResourceConfigException("warning.config.block.behavior.stackable.missing_property", propertyName);
});
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
SoundData stackSound = null;
if (sounds != null) {
stackSound = Optional.ofNullable(sounds.get("stack")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1)).orElse(null);
}
Object itemsObj = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("items"), "warning.config.block.behavior.stackable.missing_items");
List<Key> items = MiscUtils.getAsStringList(itemsObj).stream().map(Key::of).toList();
return new StackableBlockBehavior(block, amount, items, stackSound);
}
}
}

View File

@@ -0,0 +1,191 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
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.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.SingleBlockHalf;
import net.momirealms.craftengine.core.block.state.properties.StairsShape;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class StairsBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<HorizontalDirection> facingProperty;
private final Property<SingleBlockHalf> halfProperty;
private final Property<StairsShape> shapeProperty;
public StairsBlockBehavior(CustomBlock block, Property<HorizontalDirection> facing, Property<SingleBlockHalf> half, Property<StairsShape> shape) {
super(block);
this.facingProperty = facing;
this.halfProperty = half;
this.shapeProperty = shape;
}
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
Direction clickedFace = context.getClickedFace();
BlockPos clickedPos = context.getClickedPos();
ImmutableBlockState blockState = state.owner().value().defaultState()
.with(this.facingProperty, context.getHorizontalDirection().toHorizontalDirection())
.with(this.halfProperty, clickedFace != Direction.DOWN && (clickedFace == Direction.UP || !(context.getClickLocation().y - clickedPos.y() > 0.5)) ? SingleBlockHalf.BOTTOM : SingleBlockHalf.TOP);
if (super.waterloggedProperty != null) {
Object fluidState = FastNMS.INSTANCE.method$Level$getFluidState(context.getLevel().serverWorld(), LocationUtils.toBlockPos(clickedPos));
blockState = blockState.with(this.waterloggedProperty, FastNMS.INSTANCE.method$FluidState$getType(fluidState) == MFluids.WATER);
}
return blockState.with(this.shapeProperty, getStairsShape(blockState, context.getLevel().serverWorld(), clickedPos));
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object level;
Object blockPos;
Object blockState = args[0];
if (VersionHelper.isOrAbove1_21_2()) {
level = args[1];
blockPos = args[3];
} else {
level = args[3];
blockPos = args[4];
}
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState == null || immutableBlockState.isEmpty()) return blockState;
if (super.waterloggedProperty != null && immutableBlockState.get(this.waterloggedProperty)) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleFluidTick(VersionHelper.isOrAbove1_21_2() ? args[2] : args[3], VersionHelper.isOrAbove1_21_2() ? args[3] : args[4], MFluids.WATER, 5);
}
Direction direction = DirectionUtils.fromNMSDirection(VersionHelper.isOrAbove1_21_2() ? args[4] : args[1]);
StairsShape stairsShape = getStairsShape(immutableBlockState, level, LocationUtils.fromBlockPos(blockPos));
return direction.axis().isHorizontal()
? immutableBlockState.with(this.shapeProperty, stairsShape).customBlockState().handle()
: superMethod.call();
}
private StairsShape getStairsShape(ImmutableBlockState state, Object level, BlockPos pos) {
Direction direction = state.get(this.facingProperty).toDirection();
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(pos.relative(direction)));
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (immutableBlockState != null && !immutableBlockState.isEmpty()) {
if (isStairs(blockState) && state.get(this.halfProperty) == immutableBlockState.get(this.halfProperty)) {
Direction direction1 = immutableBlockState.get(this.facingProperty).toDirection();
if (direction1.axis() != state.get(this.facingProperty).toDirection().axis() && canTakeShape(state, level, pos, direction1.opposite())) {
if (direction1 == direction.counterClockWise()) {
return StairsShape.OUTER_LEFT;
}
return StairsShape.OUTER_RIGHT;
}
}
} else if (isStairs(blockState)) {
// 处理可能是原版楼梯
// try {
// Object nmsHalf = CoreReflections.method$StateHolder$getValue.invoke(blockState, CoreReflections.instance$StairBlock$HALF);
// SingleBlockHalf half = SingleBlockHalf.valueOf(nmsHalf.toString().toUpperCase(Locale.ROOT));
// if (state.get(this.halfProperty).equals(half)) {
// Object nmsFacing = CoreReflections.method$StateHolder$getValue.invoke(blockState, CoreReflections.instance$StairBlock$FACING);
// Direction direction1 = DirectionUtils.fromNMSDirection(nmsFacing);
// if (direction1.axis() != state.get(this.facingProperty).toDirection().axis() && canTakeShape(state, level, pos, direction1.opposite())) {
// if (direction1 == direction.counterClockWise()) {
// return StairsShape.OUTER_LEFT;
// }
// return StairsShape.OUTER_RIGHT;
// }
// }
// } catch (Exception e) {
// CraftEngine.instance().logger().warn("Failed to get facing from blockState", e);
// }
}
Object blockState1 = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(pos.relative(direction.opposite())));
int stateId1 = BlockStateUtils.blockStateToId(blockState1);
ImmutableBlockState immutableBlockState1 = BukkitBlockManager.instance().getImmutableBlockState(stateId1);
if (immutableBlockState1 != null && !immutableBlockState1.isEmpty()) {
if (isStairs(blockState1) && state.get(this.halfProperty) == immutableBlockState1.get(this.halfProperty)) {
Direction direction2 = immutableBlockState1.get(this.facingProperty).toDirection();
if (direction2.axis() != state.get(this.facingProperty).toDirection().axis() && canTakeShape(state, level, pos, direction2)) {
if (direction2 == direction.counterClockWise()) {
return StairsShape.INNER_LEFT;
}
return StairsShape.INNER_RIGHT;
}
}
} else if (isStairs(blockState1)) {
// 处理可能是原版楼梯
// try {
// Object nmsHalf = CoreReflections.method$StateHolder$getValue.invoke(blockState1, CoreReflections.instance$StairBlock$HALF);
// SingleBlockHalf half = SingleBlockHalf.valueOf(nmsHalf.toString().toUpperCase(Locale.ROOT));
// if (state.get(this.halfProperty).equals(half)) {
// Object nmsFacing = CoreReflections.method$StateHolder$getValue.invoke(blockState1, CoreReflections.instance$StairBlock$FACING);
// Direction direction1 = DirectionUtils.fromNMSDirection(nmsFacing);
// if (direction1.axis() != state.get(this.facingProperty).toDirection().axis() && canTakeShape(state, level, pos, direction1)) {
// if (direction1 == direction.counterClockWise()) {
// return StairsShape.INNER_LEFT;
// }
// return StairsShape.INNER_RIGHT;
// }
// }
// } catch (Exception e) {
// CraftEngine.instance().logger().warn("Failed to get facing from blockState", e);
// }
}
return StairsShape.STRAIGHT;
}
private boolean isStairs(Object state) {
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (immutableBlockState == null || immutableBlockState.isEmpty()) {
return FastNMS.INSTANCE.method$BlockState$getBlock(state).equals(CoreReflections.clazz$StairBlock);
}
Optional<StairsBlockBehavior> optionalBehavior = immutableBlockState.behavior().getAs(StairsBlockBehavior.class);
return optionalBehavior.isPresent();
}
private boolean canTakeShape(ImmutableBlockState state, Object level, BlockPos pos, Direction face) {
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.toBlockPos(pos.relative(face)));
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) {
// 处理可能是原版楼梯
// try {
// Object nmsFacing = CoreReflections.method$StateHolder$getValue.invoke(blockState, CoreReflections.instance$StairBlock$FACING);
// Direction direction = DirectionUtils.fromNMSDirection(nmsFacing);
// if (direction != state.get(this.facingProperty).toDirection()) return true;
// Object nmsHalf = CoreReflections.method$StateHolder$getValue.invoke(blockState, CoreReflections.instance$StairBlock$HALF);
// SingleBlockHalf half = SingleBlockHalf.valueOf(nmsHalf.toString().toUpperCase(Locale.ROOT));
// if (half != state.get(this.halfProperty)) return true;
// } catch (Exception e) {
// CraftEngine.instance().logger().warn("Failed to handle canTakeShape", e);
// }
return !isStairs(blockState);
}
return !isStairs(blockState) || immutableBlockState.get(this.facingProperty) != state.get(this.facingProperty) || immutableBlockState.get(this.halfProperty) != state.get(this.halfProperty);
}
public static class Factory implements BlockBehaviorFactory {
@Override
@SuppressWarnings("unchecked")
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.stairs.missing_facing");
Property<SingleBlockHalf> half = (Property<SingleBlockHalf>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.stairs.missing_half");
Property<StairsShape> shape = (Property<StairsShape>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("shape"), "warning.config.block.behavior.stairs.missing_shape");
return new StairsBlockBehavior(block, facing, half, shape);
}
}
}

View File

@@ -1,43 +1,15 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
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.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.sparrow.nbt.CompoundTag;
import org.bukkit.GameEvent;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.Block;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.Map;
import java.util.Optional;
public class StrippableBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private static final Key AXE_STRIP_SOUND = Key.of("minecraft:item.axe.strip");
private final Key stripped;
public StrippableBlockBehavior(CustomBlock block, Key stripped) {
@@ -49,84 +21,6 @@ public class StrippableBlockBehavior extends BukkitBlockBehavior {
return this.stripped;
}
@SuppressWarnings("unchecked")
@Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
if (item == null) {
return InteractionResult.PASS;
}
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
if (optionalCustomItem.isEmpty()) {
if (!item.is(ItemTags.AXES))
return InteractionResult.PASS;
} else {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
if (!customItem.settings().tags().contains(ItemTags.AXES) && !item.is(ItemTags.AXES))
return InteractionResult.PASS;
}
Player player = context.getPlayer();
// no adventure mode
if (player.isAdventureMode()) {
return InteractionResult.PASS;
}
Item<?> offHandItem = player.getItemInHand(InteractionHand.OFF_HAND);
// is using a shield
if (context.getHand() == InteractionHand.MAIN_HAND && offHandItem != null && offHandItem.vanillaId().equals(ItemKeys.SHIELD) && !player.isSecondaryUseActive()) {
return InteractionResult.PASS;
}
Optional<CustomBlock> optionalNewCustomBlock = BukkitBlockManager.instance().blockById(stripped());
if (optionalNewCustomBlock.isEmpty()) {
CraftEngine.instance().logger().warn("stripped block " + stripped() + " does not exist");
return InteractionResult.FAIL;
}
CustomBlock newCustomBlock = optionalNewCustomBlock.get();
CompoundTag compoundTag = state.propertiesNbt();
ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag);
org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
Block block = clicked.block();
// Call bukkit event
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, block, BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.FAIL;
}
BlockPos pos = context.getClickedPos();
context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1);
CraftEngineBlocks.place(block.getLocation(), newState, UpdateOption.UPDATE_ALL_IMMEDIATE, false);
block.getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z()));
Material material = MaterialUtils.getMaterial(item.vanillaId());
bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1);
// resend swing if it's not interactable on client side
if (!InteractUtils.isInteractable(
bukkitPlayer, BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()),
context.getHitResult(), item
) || player.isSecondaryUseActive()) {
player.swingHand(context.getHand());
}
// shrink item amount
if (VersionHelper.isOrAbove1_20_5()) {
Object itemStack = item.getLiteralObject();
Object serverPlayer = player.serverPlayer();
Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND;
try {
CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot);
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to hurt itemStack", e);
}
} else {
ItemStack itemStack = item.getItem();
itemStack.damage(1, bukkitPlayer);
}
return InteractionResult.SUCCESS;
}
public static class Factory implements BlockBehaviorFactory {
@Override

View File

@@ -0,0 +1,77 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
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.util.Direction;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class SturdyBaseBlockBehavior extends AbstractCanSurviveBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Direction direction;
private final boolean stackable;
private final boolean checkFull;
private final boolean checkRigid;
private final boolean checkCenter;
public SturdyBaseBlockBehavior(CustomBlock block, int delay, Direction direction, boolean stackable, boolean checkFull, boolean checkRigid, boolean checkCenter) {
super(block, delay);
this.direction = direction;
this.stackable = stackable;
this.checkFull = checkFull;
this.checkRigid = checkRigid;
this.checkCenter = checkCenter;
}
@Override
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception {
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos) + this.direction.stepX();
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos) + this.direction.stepY();
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos) + this.direction.stepZ();
Object targetPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z);
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, targetPos);
if (this.checkFull && (boolean) CoreReflections.method$BlockStateBase$isFaceSturdy.invoke(
blockState, world, targetPos, DirectionUtils.toNMSDirection(this.direction.opposite()),
CoreReflections.instance$SupportType$FULL
)) {
return true;
}
if (this.checkRigid && FastNMS.INSTANCE.method$Block$canSupportRigidBlock(world, targetPos)) {
return true;
}
if (this.checkCenter && FastNMS.INSTANCE.method$Block$canSupportCenter(world, targetPos, DirectionUtils.toNMSDirection(this.direction.opposite()))) {
return true;
}
if (!this.stackable) {
return false;
}
ImmutableBlockState targetCustomState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (targetCustomState == null || targetCustomState.isEmpty()) return false;
return targetCustomState.owner().value() == super.customBlock;
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay");
Direction direction = Direction.valueOf(arguments.getOrDefault("direction", "down").toString().toUpperCase(Locale.ENGLISH));
boolean stackable = (boolean) arguments.getOrDefault("stackable", false);
List<String> supportTypes = MiscUtils.getAsStringList(arguments.getOrDefault("support-types", List.of("full")));
return new SturdyBaseBlockBehavior(block, delay, direction, stackable, supportTypes.contains("full"), supportTypes.contains("rigid"), supportTypes.contains("center"));
}
}
}

View File

@@ -0,0 +1,249 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
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.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.InteractUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
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.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.SingleBlockHalf;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
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 org.bukkit.Bukkit;
import org.bukkit.GameEvent;
import org.bukkit.block.Block;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class TrapDoorBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<SingleBlockHalf> halfProperty;
private final Property<HorizontalDirection> facingProperty;
private final Property<Boolean> poweredProperty;
private final Property<Boolean> openProperty;
private final boolean canOpenWithHand;
private final boolean canOpenByWindCharge;
private final SoundData openSound;
private final SoundData closeSound;
public TrapDoorBlockBehavior(CustomBlock block,
Property<SingleBlockHalf> halfProperty,
Property<HorizontalDirection> facingProperty,
Property<Boolean> poweredProperty,
Property<Boolean> openProperty,
boolean canOpenWithHand,
boolean canOpenByWindCharge,
SoundData openSound,
SoundData closeSound) {
super(block);
this.halfProperty = halfProperty;
this.facingProperty = facingProperty;
this.poweredProperty = poweredProperty;
this.openProperty = openProperty;
this.canOpenWithHand = canOpenWithHand;
this.canOpenByWindCharge = canOpenByWindCharge;
this.openSound = openSound;
this.closeSound = closeSound;
}
@Override
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object blockState = args[0];
if (super.waterloggedProperty != null) {
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state != null && !state.isEmpty() && state.get(super.waterloggedProperty)) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleFluidTick(VersionHelper.isOrAbove1_21_2() ? args[2] : args[3], VersionHelper.isOrAbove1_21_2() ? args[3] : args[4], MFluids.WATER, 5);
}
}
return blockState;
}
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
Object level = context.getLevel().serverWorld();
Object clickedPos = LocationUtils.toBlockPos(context.getClickedPos());
Direction clickedFace = context.getClickedFace();
if (!context.replacingClickedOnBlock() && clickedFace.axis().isHorizontal()) {
state = state.with(this.facingProperty, clickedFace.toHorizontalDirection())
.with(this.halfProperty, context.getClickLocation().y - context.getClickedPos().y() > 0.5 ? SingleBlockHalf.TOP : SingleBlockHalf.BOTTOM);
} else {
state = state.with(this.facingProperty, context.getHorizontalDirection().opposite().toHorizontalDirection())
.with(this.halfProperty, clickedFace == Direction.UP ? SingleBlockHalf.BOTTOM : SingleBlockHalf.TOP);
}
if (FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, clickedPos)) {
state = state.with(this.poweredProperty, true).with(this.openProperty, true);
}
if (this.waterloggedProperty != null && FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.method$Level$getFluidState(level, clickedPos)) == MFluids.WATER) {
state = state.with(this.waterloggedProperty, true);
}
return state;
}
@Override
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
if (!this.canOpenWithHand) {
return InteractionResult.PASS;
}
playerToggle(context, state);
return InteractionResult.SUCCESS_AND_CANCEL;
}
@SuppressWarnings("unchecked")
private void playerToggle(UseOnContext context, ImmutableBlockState state) {
Player player = context.getPlayer();
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
player.swingHand(context.getHand());
}
}
@Override
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object type = VersionHelper.isOrAbove1_20_5() ? args[1] : args[3];
Object blockState = args[0];
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state == null || state.isEmpty()) return false;
if (type == CoreReflections.instance$PathComputationType$LAND || type == CoreReflections.instance$PathComputationType$AIR) {
return state.get(this.openProperty);
} else if (type == CoreReflections.instance$PathComputationType$WATER) {
return state.get(super.waterloggedProperty);
}
return false;
}
@Override
public void onExplosionHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.canOpenByWindCharge && FastNMS.INSTANCE.method$Explosion$canTriggerBlocks(args[3])) {
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(args[0]));
if (state == null || state.isEmpty()) return;
this.toggle(state, new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1])), LocationUtils.fromBlockPos(args[2]), null);
}
}
@SuppressWarnings("UnstableApiUsage")
@Override
public void neighborChanged(Object thisBlock, Object[] args, Callable<Object> superMethod) {
Object blockState = args[0];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
Object level = args[1];
Object blockPos = args[2];
boolean hasSignal = FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, blockPos);
if (hasSignal == immutableBlockState.get(this.poweredProperty)) return;
Block bblock = FastNMS.INSTANCE.method$CraftBlock$at(level, blockPos);
int power = bblock.getBlockPower();
int oldPower = immutableBlockState.get(this.openProperty) ? 15 : 0;
Object neighborBlock = args[3];
if (oldPower == 0 ^ power == 0 || FastNMS.INSTANCE.method$BlockStateBase$isSignalSource(FastNMS.INSTANCE.method$Block$defaultState(neighborBlock))) {
BlockRedstoneEvent event = new BlockRedstoneEvent(bblock, oldPower, power);
Bukkit.getPluginManager().callEvent(event);
hasSignal = event.getNewCurrent() > 0;
}
World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
boolean changed = immutableBlockState.get(this.openProperty) != hasSignal;
if (hasSignal && changed) {
Object abovePos = LocationUtils.above(blockPos);
Object aboveBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, abovePos);
if (CoreReflections.clazz$RedStoneWireBlock.isInstance(FastNMS.INSTANCE.method$BlockState$getBlock(aboveBlockState))) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, abovePos, MBlocks.AIR$defaultState, UpdateOption.UPDATE_ALL.flags());
world.dropItemNaturally(
new Vec3d(FastNMS.INSTANCE.field$Vec3i$x(abovePos) + 0.5, FastNMS.INSTANCE.field$Vec3i$y(abovePos) + 0.5, FastNMS.INSTANCE.field$Vec3i$z(abovePos) + 0.5),
BukkitItemManager.instance().createWrappedItem(ItemKeys.REDSTONE, null)
);
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos) != blockPos) {
return;
}
}
}
if (changed) {
immutableBlockState = immutableBlockState.with(this.openProperty, hasSignal);
FastNMS.INSTANCE.method$Level$getCraftWorld(level).sendGameEvent(null,
hasSignal ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE,
new Vector(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos), FastNMS.INSTANCE.field$Vec3i$z(blockPos))
);
this.playSound(LocationUtils.fromBlockPos(blockPos), world, hasSignal);
}
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, immutableBlockState.with(this.poweredProperty, hasSignal).customBlockState().handle(), UpdateOption.Flags.UPDATE_CLIENTS);
if (this.waterloggedProperty != null && immutableBlockState.get(this.waterloggedProperty)) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleFluidTick(level, blockPos, MFluids.WATER, 5);
}
}
private void toggle(ImmutableBlockState state, World world, BlockPos pos, @Nullable Player player) {
ImmutableBlockState newState = state.cycle(this.openProperty);
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
boolean open = newState.get(this.openProperty);
((org.bukkit.World) world.platformWorld()).sendGameEvent(
player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null,
open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE,
new Vector(pos.x(), pos.y(), pos.z())
);
this.playSound(pos, world, open);
}
private void playSound(BlockPos pos, World world, boolean open) {
if (open) {
if (this.openSound != null) {
world.playBlockSound(new Vec3d(pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5), this.openSound);
}
} else {
if (this.closeSound != null) {
world.playBlockSound(new Vec3d(pos.x() + 0.5, pos.y() + 0.5, pos.z() + 0.5), this.closeSound);
}
}
}
@SuppressWarnings("unchecked")
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<SingleBlockHalf> half = (Property<SingleBlockHalf>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("half"), "warning.config.block.behavior.trapdoor.missing_half");
Property<HorizontalDirection> facing = (Property<HorizontalDirection>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("facing"), "warning.config.block.behavior.trapdoor.missing_facing");
Property<Boolean> open = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("open"), "warning.config.block.behavior.trapdoor.missing_open");
Property<Boolean> powered = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("powered"), "warning.config.block.behavior.trapdoor.missing_powered");
boolean canOpenWithHand = (boolean) arguments.getOrDefault("can-open-with-hand", true);
boolean canOpenByWindCharge = (boolean) arguments.getOrDefault("can-open-by-wind-charge", true);
Map<String, Object> sounds = (Map<String, Object>) arguments.get("sounds");
SoundData openSound = null;
SoundData closeSound = null;
if (sounds != null) {
openSound = Optional.ofNullable(sounds.get("open")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
closeSound = Optional.ofNullable(sounds.get("close")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
}
return new TrapDoorBlockBehavior(block, half, facing, powered, open, canOpenWithHand, canOpenByWindCharge, openSound, closeSound);
}
}
}

View File

@@ -1,8 +1,9 @@
package net.momirealms.craftengine.core.block.behavior;
package net.momirealms.craftengine.bukkit.block.behavior;
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.AbstractBlockBehavior;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.item.context.UseOnContext;
@@ -11,7 +12,7 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
public class UnsafeCompositeBlockBehavior extends AbstractBlockBehavior {
public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior {
private final AbstractBlockBehavior[] behaviors;
public UnsafeCompositeBlockBehavior(CustomBlock customBlock, List<AbstractBlockBehavior> behaviors) {
@@ -34,17 +35,29 @@ public class UnsafeCompositeBlockBehavior extends AbstractBlockBehavior {
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
for (AbstractBlockBehavior behavior : this.behaviors) {
InteractionResult result = behavior.useOnBlock(context, state);
if (result != InteractionResult.PASS) {
if (result != InteractionResult.PASS && result != InteractionResult.TRY_EMPTY_HAND) {
return result;
}
}
return super.useOnBlock(context, state);
}
@Override
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
for (AbstractBlockBehavior behavior : this.behaviors) {
InteractionResult result = behavior.useWithoutItem(context, state);
if (result != InteractionResult.PASS) {
return result;
}
}
return super.useWithoutItem(context, state);
}
@Override
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
for (AbstractBlockBehavior behavior : this.behaviors) {
state = behavior.updateStateForPlacement(context, state);
if (state == null) return null;
}
return state;
}
@@ -163,4 +176,84 @@ public class UnsafeCompositeBlockBehavior extends AbstractBlockBehavior {
}
return true;
}
@Override
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
if (!behavior.isPathFindable(thisBlock, args, superMethod)) {
return false;
}
}
return (boolean) superMethod.call();
}
@Override
public void onExplosionHit(Object thisBlock, Object[] args, Callable<Object> superMethod) {
for (AbstractBlockBehavior behavior : this.behaviors) {
behavior.onExplosionHit(thisBlock, args, superMethod);
}
}
@Override
public void setPlacedBy(BlockPlaceContext context, ImmutableBlockState state) {
for (AbstractBlockBehavior behavior : this.behaviors) {
behavior.setPlacedBy(context, state);
}
}
@Override
public boolean canBeReplaced(BlockPlaceContext context, ImmutableBlockState state) {
for (AbstractBlockBehavior behavior : this.behaviors) {
if (!behavior.canBeReplaced(context, state)) {
return false;
}
}
return super.canBeReplaced(context, state);
}
@Override
public void entityInside(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
behavior.entityInside(thisBlock, args, superMethod);
}
}
@Override
public void affectNeighborsAfterRemoval(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
for (AbstractBlockBehavior behavior : this.behaviors) {
behavior.affectNeighborsAfterRemoval(thisBlock, args, superMethod);
}
}
@Override
public int getSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
for (AbstractBlockBehavior behavior : this.behaviors) {
int signal = behavior.getSignal(thisBlock, args, superMethod);
if (signal != 0) {
return signal;
}
}
return 0;
}
@Override
public int getDirectSignal(Object thisBlock, Object[] args, Callable<Object> superMethod) {
for (AbstractBlockBehavior behavior : this.behaviors) {
int signal = behavior.getDirectSignal(thisBlock, args, superMethod);
if (signal != 0) {
return signal;
}
}
return 0;
}
@Override
public boolean isSignalSource(Object thisBlock, Object[] args, Callable<Object> superMethod) {
for (AbstractBlockBehavior behavior : this.behaviors) {
if (behavior.isSignalSource(thisBlock, args, superMethod)) {
return true;
}
}
return false;
}
}

View File

@@ -1,88 +0,0 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class WaterLoggedBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
@Nullable
private final Property<Boolean> waterloggedProperty;
public WaterLoggedBlockBehavior(CustomBlock block, @Nullable Property<Boolean> waterloggedProperty) {
super(block);
this.waterloggedProperty = waterloggedProperty;
}
// TODO create real waterlogged blocks, needs to have real waterlogged property
// @Override
// public Object pickupBlock(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// if (this.waterloggedProperty == null) return Reflections.instance$ItemStack$EMPTY;
// Object blockState;
// Object world;
// Object pos;
// if (VersionHelper.isVersionNewerThan1_20_2()) {
// world = args[1];
// pos = args[2];
// blockState = args[3];
// } else {
// world = args[0];
// pos = args[1];
// blockState = args[2];
// }
// ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
// if (immutableBlockState != null) {
// if (immutableBlockState.get(this.waterloggedProperty)) {
// Reflections.method$LevelWriter$setBlock.invoke(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().handle(), 3);
// // TODO check can survive
// Object itemStack = Reflections.constructor$ItemStack.newInstance(Reflections.instance$Items$WATER_BUCKET);
// return itemStack;
// }
// }
// return Reflections.instance$ItemStack$EMPTY;
// }
//
// @Override
// public boolean placeLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// if (this.waterloggedProperty == null) return false;
// Object blockState = args[2];
// ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
// if (immutableBlockState != null) {
// Object fluidType = Reflections.method$FluidState$getType.invoke(args[3]);
// if (!immutableBlockState.get(this.waterloggedProperty) && fluidType == Reflections.instance$Fluids$WATER) {
// Reflections.method$LevelWriter$setBlock.invoke(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().handle(), 3);
// Reflections.method$LevelAccessor$scheduleTick.invoke(args[0], fluidType, Reflections.method$Fluid$getTickDelay.invoke(fluidType, args[0]));
// return true;
// }
// }
// return false;
// }
//
// // use water
// @Override
// public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// return super.canPlaceLiquid(thisBlock, args, superMethod);
// }
//
// @Override
// public Object getFluidState(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// Object blockState = args[0];
// ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
// if (state == null || state.isEmpty() || this.waterloggedProperty == null) return super.getFluidState(thisBlock, args, superMethod);
// boolean waterlogged = state.get(this.waterloggedProperty);
// return waterlogged ? Reflections.method$FlowingFluid$getSource.invoke(Reflections.instance$Fluids$WATER, false) : super.getFluidState(thisBlock, args, superMethod);
// }
@SuppressWarnings("unchecked")
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<Boolean> waterlogged = (Property<Boolean>) block.getProperty("waterlogged");
return new WaterLoggedBlockBehavior(block, waterlogged);
}
}
}

View File

@@ -0,0 +1,9 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class AgeableMobData<T> extends PathfinderMobData<T> {
public static final MobData<Boolean> Baby = new AgeableMobData<>(AgeableMobData.class, EntityDataValue.Serializers$BOOLEAN, false);
public AgeableMobData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class AnimalData<T> extends AgeableMobData<T> {
public AnimalData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -5,16 +5,16 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import java.util.Optional;
public class BaseEntityData<T> extends SimpleEntityData<T> {
public static final BaseEntityData<Byte> SharedFlags = new BaseEntityData<>(0, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final BaseEntityData<Integer> AirSupply = new BaseEntityData<>(1, EntityDataValue.Serializers$INT, 300);
public static final BaseEntityData<Optional<Object>> CustomName = new BaseEntityData<>(2, EntityDataValue.Serializers$OPTIONAL_COMPONENT, Optional.empty());
public static final BaseEntityData<Boolean> CustomNameVisible = new BaseEntityData<>(3, EntityDataValue.Serializers$BOOLEAN, false);
public static final BaseEntityData<Boolean> Silent = new BaseEntityData<>(4, EntityDataValue.Serializers$BOOLEAN, false);
public static final BaseEntityData<Boolean> NoGravity = new BaseEntityData<>(5, EntityDataValue.Serializers$BOOLEAN, false);
public static final BaseEntityData<Object> Pose = new BaseEntityData<>(6, EntityDataValue.Serializers$POSE, CoreReflections.instance$Pose$STANDING);
public static final BaseEntityData<Integer> TicksFrozen = new BaseEntityData<>(7, EntityDataValue.Serializers$INT, 0);
public static final BaseEntityData<Byte> SharedFlags = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final BaseEntityData<Integer> AirSupply = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$INT, 300);
public static final BaseEntityData<Optional<Object>> CustomName = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$OPTIONAL_COMPONENT, Optional.empty());
public static final BaseEntityData<Boolean> CustomNameVisible = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$BOOLEAN, false);
public static final BaseEntityData<Boolean> Silent = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$BOOLEAN, false);
public static final BaseEntityData<Boolean> NoGravity = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$BOOLEAN, false);
public static final BaseEntityData<Object> Pose = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$POSE, CoreReflections.instance$Pose$STANDING);
public static final BaseEntityData<Integer> TicksFrozen = new BaseEntityData<>(BaseEntityData.class, EntityDataValue.Serializers$INT, 0);
public BaseEntityData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public BaseEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -4,9 +4,9 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
public class BlockDisplayEntityData<T> extends DisplayEntityData<T> {
// Block display only
public static final DisplayEntityData<Object> DisplayedBlock = of(23, EntityDataValue.Serializers$BLOCK_STATE, MBlocks.AIR$defaultState);
public static final DisplayEntityData<Object> DisplayedBlock = new BlockDisplayEntityData<>(BlockDisplayEntityData.class, EntityDataValue.Serializers$BLOCK_STATE, MBlocks.AIR$defaultState);
public BlockDisplayEntityData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public BlockDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -6,39 +6,40 @@ import org.joml.Vector3f;
public class DisplayEntityData<T> extends BaseEntityData<T> {
// Display only
public static final DisplayEntityData<Integer> InterpolationDelay = of(8, EntityDataValue.Serializers$INT, 0);
public static final DisplayEntityData<Integer> InterpolationDelay = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, true);
// 1.19.4-1.20.1
public static final DisplayEntityData<Integer> InterpolationDuration = of(9, EntityDataValue.Serializers$INT, 0);
public static final DisplayEntityData<Integer> InterpolationDuration = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, !VersionHelper.isOrAbove1_20_2());
// 1.20.2+
public static final DisplayEntityData<Integer> TransformationInterpolationDuration = of(9, EntityDataValue.Serializers$INT, 0);
public static final DisplayEntityData<Integer> PositionRotationInterpolationDuration = of(10, EntityDataValue.Serializers$INT, 0);
public static final DisplayEntityData<Integer> TransformationInterpolationDuration = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, VersionHelper.isOrAbove1_20_2());
public static final DisplayEntityData<Integer> PositionRotationInterpolationDuration = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, 0, VersionHelper.isOrAbove1_20_2());
public static final DisplayEntityData<Object> Translation = of(11, EntityDataValue.Serializers$VECTOR3, new Vector3f(0f));
public static final DisplayEntityData<Object> Scale = of(12, EntityDataValue.Serializers$VECTOR3, new Vector3f(1f));
public static final DisplayEntityData<Object> RotationLeft = of(13, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f));
public static final DisplayEntityData<Object> RotationRight = of(14, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f));
public static final DisplayEntityData<Object> Translation = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(0f), true);
public static final DisplayEntityData<Object> Scale = of(DisplayEntityData.class, EntityDataValue.Serializers$VECTOR3, new Vector3f(1f), true);
public static final DisplayEntityData<Object> RotationLeft = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true);
public static final DisplayEntityData<Object> RotationRight = of(DisplayEntityData.class, EntityDataValue.Serializers$QUATERNION, new Quaternionf(0f, 0f, 0f, 1f), true);
/**
* Billboard Constraints (0 = FIXED, 1 = VERTICAL, 2 = HORIZONTAL, 3 = CENTER)
*/
public static final DisplayEntityData<Byte> BillboardConstraints = of(15, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final DisplayEntityData<Byte> BillboardConstraints = of(DisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0, true);
/**
* Brightness override (blockLight << 4 | skyLight << 20)
*/
public static final DisplayEntityData<Integer> BrightnessOverride = of(16, EntityDataValue.Serializers$INT, -1);
public static final DisplayEntityData<Float> ViewRange = of(17, EntityDataValue.Serializers$FLOAT, 1f);
public static final DisplayEntityData<Float> ShadowRadius = of(18, EntityDataValue.Serializers$FLOAT, 0f);
public static final DisplayEntityData<Float> ShadowStrength = of(19, EntityDataValue.Serializers$FLOAT, 0f);
public static final DisplayEntityData<Float> Width = of(20, EntityDataValue.Serializers$FLOAT, 0f);
public static final DisplayEntityData<Float> Height = of(21, EntityDataValue.Serializers$FLOAT, 0f);
public static final DisplayEntityData<Integer> GlowColorOverride = of(22, EntityDataValue.Serializers$INT, -1);
public static final DisplayEntityData<Integer> BrightnessOverride = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, -1, true);
public static final DisplayEntityData<Float> ViewRange = of(DisplayEntityData.class, EntityDataValue.Serializers$FLOAT, 1f, true);
public static final DisplayEntityData<Float> ShadowRadius = of(DisplayEntityData.class, EntityDataValue.Serializers$FLOAT, 0f, true);
public static final DisplayEntityData<Float> ShadowStrength = of(DisplayEntityData.class, EntityDataValue.Serializers$FLOAT, 0f, true);
public static final DisplayEntityData<Float> Width = of(DisplayEntityData.class, EntityDataValue.Serializers$FLOAT, 0f, true);
public static final DisplayEntityData<Float> Height = of(DisplayEntityData.class, EntityDataValue.Serializers$FLOAT, 0f, true);
public static final DisplayEntityData<Integer> GlowColorOverride = of(DisplayEntityData.class, EntityDataValue.Serializers$INT, -1, true);
public static <T> DisplayEntityData<T> of(final int id, final Object serializer, T defaultValue) {
return new DisplayEntityData<>(id, serializer, defaultValue);
public static <T> DisplayEntityData<T> of(final Class<?> clazz, final Object serializer, T defaultValue, boolean condition) {
if (!condition) return null;
return new DisplayEntityData<>(clazz, serializer, defaultValue);
}
public DisplayEntityData(int id, Object serializer, T defaultValue) {
super(!VersionHelper.isOrAbove1_20_2() && id >= 11 ? id - 1 : id, serializer, defaultValue);
public DisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -5,8 +5,11 @@ import java.util.List;
public interface EntityData<T> {
Object serializer();
int id();
T defaultValue();
Object entityDataAccessor();
default Object createEntityDataIfNotDefaultValue(T value) {
@@ -24,7 +27,7 @@ public interface EntityData<T> {
list.add(EntityDataValue.create(id(), serializer(), entityDataAccessor(), value));
}
static <T> EntityData<T> of(int id, Object serializer, T defaultValue) {
return new SimpleEntityData<>(id, serializer, defaultValue);
static <T> EntityData<T> of(Class<?> clazz, Object serializer, T defaultValue) {
return new SimpleEntityData<>(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,10 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class HappyGhastData<T> extends AnimalData<T> {
public static final HappyGhastData<Boolean> IsLeashHolder = new HappyGhastData<>(HappyGhastData.class, EntityDataValue.Serializers$BOOLEAN, false);
public static final BaseEntityData<Boolean> StaysStill = new HappyGhastData<>(HappyGhastData.class, EntityDataValue.Serializers$BOOLEAN, false);
public HappyGhastData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -1,17 +1,11 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.core.util.VersionHelper;
public class InteractionEntityData<T> extends BaseEntityData<T> {
public static final InteractionEntityData<Float> Width = of(8, EntityDataValue.Serializers$FLOAT, 1F);
public static final InteractionEntityData<Float> Height = of(9, EntityDataValue.Serializers$FLOAT, 1F);
public static final InteractionEntityData<Boolean> Responsive = of(10, EntityDataValue.Serializers$BOOLEAN, false);
public static final InteractionEntityData<Float> Width = new InteractionEntityData<>(InteractionEntityData.class, EntityDataValue.Serializers$FLOAT, 1F);
public static final InteractionEntityData<Float> Height = new InteractionEntityData<>(InteractionEntityData.class, EntityDataValue.Serializers$FLOAT, 1F);
public static final InteractionEntityData<Boolean> Responsive = new InteractionEntityData<>(InteractionEntityData.class, EntityDataValue.Serializers$BOOLEAN, false);
public static <T> InteractionEntityData<T> of(final int id, final Object serializer, T defaultValue) {
return new InteractionEntityData<>(id, serializer, defaultValue);
}
public InteractionEntityData(int id, Object serializer, T defaultValue) {
super(!VersionHelper.isOrAbove1_20_2() && id >= 11 ? id - 1 : id, serializer, defaultValue);
public InteractionEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -1,10 +1,10 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MItems;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class ItemDisplayEntityData<T> extends DisplayEntityData<T> {
// Item display only
public static final DisplayEntityData<Object> DisplayedItem = of(23, EntityDataValue.Serializers$ITEM_STACK, MItems.Air$ItemStack);
public static final DisplayEntityData<Object> DisplayedItem = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY);
/**
* Display type:
* 0 = NONE
@@ -17,9 +17,9 @@ public class ItemDisplayEntityData<T> extends DisplayEntityData<T> {
* 7 = GROUND
* 8 = FIXED
*/
public static final DisplayEntityData<Byte> DisplayType = of(24, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final DisplayEntityData<Byte> DisplayType = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public ItemDisplayEntityData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public ItemDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -4,15 +4,15 @@ import java.util.List;
import java.util.Optional;
public class LivingEntityData<T> extends BaseEntityData<T> {
public static final LivingEntityData<Byte> LivingEntityFlags = new LivingEntityData<>(8, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final LivingEntityData<Float> Health = new LivingEntityData<>(9, EntityDataValue.Serializers$FLOAT, 1.0f);
public static final LivingEntityData<List<Object>> EffectParticles = new LivingEntityData<>(10, EntityDataValue.Serializers$PARTICLES, List.of());
public static final LivingEntityData<Boolean> EffectAmbience = new LivingEntityData<>(11, EntityDataValue.Serializers$BOOLEAN, false);
public static final LivingEntityData<Integer> ArrowCount = new LivingEntityData<>(12, EntityDataValue.Serializers$INT, 0);
public static final LivingEntityData<Integer> StingerCount = new LivingEntityData<>(13, EntityDataValue.Serializers$INT, 0);
public static final LivingEntityData<Optional<Object>> SleepingPos = new LivingEntityData<>(14, EntityDataValue.Serializers$OPTIONAL_BLOCK_POS, Optional.empty());
public static final LivingEntityData<Byte> LivingEntityFlags = new LivingEntityData<>(LivingEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final LivingEntityData<Float> Health = new LivingEntityData<>(LivingEntityData.class, EntityDataValue.Serializers$FLOAT, 1.0f);
public static final LivingEntityData<List<Object>> EffectParticles = new LivingEntityData<>(LivingEntityData.class, EntityDataValue.Serializers$PARTICLES, List.of());
public static final LivingEntityData<Boolean> EffectAmbience = new LivingEntityData<>(LivingEntityData.class, EntityDataValue.Serializers$BOOLEAN, false);
public static final LivingEntityData<Integer> ArrowCount = new LivingEntityData<>(LivingEntityData.class, EntityDataValue.Serializers$INT, 0);
public static final LivingEntityData<Integer> StingerCount = new LivingEntityData<>(LivingEntityData.class, EntityDataValue.Serializers$INT, 0);
public static final LivingEntityData<Optional<Object>> SleepingPos = new LivingEntityData<>(LivingEntityData.class, EntityDataValue.Serializers$OPTIONAL_BLOCK_POS, Optional.empty());
public LivingEntityData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public LivingEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -1,9 +1,9 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class MobData<T> extends LivingEntityData<T> {
public static final MobData<Byte> MobFlags = new MobData<>(15, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final MobData<Byte> MobFlags = new MobData<>(MobData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public MobData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public MobData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class PathfinderMobData<T> extends MobData<T> {
public PathfinderMobData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -3,11 +3,11 @@ package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class ShulkerData<T> extends MobData<T> {
public static final ShulkerData<Object> AttachFace = new ShulkerData<>(16, EntityDataValue.Serializers$DIRECTION, CoreReflections.instance$Direction$DOWN);
public static final ShulkerData<Byte> Peek = new ShulkerData<>(17, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final ShulkerData<Byte> Color = new ShulkerData<>(18, EntityDataValue.Serializers$BYTE, (byte) 16);
public static final ShulkerData<Object> AttachFace = new ShulkerData<>(ShulkerData.class, EntityDataValue.Serializers$DIRECTION, CoreReflections.instance$Direction$DOWN);
public static final ShulkerData<Byte> Peek = new ShulkerData<>(ShulkerData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final ShulkerData<Byte> Color = new ShulkerData<>(ShulkerData.class, EntityDataValue.Serializers$BYTE, (byte) 16);
public ShulkerData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public ShulkerData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -1,15 +1,17 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.data.ClassTreeIdRegistry;
public class SimpleEntityData<T> implements EntityData<T> {
public static final ClassTreeIdRegistry ID_REGISTRY = new ClassTreeIdRegistry();
private final int id;
private final Object serializer;
private final T defaultValue;
private final Object entityDataAccessor;
public SimpleEntityData(int id, Object serializer, T defaultValue) {
this.id = id;
public SimpleEntityData(Class<?> clazz, Object serializer, T defaultValue) {
this.id = ID_REGISTRY.define(clazz);
this.serializer = serializer;
this.defaultValue = defaultValue;
this.entityDataAccessor = FastNMS.INSTANCE.constructor$EntityDataAccessor(id, serializer);

View File

@@ -3,13 +3,13 @@ package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class TextDisplayEntityData<T> extends DisplayEntityData<T> {
public static final DisplayEntityData<Object> Text = of(23, EntityDataValue.Serializers$COMPONENT, CoreReflections.instance$Component$empty);
public static final DisplayEntityData<Integer> LineWidth = of(24, EntityDataValue.Serializers$INT, 200);
public static final DisplayEntityData<Integer> BackgroundColor = of(25, EntityDataValue.Serializers$INT, 0x40000000);
public static final DisplayEntityData<Byte> TextOpacity = of(26, EntityDataValue.Serializers$BYTE, (byte) -1);
public static final DisplayEntityData<Byte> TextDisplayMasks = of(27, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final DisplayEntityData<Object> Text = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$COMPONENT, CoreReflections.instance$Component$empty);
public static final DisplayEntityData<Integer> LineWidth = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$INT, 200);
public static final DisplayEntityData<Integer> BackgroundColor = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$INT, 0x40000000);
public static final DisplayEntityData<Byte> TextOpacity = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) -1);
public static final DisplayEntityData<Byte> TextDisplayMasks = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public TextDisplayEntityData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public TextDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -1,11 +1,11 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MItems;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class ThrowableItemProjectileData<T> extends BaseEntityData<T> {
public static final ThrowableItemProjectileData<Object> ItemStack = new ThrowableItemProjectileData<>(8, EntityDataValue.Serializers$ITEM_STACK, MItems.Air$ItemStack);
public static final ThrowableItemProjectileData<Object> ItemStack = new ThrowableItemProjectileData<>(ThrowableItemProjectileData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY);
public ThrowableItemProjectileData(int id, Object serializer, T defaultValue) {
super(id, serializer, defaultValue);
public ThrowableItemProjectileData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -347,7 +347,10 @@ public class BukkitFurniture implements Furniture {
itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z);
});
this.seats.add(new WeakReference<>(seatEntity));
seatEntity.addPassenger(player);
if (!seatEntity.addPassenger(player)) {
seatEntity.remove();
this.removeOccupiedSeat(seat.offset());
}
}
private Location calculateSeatLocation(Seat seat) {

View File

@@ -5,7 +5,6 @@ import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
@@ -32,6 +31,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
public class BukkitFurnitureManager extends AbstractFurnitureManager {
public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY);
@@ -84,7 +84,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
});
if (playSound) {
SoundData data = furniture.settings().sounds().placeSound();
location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume(), data.pitch());
location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get());
}
return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
}
@@ -96,15 +96,31 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
COLLISION_ENTITY_TYPE = Config.colliderType();
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin());
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin());
for (World world : Bukkit.getWorlds()) {
List<Entity> entities = world.getEntities();
for (Entity entity : entities) {
if (entity instanceof ItemDisplay display) {
handleBaseEntityLoadEarly(display);
} else if (entity instanceof Interaction interaction) {
handleCollisionEntityLoadOnEntitiesLoad(interaction);
} else if (entity instanceof Boat boat) {
handleCollisionEntityLoadOnEntitiesLoad(boat);
if (VersionHelper.isFolia()) {
BiConsumer<Entity, Runnable> taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {});
for (World world : Bukkit.getWorlds()) {
List<Entity> entities = world.getEntities();
for (Entity entity : entities) {
if (entity instanceof ItemDisplay display) {
taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display));
} else if (entity instanceof Interaction interaction) {
taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction));
} else if (entity instanceof Boat boat) {
taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat));
}
}
}
} else {
for (World world : Bukkit.getWorlds()) {
List<Entity> entities = world.getEntities();
for (Entity entity : entities) {
if (entity instanceof ItemDisplay display) {
handleBaseEntityLoadEarly(display);
} else if (entity instanceof Interaction interaction) {
handleCollisionEntityLoadOnEntitiesLoad(interaction);
} else if (entity instanceof Boat boat) {
handleCollisionEntityLoadOnEntitiesLoad(boat);
}
}
}
}
@@ -396,7 +412,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
for (Entity bukkitEntity : nearbyEntities) {
if (bukkitEntity instanceof Player) continue;
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(bukkitEntity);
return (boolean) CoreReflections.method$Entity$canBeCollidedWith.invoke(nmsEntity);
return FastNMS.INSTANCE.method$Entity$canBeCollidedWith(nmsEntity);
}
} catch (Exception ignored) {}
return false;

View File

@@ -1,15 +1,24 @@
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.Map;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -17,10 +26,16 @@ import java.util.function.Supplier;
public class HappyGhastHitBox extends AbstractHitBox {
public static final Factory FACTORY = new Factory();
private final double scale;
private final boolean hardCollision;
private final List<Object> cachedValues = new ArrayList<>();
public HappyGhastHitBox(Seat[] seats, Vector3f position, double scale, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) {
public HappyGhastHitBox(Seat[] seats, Vector3f position, double scale, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile, boolean hardCollision) {
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
this.scale = scale;
this.hardCollision = hardCollision;
HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(hardCollision, this.cachedValues);
HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // NO AI
HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // Invisible
}
@Override
@@ -32,12 +47,62 @@ public class HappyGhastHitBox extends AbstractHitBox {
return scale;
}
public boolean hardCollision() {
return hardCollision;
}
@Override
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
public void initPacketsAndColliders(int[] entityIds, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) {
Vector3f offset = conjugated.transform(new Vector3f(position()));
try {
double x = position.x();
double y = position.y();
double z = position.z();
float yaw = position.xRot();
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0
), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[0], List.copyOf(this.cachedValues)), true);
if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) {
Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[0], Collections.singletonList(attributeInstance)), false);
}
if (this.hardCollision) {
collider.accept(this.createCollider(position.world(), offset, x, y, z, entityIds[0], aabb));
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to construct custom hitbox spawn packet", e);
}
}
public Collider createCollider(World world, Vector3f offset, double x, double y, double z, int entityId, BiConsumer<Integer, AABB> aabb) {
AABB ceAABB = createAABB(offset, x, y, z);
Object level = world.serverWorld();
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
aabb.accept(entityId, ceAABB);
return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding());
}
public AABB createAABB(Vector3f offset, double x, double y, double z) {
double baseSize = 4.0 * this.scale;
double halfSize = baseSize * 0.5;
double minX = x - halfSize + offset.x();
double maxX = x + halfSize + offset.x();
double minY = y + offset.y();
double maxY = y + baseSize + offset.y();
double minZ = z - halfSize + offset.z();
double maxZ = z + halfSize + offset.z();
return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
}
@Override
public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<AABB> aabbs) {
if (!this.hardCollision) return;
Vector3f offset = conjugated.transform(new Vector3f(position()));
AABB aabb = createAABB(offset, x, y, z);
aabbs.accept(aabb);
}
@Override
@@ -49,14 +114,18 @@ public class HappyGhastHitBox extends AbstractHitBox {
@Override
public HitBox create(Map<String, Object> arguments) {
if (!VersionHelper.isOrAbove1_21_6()) {
throw new UnsupportedOperationException("HappyGhastHitBox is only supported on 1.21.6+");
}
double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale");
boolean hardCollision = (boolean) arguments.getOrDefault("hard-collision", true);
boolean canUseOn = (boolean) arguments.getOrDefault("can-use-item-on", false);
boolean canBeHitByProjectile = (boolean) arguments.getOrDefault("can-be-hit-by-projectile", false);
boolean blocksBuilding = (boolean) arguments.getOrDefault("blocks-building", false);
return new HappyGhastHitBox(
HitBoxFactory.getSeats(arguments),
MiscUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"),
scale, canUseOn, blocksBuilding, canBeHitByProjectile
scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision
);
}
}

View File

@@ -16,4 +16,10 @@ public class BukkitCustomProjectile extends AbstractCustomProjectile {
public BukkitProjectile projectile() {
return (BukkitProjectile) super.projectile();
}
@SuppressWarnings("unchecked")
@Override
public Item<ItemStack> item() {
return (Item<ItemStack>) item;
}
}

View File

@@ -9,15 +9,16 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.scheduler.impl.FoliaTask;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.core.entity.projectile.CustomProjectile;
import net.momirealms.craftengine.core.entity.projectile.ProjectileManager;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.entity.projectile.ProjectileType;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -65,7 +66,7 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
}
@Override
public Optional<CustomProjectile> projectileByEntityId(int entityId) {
public Optional<BukkitCustomProjectile> projectileByEntityId(int entityId) {
return Optional.ofNullable(this.projectiles.get(entityId));
}
@@ -116,16 +117,15 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
if (meta != null) {
BukkitCustomProjectile customProjectile = new BukkitCustomProjectile(meta, projectile, wrapped);
this.projectiles.put(projectile.getEntityId(), customProjectile);
new ProjectileInjectTask(projectile);
new ProjectileInjectTask(projectile, !projectileItem.getItemMeta().hasEnchant(Enchantment.LOYALTY));
}
});
}
@EventHandler
public void onPlayerInteract(PlayerItemConsumeEvent event) {
String type = getType(event.getItem());
if (type == null) return;
if (type.equals("bow") || type.equals("trident")) {
public void onPlayerConsume(PlayerItemConsumeEvent event) {
ProjectileType type = getCustomProjectileType(event.getItem());
if (type == ProjectileType.TRIDENT) {
event.setCancelled(true);
}
}
@@ -133,23 +133,21 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
@EventHandler
public void onPlayerStopUsingItem(PlayerStopUsingItemEvent event) {
ItemStack item = event.getItem();
String type = getType(item);
ProjectileType type = getCustomProjectileType(item);
if (type == null) return;
int ticksHeldFor = event.getTicksHeldFor();
Player player = event.getPlayer();
if (type.equals("trident")) {
if (type == ProjectileType.TRIDENT) {
if (ticksHeldFor < 10) return;
Object nmsItemStack = FastNMS.INSTANCE.field$CraftItemStack$handle(item);
Object nmsServerLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld());
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(player);
TridentRelease.releaseUsing(nmsItemStack, nmsServerLevel, nmsEntity);
} else if (type.equals("bow")) {
if (ticksHeldFor < 3) return;
}
}
@Nullable
private String getType(ItemStack item) {
private ProjectileType getCustomProjectileType(ItemStack item) {
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(item);
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) return null;
@@ -162,11 +160,13 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
public class ProjectileInjectTask implements Runnable {
private final Projectile projectile;
private final SchedulerTask task;
private final boolean checkInGround;
private Object cachedServerEntity;
private int lastInjectedInterval = 0;
public ProjectileInjectTask(Projectile projectile) {
public ProjectileInjectTask(Projectile projectile, boolean checkInGround) {
this.projectile = projectile;
this.checkInGround = checkInGround;
if (VersionHelper.isFolia()) {
this.task = new FoliaTask(projectile.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> this.run(), () -> {}, 1, 1));
} else {
@@ -191,14 +191,18 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
this.cachedServerEntity = serverEntity;
}
boolean inGround = FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity);
if (canSpawnParticle(nmsEntity, inGround)) {
this.projectile.getWorld().spawnParticle(ParticleUtils.BUBBLE, this.projectile.getLocation(), 3, 0.1, 0.1, 0.1, 0);
}
if (inGround) {
updateProjectileUpdateInterval(Integer.MAX_VALUE);
} else {
if (!CoreReflections.clazz$AbstractArrow.isInstance(nmsEntity) || !this.checkInGround) {
updateProjectileUpdateInterval(1);
} else {
boolean inGround = FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity);
if (canSpawnParticle(nmsEntity, inGround)) {
this.projectile.getWorld().spawnParticle(ParticleUtils.BUBBLE, this.projectile.getLocation(), 3, 0.1, 0.1, 0.1, 0);
}
if (inGround) {
updateProjectileUpdateInterval(Integer.MAX_VALUE);
} else {
updateProjectileUpdateInterval(1);
}
}
}

View File

@@ -10,6 +10,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
@SuppressWarnings("all")
public class TridentRelease {
private TridentRelease() {}
@@ -32,14 +33,11 @@ public class TridentRelease {
private static boolean releaseUsing_1_21_2(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copyWithCount(stack, 1);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
Object copyStack1 = FastNMS.INSTANCE.method$ItemStack$copy(stack);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack1)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
if ((spinStrength > 0.0F && !FastNMS.INSTANCE.method$Entity$isInWaterOrRain(entity)) || FastNMS.INSTANCE.method$ItemStack$nextDamageWillBreak(stack)) {
return false;
}
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
FastNMS.INSTANCE.method$ItemStack$setDamageValue(copyStack1, FastNMS.INSTANCE.method$ItemStack$getDamageValue(stack) + 1);
Object sound = FastNMS.INSTANCE.method$EnchantmentHelper$pickHighestLevel(stack);
@@ -69,7 +67,7 @@ public class TridentRelease {
FastNMS.INSTANCE.method$ItemStack$consume(stack, 1, entity);
}
FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(trident, copyStack1);
FastNMS.INSTANCE.field$AbstractArrow$pickupItemStack(trident, copyStack);
if (FastNMS.INSTANCE.method$Player$hasInfiniteMaterials(entity)) {
FastNMS.INSTANCE.field$AbstractArrow$pickup(trident, CoreReflections.instance$AbstractArrow$Pickup$CREATIVE_ONLY);
}
@@ -118,7 +116,7 @@ public class TridentRelease {
}
private static boolean releaseUsing_1_21(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copyWithCount(stack, 1);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
@@ -212,7 +210,7 @@ public class TridentRelease {
}
private static boolean releaseUsing_1_20_5(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copyWithCount(stack, 1);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
@@ -261,7 +259,7 @@ public class TridentRelease {
level,
null,
entitythrowntrident,
MSoundEvents.instance$SoundEvent$TRIDENT_THROW,
MSoundEvents.TRIDENT_THROW,
CoreReflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
@@ -294,11 +292,11 @@ public class TridentRelease {
Object soundeffect;
if (spinStrength >= 3) {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_3;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_3;
} else if (spinStrength == 2) {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_2;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_2;
} else {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_1;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_1;
}
FastNMS.INSTANCE.method$Level$playSound(
@@ -313,7 +311,7 @@ public class TridentRelease {
}
private static boolean releaseUsing_1_20_3(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copyWithCount(stack, 1);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
@@ -362,7 +360,7 @@ public class TridentRelease {
level,
null,
entitythrowntrident,
MSoundEvents.instance$SoundEvent$TRIDENT_THROW,
MSoundEvents.TRIDENT_THROW,
CoreReflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
@@ -395,11 +393,11 @@ public class TridentRelease {
Object soundeffect;
if (spinStrength >= 3) {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_3;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_3;
} else if (spinStrength == 2) {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_2;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_2;
} else {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_1;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_1;
}
FastNMS.INSTANCE.method$Level$playSound(
@@ -414,7 +412,7 @@ public class TridentRelease {
}
private static boolean releaseUsing_1_20(Object stack, Object level, Object entity) {
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copy(stack);
Object copyStack = FastNMS.INSTANCE.method$ItemStack$copyWithCount(stack, 1);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(copyStack)) return false;
float spinStrength = FastNMS.INSTANCE.method$EnchantmentHelper$getTridentSpinAttackStrength(stack, entity);
@@ -463,7 +461,7 @@ public class TridentRelease {
level,
null,
entitythrowntrident,
MSoundEvents.instance$SoundEvent$TRIDENT_THROW,
MSoundEvents.TRIDENT_THROW,
CoreReflections.instance$SoundSource$PLAYERS,
1.0F, 1.0F
);
@@ -495,11 +493,11 @@ public class TridentRelease {
Object soundeffect;
if (spinStrength >= 3) {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_3;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_3;
} else if (spinStrength == 2) {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_2;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_2;
} else {
soundeffect = MSoundEvents.instance$SoundEvent$TRIDENT_RIPTIDE_1;
soundeffect = MSoundEvents.TRIDENT_RIPTIDE_1;
}
FastNMS.INSTANCE.method$Level$playSound(

View File

@@ -1,9 +1,7 @@
package net.momirealms.craftengine.bukkit.item;
import com.saicone.rtag.item.ItemTagStream;
import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.FlintAndSteelItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory;
import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener;
import net.momirealms.craftengine.bukkit.item.listener.DebugStickListener;
@@ -13,7 +11,6 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.entity.player.Player;
@@ -26,7 +23,6 @@ import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.ResourceKey;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
@@ -43,9 +39,8 @@ import java.util.Set;
public class BukkitItemManager extends AbstractItemManager<ItemStack> {
static {
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
registerVanillaItemExtraBehavior(FlintAndSteelItemBehavior.INSTANCE, ItemKeys.FLINT_AND_STEEL);
registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES);
}
private static BukkitItemManager instance;
@@ -139,9 +134,10 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
HandlerList.unregisterAll(this.armorEventListener);
}
@SuppressWarnings("deprecation")
@Override
public Item<ItemStack> fromByteArray(byte[] bytes) {
return this.factory.wrap(ItemTagStream.INSTANCE.fromBytes(bytes));
return this.factory.wrap(Bukkit.getUnsafe().deserializeItem(bytes));
}
@Override
@@ -165,12 +161,12 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
this.plugin.logger().warn(id + " is not a valid namespaced key");
return new ItemStack(Material.AIR);
}
Material material = Registry.MATERIAL.get(key);
if (material == null) {
Object item = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(id));
if (item == null) {
this.plugin.logger().warn(id + " is not a valid material");
return new ItemStack(Material.AIR);
}
return new ItemStack(material);
return FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.constructor$ItemStack(item, 1));
}
@Override

View File

@@ -64,7 +64,8 @@ public class LegacyNetworkItemHandler implements NetworkItemHandler<ItemStack> {
return new OtherItem(wrapped, false).process();
} else {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
boolean hasDifferentMaterial = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()) != customItem.clientItem();
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject());
boolean hasDifferentMaterial = serverItem == customItem.item() && serverItem != customItem.clientItem();
if (hasDifferentMaterial) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
}

View File

@@ -66,7 +66,8 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
return new OtherItem(wrapped, false).process();
} else {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
boolean hasDifferentMaterial = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()) != customItem.clientItem();
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject());
boolean hasDifferentMaterial = serverItem == customItem.item() && serverItem != customItem.clientItem();
if (hasDifferentMaterial) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
}

View File

@@ -0,0 +1,134 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
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.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.sparrow.nbt.CompoundTag;
import org.bukkit.GameEvent;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
public class AxeItemBehavior extends ItemBehavior {
public static final Factory FACTORY = new Factory();
public static final AxeItemBehavior INSTANCE = new AxeItemBehavior();
private static final Key AXE_STRIP_SOUND = Key.of("minecraft:item.axe.strip");
private boolean canBlockAttack(Item<ItemStack> item) {
if (VersionHelper.isOrAbove1_21_5()) {
return item.hasComponent("minecraft:blocks_attacks");
} else {
return item.vanillaId().equals(ItemKeys.SHIELD);
}
}
@SuppressWarnings({"UnstableApiUsage", "unchecked"})
@Override
public InteractionResult useOnBlock(UseOnContext context) {
Player player = context.getPlayer();
// no adventure mode for the moment
if (player.isAdventureMode()) {
return InteractionResult.PASS;
}
BukkitBlockInWorld block = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
BlockData blockData = block.block().getBlockData();
int stateId = BlockStateUtils.blockDataToId(blockData);
if (BlockStateUtils.isVanillaBlock(stateId)) return InteractionResult.PASS;
ImmutableBlockState customBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (customBlockState == null || customBlockState.isEmpty()) return InteractionResult.PASS;
Optional<StrippableBlockBehavior> behaviorOptional = customBlockState.behavior().getAs(StrippableBlockBehavior.class);
if (behaviorOptional.isEmpty()) return InteractionResult.PASS;
Key stripped = behaviorOptional.get().stripped();
Item<ItemStack> offHandItem = (Item<ItemStack>) player.getItemInHand(InteractionHand.OFF_HAND);
// is using a shield
if (context.getHand() == InteractionHand.MAIN_HAND && offHandItem != null && canBlockAttack(offHandItem) && !player.isSecondaryUseActive()) {
return InteractionResult.PASS;
}
Optional<CustomBlock> optionalNewCustomBlock = BukkitBlockManager.instance().blockById(stripped);
if (optionalNewCustomBlock.isEmpty()) {
CraftEngine.instance().logger().warn("stripped block " + stripped + " does not exist");
return InteractionResult.FAIL;
}
CustomBlock newCustomBlock = optionalNewCustomBlock.get();
CompoundTag compoundTag = customBlockState.propertiesNbt();
ImmutableBlockState newState = newCustomBlock.getBlockState(compoundTag);
org.bukkit.entity.Player bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer());
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
// Call bukkit event
EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().handle()));
if (EventUtils.fireAndCheckCancel(event)) {
return InteractionResult.FAIL;
}
Item<ItemStack> item = (Item<ItemStack>) context.getItem();
BlockPos pos = context.getClickedPos();
context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1);
FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().handle(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags());
clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z()));
Material material = MaterialUtils.getMaterial(item.vanillaId());
bukkitPlayer.setStatistic(Statistic.USE_ITEM, material, bukkitPlayer.getStatistic(Statistic.USE_ITEM, material) + 1);
// resend swing if it's not interactable on client side
if (!InteractUtils.isInteractable(
bukkitPlayer, BlockStateUtils.fromBlockData(customBlockState.vanillaBlockState().handle()),
context.getHitResult(), item
) || player.isSecondaryUseActive()) {
player.swingHand(context.getHand());
}
// shrink item amount
if (VersionHelper.isOrAbove1_20_5()) {
Object itemStack = item.getLiteralObject();
Object serverPlayer = player.serverPlayer();
Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND;
try {
CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot);
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to hurt itemStack", e);
}
} else {
ItemStack itemStack = item.getItem();
itemStack.damage(1, bukkitPlayer);
}
return InteractionResult.SUCCESS;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -17,6 +17,7 @@ import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.behavior.BlockBoundItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
@@ -52,7 +53,7 @@ import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
public class BlockItemBehavior extends ItemBehavior {
public class BlockItemBehavior extends BlockBoundItemBehavior {
public static final Factory FACTORY = new Factory();
private final Key blockId;
@@ -112,11 +113,6 @@ public class BlockItemBehavior extends ItemBehavior {
}
}
int gameTicks = player.gameTicks();
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
return InteractionResult.FAIL;
}
// trigger event
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
DirectionUtils.toBlockFace(context.getClickedFace()), bukkitBlock, context.getHand());
@@ -164,6 +160,8 @@ public class BlockItemBehavior extends ItemBehavior {
item.load();
}
block.setPlacedBy(context, blockStateToPlace);
player.swingHand(context.getHand());
context.getLevel().playBlockSound(position, blockStateToPlace.sounds().placeSound());
world.sendGameEvent(bukkitPlayer, GameEvent.BLOCK_PLACE, new Vector(pos.x(), pos.y(), pos.z()));
@@ -199,7 +197,8 @@ public class BlockItemBehavior extends ItemBehavior {
}
}
public Key blockId() {
@Override
public Key block() {
return this.blockId;
}

View File

@@ -1,72 +0,0 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import java.nio.file.Path;
import java.util.Map;
public class BucketItemBehavior extends ItemBehavior {
public static final BucketItemBehavior INSTANCE = new BucketItemBehavior();
public static final Factory FACTORY = new Factory();
private static final Key ITEM_BUCKET_FILL = Key.of("item.bucket.fill");
@SuppressWarnings("unchecked")
@Override
public InteractionResult useOnBlock(UseOnContext context) {
if (context.getPlayer().isAdventureMode()) return InteractionResult.PASS;
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
Block block = clicked.block();
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state == null || state.isEmpty()) return InteractionResult.PASS;
CustomBlock customBlock = state.owner().value();
Property<Boolean> waterlogged = (Property<Boolean>) customBlock.getProperty("waterlogged");
if (waterlogged == null) return InteractionResult.PASS;
boolean waterloggedState = state.get(waterlogged);
if (!waterloggedState) return InteractionResult.PASS;
BlockPos pos = context.getClickedPos();
Player player = (Player) context.getPlayer().platformPlayer();
World world = player.getWorld();
Location location = new Location(world, pos.x(), pos.y(), pos.z());
EquipmentSlot slot = context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND;
CraftEngineBlocks.place(location, state.with(waterlogged, false), UpdateOption.UPDATE_ALL, false);
if (player.getGameMode() == GameMode.SURVIVAL) {
// to prevent dupe in moment
player.getInventory().setItem(slot, new ItemStack(Material.AIR));
BukkitCraftEngine.instance().scheduler().sync().runDelayed(() ->
player.getInventory().setItem(slot, new ItemStack(Material.WATER_BUCKET)), world, location.getBlockX() >> 4, location.getBlockZ() >> 4);
}
player.setStatistic(Statistic.USE_ITEM, Material.BUCKET, player.getStatistic(Statistic.USE_ITEM, Material.BUCKET) + 1);
// client will assume it has sounds
// context.getPlayer().level().playBlockSound(Vec3d.atCenterOf(context.getClickedPos()), ITEM_BUCKET_FILL, 1, 1);
return InteractionResult.SUCCESS_AND_CANCEL;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -8,19 +8,17 @@ public class BukkitItemBehaviors extends ItemBehaviors {
public static final Key BLOCK_ITEM = Key.from("craftengine:block_item");
public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item");
public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item");
public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item");
public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item");
public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item");
public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item");
public static final Key AXE_ITEM = Key.from("craftengine:axe_item");
public static void init() {
register(EMPTY, EmptyItemBehavior.FACTORY);
register(BLOCK_ITEM, BlockItemBehavior.FACTORY);
register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY);
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY);
register(BUCKET_ITEM, BucketItemBehavior.FACTORY);
register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY);
register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY);
register(AXE_ITEM, AxeItemBehavior.FACTORY);
}
}

View File

@@ -1,7 +1,6 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.EventUtils;
@@ -35,6 +34,7 @@ public class CompostableItemBehavior extends ItemBehavior {
this.chance = chance;
}
@SuppressWarnings("UnstableApiUsage")
@Override
public InteractionResult useOnBlock(UseOnContext context) {
BukkitBlockInWorld block = (BukkitBlockInWorld) context.getLevel().getBlockAt(context.getClickedPos());
@@ -61,7 +61,7 @@ public class CompostableItemBehavior extends ItemBehavior {
context.getLevel().levelEvent(WorldEvents.COMPOSTER_COMPOSTS, context.getClickedPos(), willRaise ? 1 : 0);
((World) context.getLevel().platformWorld()).sendGameEvent((Entity) context.getPlayer().platformPlayer(), GameEvent.BLOCK_CHANGE, new Vector(block.x() + 0.5, block.y() + 0.5, block.z() + 0.5));
if (currentLevel + 1 == 7) {
FastNMS.INSTANCE.method$LevelAccessor$scheduleTick(context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), blockOwner, 20);
FastNMS.INSTANCE.method$LevelAccessor$scheduleBlockTick(context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), blockOwner, 20);
}
if (!context.getPlayer().canInstabuild()) {
context.getItem().shrink(1);

View File

@@ -21,7 +21,6 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

View File

@@ -81,11 +81,6 @@ public class FurnitureItemBehavior extends ItemBehavior {
return InteractionResult.FAIL;
}
int gameTicks = player.gameTicks();
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
return InteractionResult.FAIL;
}
Vec3d clickedPosition = context.getClickLocation();
// trigger event

View File

@@ -1,65 +0,0 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import java.nio.file.Path;
import java.util.Map;
public class WaterBucketItemBehavior extends ItemBehavior {
public static final WaterBucketItemBehavior INSTANCE = new WaterBucketItemBehavior();
public static final Factory FACTORY = new Factory();
@SuppressWarnings("unchecked")
@Override
public InteractionResult useOnBlock(UseOnContext context) {
if (context.getPlayer().isAdventureMode()) return InteractionResult.PASS;
BlockPos pos = context.getClickedPos();
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(pos);
Block block = clicked.block();
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state == null || state.isEmpty()) return InteractionResult.PASS;
CustomBlock customBlock = state.owner().value();
Property<Boolean> waterlogged = (Property<Boolean>) customBlock.getProperty("waterlogged");
if (waterlogged == null) return InteractionResult.PASS;
Player player = (Player) context.getPlayer().platformPlayer();
World world = player.getWorld();
Location location = new Location(world, pos.x(), pos.y(), pos.z());
// TODO Refactor all of this because it's playing a trick with the server
ImmutableBlockState nextState = state.with(waterlogged, true);
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(() ->
CraftEngineBlocks.place(location, nextState, UpdateOption.UPDATE_ALL, false), world, location.getBlockX() >> 4, location.getBlockZ() >> 4);
return InteractionResult.SUCCESS;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -1,8 +1,6 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.google.gson.JsonElement;
import com.saicone.rtag.item.ItemTagStream;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.ItemTags;
import net.momirealms.craftengine.core.item.ItemFactory;
@@ -12,6 +10,7 @@ import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import java.util.Objects;
@@ -48,9 +47,10 @@ public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extend
}
}
@SuppressWarnings("deprecation")
@Override
protected byte[] toByteArray(W item) {
return ItemTagStream.INSTANCE.toBytes(item.getItem());
return Bukkit.getUnsafe().serializeItem(item.getItem());
}
@Override

View File

@@ -1,10 +1,9 @@
package net.momirealms.craftengine.bukkit.item.listener;
import com.saicone.rtag.RtagItem;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.item.LegacyItemWrapper;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
@@ -14,6 +13,7 @@ import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.craftengine.core.util.MiscUtils;
import org.bukkit.Material;
@@ -73,11 +73,11 @@ public class DebugStickListener implements Listener {
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(blockId))), true);
player.sendPacket(systemChatPacket, false);
} else {
LegacyItemWrapper wrapped = new LegacyItemWrapper(new RtagItem(itemInHand));
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(itemInHand);
Object storedData = wrapped.getJavaTag("craftengine:debug_stick_state");
if (storedData == null) storedData = new HashMap<>();
if (storedData instanceof Map<?,?> map) {
Map<String, Object> data = MiscUtils.castToMap(map, false);
Map<String, Object> data = new HashMap<>(MiscUtils.castToMap(map, false));
String currentPropertyName = (String) data.get(blockId);
Property<?> currentProperty = block.getProperty(currentPropertyName);
if (currentProperty == null) {

View File

@@ -1,8 +1,12 @@
package net.momirealms.craftengine.bukkit.item.listener;
import io.papermc.paper.event.block.CompostItemEvent;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.item.BukkitCustomItem;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
@@ -16,33 +20,40 @@ import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.item.setting.FoodData;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.EntityType;
import org.bukkit.block.data.Openable;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.EnchantingInventory;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -161,14 +172,45 @@ public class ItemEventListener implements Listener {
if (hitResult != null) {
UseOnContext useOnContext = new UseOnContext(serverPlayer, hand, itemInHand, hitResult);
if (immutableBlockState.behavior() instanceof AbstractBlockBehavior behavior) {
InteractionResult result = behavior.useOnBlock(useOnContext, immutableBlockState);
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
boolean hasItem = player.getInventory().getItemInMainHand().getType() != Material.AIR || player.getInventory().getItemInOffHand().getType() != Material.AIR;
boolean flag = player.isSneaking() && hasItem;
if (!flag) {
if (immutableBlockState.behavior() instanceof AbstractBlockBehavior behavior) {
InteractionResult result = behavior.useOnBlock(useOnContext, immutableBlockState);
if (result.success()) {
serverPlayer.updateLastSuccessfulInteractionTick(serverPlayer.gameTicks());
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
}
return;
}
if (result == InteractionResult.TRY_EMPTY_HAND && hand == InteractionHand.MAIN_HAND) {
result = behavior.useWithoutItem(useOnContext, immutableBlockState);
if (result.success()) {
serverPlayer.updateLastSuccessfulInteractionTick(serverPlayer.gameTicks());
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
}
return;
}
}
if (result == InteractionResult.FAIL) {
return;
}
}
if (result != InteractionResult.PASS) {
return;
}
}
} else {
if (Config.enableSoundSystem() && hitResult != null) {
Object blockOwner = FastNMS.INSTANCE.method$BlockState$getBlock(blockState);
if (this.plugin.blockManager().isOpenableBlockSoundRemoved(blockOwner)) {
boolean hasItem = player.getInventory().getItemInMainHand().getType() != Material.AIR || player.getInventory().getItemInOffHand().getType() != Material.AIR;
boolean flag = player.isSneaking() && hasItem;
if (!flag) {
if (blockData instanceof Openable openable) {
SoundData soundData = this.plugin.blockManager().getRemovedOpenableBlockSound(blockOwner, !openable.isOpen());
serverPlayer.playSound(soundData.id(), SoundSource.BLOCK, soundData.volume().get(), soundData.pitch().get());
}
}
}
}
@@ -258,11 +300,13 @@ public class ItemEventListener implements Listener {
// 依次执行物品行为
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.useOnBlock(useOnContext);
if (result.success()) {
serverPlayer.updateLastSuccessfulInteractionTick(serverPlayer.gameTicks());
}
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
}
// 非pass的情况直接结束
if (result != InteractionResult.PASS) {
return;
}
@@ -288,7 +332,7 @@ public class ItemEventListener implements Listener {
}
}
@EventHandler
@EventHandler(priority = EventPriority.HIGH)
public void onInteractAir(PlayerInteractEvent event) {
Action action = event.getAction();
if (action != Action.RIGHT_CLICK_AIR && action != Action.LEFT_CLICK_AIR)
@@ -304,9 +348,9 @@ public class ItemEventListener implements Listener {
if (itemInHand == null) return;
// todo 真的需要这个吗
// if (cancelEventIfHasInteraction(event, serverPlayer, hand)) {
// return;
// }
if (cancelEventIfHasInteraction(event, serverPlayer, hand)) {
return;
}
Optional<CustomItem<ItemStack>> optionalCustomItem = itemInHand.getCustomItem();
if (optionalCustomItem.isPresent()) {
@@ -325,6 +369,9 @@ public class ItemEventListener implements Listener {
if (optionalItemBehaviors.isPresent()) {
for (ItemBehavior itemBehavior : optionalItemBehaviors.get()) {
InteractionResult result = itemBehavior.use(serverPlayer.world(), serverPlayer, hand);
if (result.success()) {
serverPlayer.updateLastSuccessfulInteractionTick(serverPlayer.gameTicks());
}
if (result == InteractionResult.SUCCESS_AND_CANCEL) {
event.setCancelled(true);
return;
@@ -404,7 +451,7 @@ public class ItemEventListener implements Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityDamage(EntityDamageEvent event) {
if (event.getEntityType() == EntityType.ITEM && event.getEntity() instanceof org.bukkit.entity.Item item) {
if (event.getEntity() instanceof org.bukkit.entity.Item item) {
Optional.ofNullable(this.plugin.itemManager().wrap(item.getItemStack()))
.flatMap(Item::getCustomItem)
.ifPresent(it -> {
@@ -414,4 +461,57 @@ public class ItemEventListener implements Listener {
});
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEnchant(PrepareItemEnchantEvent event) {
ItemStack itemToEnchant = event.getItem();
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(itemToEnchant);
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) return;
CustomItem<ItemStack> customItem = optionalCustomItem.get();
if (!customItem.settings().canEnchant()) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true)
public void onCompost(CompostItemEvent event) {
ItemStack itemToCompost = event.getItem();
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(itemToCompost);
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) return;
event.setWillRaiseLevel(RandomUtils.generateRandomFloat(0, 1) < optionalCustomItem.get().settings().compostProbability());
}
@EventHandler(ignoreCancelled = true)
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getInventory() instanceof EnchantingInventory inventory)) return;
if (!(event.getWhoClicked() instanceof Player player)) return;
ItemStack lazuli = inventory.getSecondary();
if (lazuli != null) return;
ItemStack item = inventory.getItem();
if (item == null) return;
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(item);
if (wrapped == null) return;
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) return;
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
if (customItem.clientItem() == FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) return;
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
if (serverPlayer == null) return;
this.plugin.scheduler().sync().runDelayed(() -> {
Object container = FastNMS.INSTANCE.field$Player$containerMenu(serverPlayer.serverPlayer());
if (!CoreReflections.clazz$EnchantmentMenu.isInstance(container)) return;
Object secondSlotItem = FastNMS.INSTANCE.method$Slot$getItem(FastNMS.INSTANCE.method$AbstractContainerMenu$getSlot(container, 1));
if (secondSlotItem == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(secondSlotItem)) return;
Object[] dataSlots = FastNMS.INSTANCE.field$AbstractContainerMenu$dataSlots(container).toArray();
List<Object> packets = new ArrayList<>(dataSlots.length);
for (int i = 0; i < dataSlots.length; i++) {
Object dataSlot = dataSlots[i];
int data = FastNMS.INSTANCE.method$DataSlot$get(dataSlot);
packets.add(FastNMS.INSTANCE.constructor$ClientboundContainerSetDataPacket(FastNMS.INSTANCE.field$AbstractContainerMenu$containerId(container), i, data));
}
serverPlayer.sendPackets(packets, false);
});
}
}

View File

@@ -2,8 +2,6 @@ package net.momirealms.craftengine.bukkit.item.recipe;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.saicone.rtag.item.ItemObject;
import com.saicone.rtag.tag.TagCompound;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.CloneableConstantItem;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
@@ -11,6 +9,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.MaterialUtils;
import net.momirealms.craftengine.bukkit.util.RecipeUtils;
@@ -304,12 +303,14 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
public void unload() {
if (!Config.enableRecipeSystem()) return;
super.unload();
try {
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(nmsRecipeManager);
}
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to unregister recipes", e);
if (VersionHelper.isOrAbove1_21_2()) {
this.plugin.scheduler().executeSync(() -> {
try {
CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(nmsRecipeManager);
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to unregister recipes", e);
}
});
}
}
@@ -435,7 +436,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
}
}
} catch (Exception e) {
plugin.logger().warn("Failed to read data pack recipes", e);
this.plugin.logger().warn("Failed to read data pack recipes", e);
}
}
@@ -491,7 +492,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
}
private void handleDataPackStoneCuttingRecipe(Key id, VanillaStoneCuttingRecipe recipe) {
ItemStack result = createResultStack(recipe.result());
ItemStack result = createDataPackResultStack(recipe.result());
Set<Holder<Key>> holders = new HashSet<>();
for (String item : recipe.ingredient()) {
if (item.charAt(0) == '#') {
@@ -510,7 +511,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
private void handleDataPackShapelessRecipe(Key id, VanillaShapelessRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createResultStack(recipe.result());
ItemStack result = createDataPackResultStack(recipe.result());
boolean hasCustomItemInTag = false;
List<Ingredient<ItemStack>> ingredientList = new ArrayList<>();
for (List<String> list : recipe.ingredients()) {
@@ -546,7 +547,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
private void handleDataPackShapedRecipe(Key id, VanillaShapedRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createResultStack(recipe.result());
ItemStack result = createDataPackResultStack(recipe.result());
boolean hasCustomItemInTag = false;
Map<Character, Ingredient<ItemStack>> ingredients = new HashMap<>();
for (Map.Entry<Character, List<String>> entry : recipe.ingredients().entrySet()) {
@@ -586,7 +587,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
HeptaFunction<Key, CookingRecipeCategory, String, Ingredient<ItemStack>, Integer, Float, CustomRecipeResult<ItemStack>, CustomCookingRecipe<ItemStack>> constructor2,
Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createResultStack(recipe.result());
ItemStack result = createDataPackResultStack(recipe.result());
Set<Holder<Key>> holders = new HashSet<>();
boolean hasCustomItemInTag = readVanillaIngredients(false, recipe.ingredient(), holders::add);
CustomCookingRecipe<ItemStack> ceRecipe = constructor2.apply(
@@ -607,7 +608,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
private void handleDataPackSmithingTransform(Key id, VanillaSmithingTransformRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createResultStack(recipe.result());
ItemStack result = createDataPackResultStack(recipe.result());
boolean hasCustomItemInTag;
Set<Holder<Key>> additionHolders = new HashSet<>();
@@ -656,17 +657,22 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
return hasCustomItemInTag;
}
private ItemStack createResultStack(RecipeResult result) {
private ItemStack createDataPackResultStack(RecipeResult result) {
ItemStack itemStack;
if (result.components() == null) {
itemStack = new ItemStack(Objects.requireNonNull(MaterialUtils.getMaterial(result.id())));
itemStack.setAmount(result.count());
} else {
// 低版本无法应用nbt或组件,所以这里是1.20.5+
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", result.id());
jsonObject.addProperty("count", result.count());
jsonObject.add("components", result.components());
Object nmsStack = ItemObject.newItem(TagCompound.newTag(jsonObject.toString()));
Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject)
.resultOrPartial((itemId) -> plugin.logger().severe("Tried to load invalid item: '" + itemId + "'")).orElse(null);
if (nmsStack == null) {
return new ItemStack(Material.STONE);
}
try {
itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack);
} catch (Exception e) {

View File

@@ -473,6 +473,7 @@ public class RecipeEventListener implements Listener {
// one of them is vanilla item
if (!firstCustom || !secondCustom) {
if (second.canRepair(first)) return; // 这里需要考虑原版逻辑
// block "vanilla + custom" recipes
event.setResult(null);
return;
@@ -792,6 +793,8 @@ public class RecipeEventListener implements Listener {
}
// 不是完美的解决方案,仍然需要更多的探讨
// TODO 生成类代理掉ResultSlot并注入menu的slots对象修改掉onTake方法
// TODO 对于耐久度降低的配方应该注册special recipe
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onCraft(CraftItemEvent event) {
org.bukkit.inventory.Recipe recipe = event.getRecipe();

View File

@@ -5,7 +5,6 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.command.feature.*;
import net.momirealms.craftengine.core.plugin.command.AbstractCommandManager;
import net.momirealms.craftengine.core.plugin.command.CommandFeature;
import net.momirealms.craftengine.core.plugin.command.parser.BlockStateParser;
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.SenderMapper;
@@ -18,7 +17,7 @@ import java.util.List;
public class BukkitCommandManager extends AbstractCommandManager<CommandSender> {
private final BukkitCraftEngine plugin;
private final Index<String, CommandFeature<CommandSender>> INDEX;
private final Index<String, CommandFeature<CommandSender>> index;
public BukkitCommandManager(BukkitCraftEngine plugin) {
super(plugin, new LegacyPaperCommandManager<>(
@@ -27,7 +26,7 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
SenderMapper.identity()
));
this.plugin = plugin;
this.INDEX = Index.create(CommandFeature::getFeatureID, List.of(
this.index = Index.create(CommandFeature::getFeatureID, List.of(
new ReloadCommand(this, plugin),
new GetItemCommand(this, plugin),
new GiveItemCommand(this, plugin),
@@ -49,6 +48,7 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
new DebugTargetBlockCommand(this, plugin),
new DebugIsSectionInjectedCommand(this, plugin),
new DebugMigrateTemplatesCommand(this, plugin),
new DebugIsChunkPersistentLoadedCommand(this, plugin),
new DebugEntityId2UUIDCommand(this, plugin),
new TotemAnimationCommand(this, plugin),
new EnableResourceCommand(this, plugin),
@@ -59,7 +59,6 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
));
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);
manager.parserRegistry().registerParser(BlockStateParser.blockStateParser());
if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
manager.registerBrigadier();
manager.brigadierManager().setNativeNumberSuggestions(true);
@@ -70,11 +69,11 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
@Override
protected Sender wrapSender(CommandSender sender) {
return plugin.senderFactory().wrap(sender);
return this.plugin.senderFactory().wrap(sender);
}
@Override
public Index<String, CommandFeature<CommandSender>> features() {
return INDEX;
return this.index;
}
}

View File

@@ -0,0 +1,35 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.command.sender.Sender;
import org.bukkit.Chunk;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command;
public class DebugIsChunkPersistentLoadedCommand extends BukkitCommandFeature<CommandSender> {
public DebugIsChunkPersistentLoadedCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
super(commandManager, plugin);
}
@Override
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.senderType(Player.class)
.handler(context -> {
Player player = context.sender();
Chunk chunk = player.getChunk();
Sender sender = plugin().senderFactory().wrap(player);
sender.sendMessage(Component.text(chunk.isForceLoaded()));
});
}
@Override
public String getFeatureID() {
return "debug_is_chunk_persistent_loaded";
}
}

View File

@@ -1,10 +1,10 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import com.saicone.rtag.RtagMirror;
import com.saicone.rtag.item.ItemObject;
import com.saicone.rtag.tag.TagCompound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -55,20 +55,7 @@ public class DebugItemDataCommand extends BukkitCommandFeature<CommandSender> {
}
private static Map<String, Object> toMap(ItemStack object) {
return TagCompound.getValue(RtagMirror.INSTANCE, toCompound(object));
}
private static Object toCompound(ItemStack object) {
if (object == null) {
return null;
} else {
Object compound = extract(object);
return TagCompound.isTagCompound(compound) ? compound : null;
}
}
private static Object extract(ItemStack object) {
return ItemObject.save(ItemObject.asNMSCopy(object));
return TagCompound.getValue(RtagMirror.INSTANCE, FastNMS.INSTANCE.itemStackToCompoundTag(object));
}
private List<String> mapToList(Map<String, Object> readableDataMap) {

View File

@@ -16,7 +16,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DebugMigrateTemplatesCommand extends BukkitCommandFeature<CommandSender> {
private static final Pattern PATTERN = Pattern.compile("(?<!\\$)\\{([^}]+)}");
private static final Pattern PATTERN = Pattern.compile("(?<!\\$)\\{([0-9a-zA-Z_]+)}");
public DebugMigrateTemplatesCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
super(commandManager, plugin);

View File

@@ -1,16 +1,23 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command;
import org.incendo.cloud.parser.standard.BooleanParser;
import org.incendo.cloud.parser.standard.FloatParser;
import org.incendo.cloud.bukkit.parser.location.LocationParser;
import org.incendo.cloud.parser.standard.IntegerParser;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TestCommand extends BukkitCommandFeature<CommandSender> {
@@ -22,17 +29,35 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.senderType(Player.class)
.required("tickRate", FloatParser.floatParser())
.required("isFrozen", BooleanParser.booleanParser())
.required("location", LocationParser.locationParser())
.required("remove", IntegerParser.integerParser())
.handler(context -> {
Player player = context.sender();
float tickRate = context.get("tickRate");
boolean isFrozen = context.get("isFrozen");
try {
plugin().adapt(player).sendPacket(NetworkReflections.constructor$ClientboundTickingStatePacket.newInstance(tickRate, isFrozen), true);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
player.sendMessage("发送失败");
int removeEntityId = context.get("remove");
if (removeEntityId >= 0) {
try {
Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{removeEntityId});
plugin().adapt(player).sendPacket(packet, true);
player.sendMessage("发送成功");
} catch (ReflectiveOperationException e) {
player.sendMessage("发送失败");
}
return;
}
Location location = context.get("location");
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
List<Object> packets = new ArrayList<>();
List<Object> cachedShulkerValues = new ArrayList<>();
HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, cachedShulkerValues); // NO AI
// HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedShulkerValues); // Invisible
HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(true, cachedShulkerValues);
packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), location.x(), location.y(), location.z(), 0, location.getYaw(),
MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0
));
packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, List.copyOf(cachedShulkerValues)));
plugin().adapt(player).sendPackets(packets, true);
player.sendMessage("发送成功 id: " + entityId);
});
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.plugin.gui;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
@@ -47,7 +48,7 @@ public class BukkitGuiManager implements GuiManager, Listener {
case ANVIL -> MenuType.ANVIL.create(bukkitPlayer).open();
case LOOM -> MenuType.LOOM.create(bukkitPlayer).open();
case ENCHANTMENT -> MenuType.ENCHANTMENT.create(bukkitPlayer).open();
case CRAFTING -> MenuType.CRAFTING.create(bukkitPlayer).open();
case CRAFTING -> MenuType.CRAFTER_3X3.create(bukkitPlayer).open();
case CARTOGRAPHY -> MenuType.CARTOGRAPHY_TABLE.create(bukkitPlayer).open();
case SMITHING -> MenuType.SMITHING.create(bukkitPlayer).open();
case GRINDSTONE -> MenuType.GRINDSTONE.create(bukkitPlayer).open();
@@ -69,7 +70,7 @@ public class BukkitGuiManager implements GuiManager, Listener {
public void updateInventoryTitle(net.momirealms.craftengine.core.entity.player.Player player, Component component) {
Object nmsPlayer = player.serverPlayer();
try {
Object containerMenu = CoreReflections.field$Player$containerMenu.get(nmsPlayer);
Object containerMenu = FastNMS.INSTANCE.field$Player$containerMenu(nmsPlayer);
int containerId = CoreReflections.field$AbstractContainerMenu$containerId.getInt(containerMenu);
Object menuType = CoreReflections.field$AbstractContainerMenu$menuType.get(containerMenu);
Object packet = NetworkReflections.constructor$ClientboundOpenScreenPacket.newInstance(containerId, menuType, ComponentUtils.adventureToMinecraft(component));

View File

@@ -29,6 +29,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
public final class BlockGenerator {
private static final BukkitBlockShape STONE_SHAPE =
@@ -54,7 +55,7 @@ public final class BlockGenerator {
// should always implement this interface
.implement(CoreReflections.clazz$Fallable)
.implement(CoreReflections.clazz$BonemealableBlock)
// TODO .implement(CoreReflections.clazz$SimpleWaterloggedBlock)
.implement(CoreReflections.clazz$SimpleWaterloggedBlock)
// internal interfaces
.implement(BehaviorHolder.class)
.implement(ShapeHolder.class)
@@ -76,6 +77,9 @@ public final class BlockGenerator {
// getSupportShape
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getBlockSupportShape))
.intercept(MethodDelegation.to(GetSupportShapeInterceptor.INSTANCE))
// isPathFindable
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$isPathFindable))
.intercept(MethodDelegation.to(IsPathFindableInterceptor.INSTANCE))
// mirror
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$mirror))
.intercept(MethodDelegation.to(MirrorInterceptor.INSTANCE))
@@ -137,9 +141,44 @@ public final class BlockGenerator {
.and(ElementMatchers.takesArgument(1, CoreReflections.clazz$LevelReader).or(ElementMatchers.takesArgument(1, CoreReflections.clazz$Direction)))
.and(ElementMatchers.named("updateShape").or(ElementMatchers.named("a"))))
.intercept(MethodDelegation.to(UpdateShapeInterceptor.INSTANCE))
// onExplosionHit 1.21+
.method(ElementMatchers.returns(void.class)
.and(ElementMatchers.takesArgument(0, CoreReflections.clazz$BlockState))
.and(ElementMatchers.takesArgument(1, CoreReflections.clazz$ServerLevel))
.and(ElementMatchers.takesArgument(2, CoreReflections.clazz$BlockPos))
.and(ElementMatchers.takesArgument(3, CoreReflections.clazz$Explosion))
.and(ElementMatchers.takesArgument(4, BiConsumer.class))
)
.intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE))
// neighborChanged
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$neighborChanged))
.intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE));
.intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE))
// pickupBlock
.method(ElementMatchers.is(CoreReflections.method$SimpleWaterloggedBlock$pickupBlock))
.intercept(MethodDelegation.to(PickUpBlockInterceptor.INSTANCE))
// placeLiquid
.method(ElementMatchers.is(CoreReflections.method$SimpleWaterloggedBlock$placeLiquid))
.intercept(MethodDelegation.to(PlaceLiquidInterceptor.INSTANCE))
// canPlaceLiquid
.method(ElementMatchers.is(CoreReflections.method$SimpleWaterloggedBlock$canPlaceLiquid))
.intercept(MethodDelegation.to(CanPlaceLiquidInterceptor.INSTANCE))
// entityInside
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$entityInside))
.intercept(MethodDelegation.to(EntityInsideInterceptor.INSTANCE))
// getSignal
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getSignal))
.intercept(MethodDelegation.to(GetSignalInterceptor.INSTANCE))
// getDirectSignal
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$getDirectSignal))
.intercept(MethodDelegation.to(GetDirectSignalInterceptor.INSTANCE))
// isSignalSource
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$isSignalSource))
.intercept(MethodDelegation.to(IsSignalSourceInterceptor.INSTANCE));
if (CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval != null) {
builder.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$affectNeighborsAfterRemoval))
.intercept(MethodDelegation.to(AffectNeighborsAfterRemovalInterceptor.INSTANCE));
}
Class<?> clazz$CraftEngineBlock = builder.make().load(BlockGenerator.class.getClassLoader()).getLoaded();
constructor$CraftEngineBlock = MethodHandles.publicLookup().in(clazz$CraftEngineBlock)
.findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$BlockBehaviour$Properties))
@@ -175,6 +214,7 @@ public final class BlockGenerator {
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
ChainUpdateBlockIndicator indicator = (ChainUpdateBlockIndicator) thisObj;
// todo chain updater
if (indicator.isNoteBlock()) {
if (CoreReflections.clazz$ServerLevel.isInstance(args[levelIndex])) {
startNoteBlockChain(args);
@@ -254,6 +294,21 @@ public final class BlockGenerator {
}
}
public static class IsPathFindableInterceptor {
public static final IsPathFindableInterceptor INSTANCE = new IsPathFindableInterceptor();
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) throws Exception {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().isPathFindable(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run isPathFindable", e);
return superMethod.call();
}
}
}
public static class MirrorInterceptor {
public static final MirrorInterceptor INSTANCE = new MirrorInterceptor();
@@ -426,4 +481,136 @@ public final class BlockGenerator {
}
}
}
public static class OnExplosionHitInterceptor {
public static final OnExplosionHitInterceptor INSTANCE = new OnExplosionHitInterceptor();
@RuntimeType
public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
holder.value().onExplosionHit(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run onExplosionHit", e);
}
}
}
public static class PickUpBlockInterceptor {
public static final PickUpBlockInterceptor INSTANCE = new PickUpBlockInterceptor();
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().pickupBlock(thisObj, args, () -> CoreReflections.instance$ItemStack$EMPTY);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run pickupBlock", e);
return CoreReflections.instance$ItemStack$EMPTY;
}
}
}
public static class PlaceLiquidInterceptor {
public static final PlaceLiquidInterceptor INSTANCE = new PlaceLiquidInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) throws Exception {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().placeLiquid(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run placeLiquid", e);
return false;
}
}
}
public static class CanPlaceLiquidInterceptor {
public static final CanPlaceLiquidInterceptor INSTANCE = new CanPlaceLiquidInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().canPlaceLiquid(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run canPlaceLiquid", e);
return false;
}
}
}
public static class GetDirectSignalInterceptor {
public static final GetDirectSignalInterceptor INSTANCE = new GetDirectSignalInterceptor();
@RuntimeType
public int intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().getDirectSignal(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run getDirectSignal", e);
return 0;
}
}
}
public static class GetSignalInterceptor {
public static final GetSignalInterceptor INSTANCE = new GetSignalInterceptor();
@RuntimeType
public int intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().getSignal(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run getSignal", e);
return 0;
}
}
}
public static class IsSignalSourceInterceptor {
public static final IsSignalSourceInterceptor INSTANCE = new IsSignalSourceInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().isSignalSource(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run isSignalSource", e);
return false;
}
}
}
public static class AffectNeighborsAfterRemovalInterceptor {
public static final AffectNeighborsAfterRemovalInterceptor INSTANCE = new AffectNeighborsAfterRemovalInterceptor();
@RuntimeType
public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
holder.value().affectNeighborsAfterRemoval(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run affectNeighborsAfterRemoval", e);
}
}
}
public static class EntityInsideInterceptor {
public static final EntityInsideInterceptor INSTANCE = new EntityInsideInterceptor();
@RuntimeType
public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
holder.value().entityInside(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run entityInside", e);
}
}
}
}

View File

@@ -160,6 +160,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, NetworkReflections.clazz$ClientboundEntityEventPacket);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.ROTATE_HEAD, NetworkReflections.clazz$ClientboundRotateHeadPacket);
registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_MOTION, NetworkReflections.clazz$ClientboundSetEntityMotionPacket);
registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket());
registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
@@ -494,11 +496,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
} else {
super.write(context, packet, channelPromise);
}
channelPromise.addListener((p) -> {
for (Runnable task : event.getDelayedTasks()) {
task.run();
}
});
} catch (Throwable e) {
plugin.logger().severe("An error occurred when reading packets. Packet class: " + packet.getClass(), e);
super.write(context, packet, channelPromise);

View File

@@ -10,6 +10,7 @@ import it.unimi.dsi.fastutil.ints.IntList;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslationArgument;
import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
@@ -127,16 +128,18 @@ public class PacketConsumers {
ADD_ENTITY_HANDLERS[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.EGG$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.POTION$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler();
ADD_ENTITY_HANDLERS[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.SMALL_FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.EGG$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.ENDER_PEARL$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.EXPERIENCE_BOTTLE$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.SNOWBALL$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.POTION$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler(false);
ADD_ENTITY_HANDLERS[MEntityTypes.ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false);
ADD_ENTITY_HANDLERS[MEntityTypes.SPECTRAL_ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false);
if (VersionHelper.isOrAbove1_20_5()) {
ADD_ENTITY_HANDLERS[MEntityTypes.OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
}
@@ -186,7 +189,7 @@ public class PacketConsumers {
};
}
private static BukkitNetworkManager.Handlers createOptionalCustomProjectileEntityHandler() {
private static BukkitNetworkManager.Handlers createOptionalCustomProjectileEntityHandler(boolean fallback) {
return (user, event) -> {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
@@ -195,7 +198,9 @@ public class PacketConsumers {
handler.convertAddCustomProjectilePacket(buf, event);
user.entityPacketHandlers().put(id, handler);
}, () -> {
user.entityPacketHandlers().put(id, CommonItemPacketHandler.INSTANCE);
if (fallback) {
user.entityPacketHandlers().put(id, CommonItemPacketHandler.INSTANCE);
}
});
};
}
@@ -1539,9 +1544,14 @@ public class PacketConsumers {
mainThreadTask = () -> {
// todo 冒险模式破坏工具白名单
if (serverPlayer.isAdventureMode() ||
!furniture.isValid() ||
!BukkitCraftEngine.instance().antiGrief().canBreak(platformPlayer, location)
) return;
!furniture.isValid()) return;
FurnitureAttemptBreakEvent preBreakEvent = new FurnitureAttemptBreakEvent(serverPlayer.platformPlayer(), furniture);
if (EventUtils.fireAndCheckCancel(preBreakEvent))
return;
if (!BukkitCraftEngine.instance().antiGrief().canBreak(platformPlayer, location))
return;
FurnitureBreakEvent breakEvent = new FurnitureBreakEvent(serverPlayer.platformPlayer(), furniture);
if (EventUtils.fireAndCheckCancel(breakEvent))
@@ -2102,6 +2112,23 @@ public class PacketConsumers {
FriendlyByteBuf buf = event.getBuffer();
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
if (VersionHelper.isOrAbove1_21_5()) {
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(itemStack);
if (wrapped != null && wrapped.isCustomItem()) {
Object containerMenu = FastNMS.INSTANCE.field$Player$containerMenu(serverPlayer.serverPlayer());
if (containerMenu != null) {
ItemStack carried = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.method$AbstractContainerMenu$getCarried(containerMenu));
if (ItemUtils.isEmpty(carried)) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf);
FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, carried);
return;
}
}
}
}
BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> {
event.setChanged(true);
buf.clear();
@@ -2341,6 +2368,9 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> {
try {
int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
EntityPacketHandler handler = user.entityPacketHandlers().get(entityId);
if (handler != null) {
handler.handleMoveAndRotate(user, event, packet);
@@ -2361,4 +2391,27 @@ public class PacketConsumers {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> ROTATE_HEAD = (user, event, packet) -> {
try {
int entityId = (int) NetworkReflections.methodHandle$ClientboundRotateHeadPacket$entityIdGetter.invokeExact(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundRotateHeadPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SET_ENTITY_MOTION = (user, event, packet) -> {
try {
if (!VersionHelper.isOrAbove1_21_6()) return;
int entityId = (int) NetworkReflections.methodHandle$ClientboundSetEntityMotionPacket$idGetter.invokeExact(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityMotionPacket", e);
}
};
}

View File

@@ -1,11 +1,11 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
import net.momirealms.craftengine.bukkit.entity.projectile.BukkitCustomProjectile;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.injector.ProtectedFieldVisitor;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.core.entity.projectile.CustomProjectile;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
@@ -26,11 +26,11 @@ import java.util.UUID;
public class ProjectilePacketHandler implements EntityPacketHandler {
private final int entityId;
private final CustomProjectile projectile;
private final BukkitCustomProjectile projectile;
private final Object cachedPacket;
private final List<Object> cachedData;
public ProjectilePacketHandler(CustomProjectile projectile, int entityId) {
public ProjectilePacketHandler(BukkitCustomProjectile projectile, int entityId) {
this.projectile = projectile;
this.entityId = entityId;
this.cachedData = createCustomProjectileEntityDataValues();
@@ -111,7 +111,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler {
Optional<CustomItem<ItemStack>> customItem = BukkitItemManager.instance().getCustomItem(this.projectile.metadata().item());
if (customItem.isEmpty()) return itemDisplayValues;
ProjectileMeta meta = this.projectile.metadata();
Item<?> displayedItem = customItem.get().buildItem(ItemBuildContext.EMPTY);
Item<ItemStack> displayedItem = customItem.get().buildItem(ItemBuildContext.EMPTY);
// 我们应当使用新的展示物品的组件覆盖原物品的组件,以完成附魔,附魔光效等组件的继承
displayedItem = this.projectile.item().mergeCopy(displayedItem);
ItemDisplayEntityData.InterpolationDelay.addEntityDataIfNotDefaultValue(-1, itemDisplayValues);
@@ -124,8 +124,13 @@ public class ProjectilePacketHandler implements EntityPacketHandler {
} else {
ItemDisplayEntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues);
}
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(displayedItem.getLiteralObject(), itemDisplayValues);
Object literalItem = displayedItem.getLiteralObject();
BukkitItemManager.instance().s2c(displayedItem.getItem(), null).ifPresentOrElse(
it -> ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(FastNMS.INSTANCE.field$CraftItemStack$handle(it), itemDisplayValues),
() -> ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(literalItem, itemDisplayValues));
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(meta.displayType().id(), itemDisplayValues);
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(meta.billboard().id(), itemDisplayValues);
return itemDisplayValues;
}

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonElement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DynamicOps;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
@@ -200,12 +201,12 @@ public final class CoreReflections {
)
);
public static final Class<?> clazz$Component$Serializer = requireNonNull(
// 1.20~1.21.5
public static final Class<?> clazz$Component$Serializer =
BukkitReflectionUtils.findReobfOrMojmapClass(
"network.chat.IChatBaseComponent$ChatSerializer",
"network.chat.Component$Serializer"
)
);
);
public static final Class<?> clazz$ComponentContents = requireNonNull(
ReflectionUtils.getClazz(
@@ -657,7 +658,7 @@ public final class CoreReflections {
public static final Class<?> clazz$SynchedEntityData$DataValue = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"network.syncher.DataWatcher$b",
VersionHelper.isOrAbove1_20_5() ? "network.syncher.DataWatcher$c" : "network.syncher.DataWatcher$b",
"network.syncher.SynchedEntityData$DataValue"
)
);
@@ -897,11 +898,11 @@ public final class CoreReflections {
);
public static final Field field$BLOCK_STATE_REGISTRY = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$Block, CoreReflections.clazz$IdMapper, 0)
ReflectionUtils.getDeclaredField(clazz$Block, clazz$IdMapper, 0)
);
public static final Method method$IdMapper$add = requireNonNull(
ReflectionUtils.getMethod(CoreReflections.clazz$IdMapper, void.class, Object.class)
ReflectionUtils.getMethod(clazz$IdMapper, void.class, Object.class)
);
public static final Object instance$Block$BLOCK_STATE_REGISTRY;
@@ -1251,6 +1252,10 @@ public final class CoreReflections {
ReflectionUtils.getDeclaredField(clazz$BlockStateBase, float.class, 0)
);
public static final Field field$BlockStateBase$useShapeForLightOcclusion = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$BlockStateBase, boolean.class, 0)
);
public static final Field field$BlockStateBase$burnable = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$BlockStateBase, boolean.class, 2)
);
@@ -1259,10 +1264,20 @@ public final class CoreReflections {
ReflectionUtils.getDeclaredField(clazz$BlockStateBase, boolean.class, 9)
);
// 1.21.2以前用
public static final Field field$BlockStateBase$isConditionallyFullOpaque = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$BlockStateBase, boolean.class, VersionHelper.isOrAbove1_21() ? 10 : 11)
);
// 1.21.2+其他版本在cache里
public static final Field field$BlockStateBase$propagatesSkylightDown = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$BlockStateBase, boolean.class, 11)
);
public static final Field field$BlockStateBase$Cache$propagatesSkylightDown = ReflectionUtils.getDeclaredField(
clazz$BlockStateBase$Cache, boolean.class, 2
);
public static final Field field$BlockStateBase$requiresCorrectToolForDrops = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$BlockStateBase, boolean.class, 5)
);
@@ -1344,16 +1359,48 @@ public final class CoreReflections {
)
);
public static final Class<?> clazz$PathComputationType = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.pathfinder.PathMode",
"world.level.pathfinder.PathComputationType"
)
);
public static final Method method$PathComputationType$values = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$PathComputationType, clazz$PathComputationType.arrayType())
);
public static final Object instance$PathComputationType$LAND;
public static final Object instance$PathComputationType$WATER;
public static final Object instance$PathComputationType$AIR;
static {
try {
Object[] objs = (Object[]) method$PathComputationType$values.invoke(null);
instance$PathComputationType$LAND = objs[0];
instance$PathComputationType$WATER = objs[1];
instance$PathComputationType$AIR = objs[2];
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to initialize PathComputationType", e);
}
}
public static final Method method$BlockBehaviour$isPathFindable = requireNonNull(
VersionHelper.isOrAbove1_20_5() ?
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, clazz$BlockState, clazz$PathComputationType) :
ReflectionUtils.getMethod(clazz$BlockBehaviour, boolean.class, clazz$BlockState, clazz$BlockGetter, clazz$BlockPos, clazz$PathComputationType)
);
public static final Method method$BlockBehaviour$getShape = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getShape", "a"}, clazz$BlockState, clazz$BlockGetter, CoreReflections.clazz$BlockPos, clazz$CollisionContext)
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getShape", "a"}, clazz$BlockState, clazz$BlockGetter, clazz$BlockPos, clazz$CollisionContext)
);
public static final Method method$BlockBehaviour$getCollisionShape = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getCollisionShape", VersionHelper.isOrAbove1_20_3() ? "b" : "c"}, clazz$BlockState, clazz$BlockGetter, CoreReflections.clazz$BlockPos, clazz$CollisionContext)
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getCollisionShape", VersionHelper.isOrAbove1_20_3() ? "b" : "c"}, clazz$BlockState, clazz$BlockGetter, clazz$BlockPos, clazz$CollisionContext)
);
public static final Method method$BlockBehaviour$getBlockSupportShape = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getBlockSupportShape", "b_"}, clazz$BlockState, clazz$BlockGetter, CoreReflections.clazz$BlockPos)
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$VoxelShape, new String[]{"getBlockSupportShape", "b_"}, clazz$BlockState, clazz$BlockGetter, clazz$BlockPos)
);
public static final Field field$BlockBehaviour$properties = requireNonNull(
@@ -1530,7 +1577,7 @@ public final class CoreReflections {
);
public static final Method method$Entity$getOnPos = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$Entity, CoreReflections.clazz$BlockPos, float.class)
ReflectionUtils.getDeclaredMethod(clazz$Entity, clazz$BlockPos, float.class)
);
public static final Class<?> clazz$ItemStack = requireNonNull(
@@ -1560,8 +1607,8 @@ public final class CoreReflections {
public static final Method method$BlockBehaviour$updateShape = requireNonNull(
VersionHelper.isOrAbove1_21_2() ?
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$LevelReader, clazz$ScheduledTickAccess, CoreReflections.clazz$BlockPos, CoreReflections.clazz$Direction, CoreReflections.clazz$BlockPos, clazz$BlockState, clazz$RandomSource) :
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, CoreReflections.clazz$Direction, clazz$BlockState, clazz$LevelAccessor, CoreReflections.clazz$BlockPos, CoreReflections.clazz$BlockPos)
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$LevelReader, clazz$ScheduledTickAccess, clazz$BlockPos, clazz$Direction, clazz$BlockPos, clazz$BlockState, clazz$RandomSource) :
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$Direction, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockPos)
);
public static final Class<?> clazz$Fallable = requireNonNull(
@@ -1587,7 +1634,7 @@ public final class CoreReflections {
);
public static final Method method$FallingBlockEntity$fall = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$FallingBlockEntity, clazz$FallingBlockEntity, clazz$Level, CoreReflections.clazz$BlockPos, clazz$BlockState)
ReflectionUtils.getStaticMethod(clazz$FallingBlockEntity, clazz$FallingBlockEntity, clazz$Level, clazz$BlockPos, clazz$BlockState)
);
public static final Method method$FallingBlockEntity$setHurtsEntities = requireNonNull(
@@ -1615,11 +1662,11 @@ public final class CoreReflections {
);
public static final Method method$BlockStateBase$hasTag = requireNonNull(
ReflectionUtils.getMethod(clazz$BlockStateBase, boolean.class, CoreReflections.clazz$TagKey)
ReflectionUtils.getMethod(clazz$BlockStateBase, boolean.class, clazz$TagKey)
);
public static final Method method$Level$removeBlock = requireNonNull(
ReflectionUtils.getMethod(clazz$Level, boolean.class, CoreReflections.clazz$BlockPos, boolean.class)
ReflectionUtils.getMethod(clazz$Level, boolean.class, clazz$BlockPos, boolean.class)
);
public static final Class<?> clazz$LeavesBlock = requireNonNull(
@@ -1657,18 +1704,18 @@ public final class CoreReflections {
public static final Method method$Block$updateFromNeighbourShapes = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$Block, clazz$BlockState, clazz$BlockState, clazz$LevelAccessor, CoreReflections.clazz$BlockPos)
ReflectionUtils.getStaticMethod(clazz$Block, clazz$BlockState, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos)
);
public static final Method method$BlockStateBase$updateNeighbourShapes = requireNonNull(
ReflectionUtils.getMethod(
// flags // depth
clazz$BlockStateBase, void.class, clazz$LevelAccessor, CoreReflections.clazz$BlockPos, int.class, int.class
clazz$BlockStateBase, void.class, clazz$LevelAccessor, clazz$BlockPos, int.class, int.class
)
);
public static final Method method$BlockState$getShape = requireNonNull(
ReflectionUtils.getMethod(clazz$BlockStateBase, clazz$VoxelShape, new String[]{"getShape", "a"}, clazz$BlockGetter, CoreReflections.clazz$BlockPos, clazz$CollisionContext)
ReflectionUtils.getMethod(clazz$BlockStateBase, clazz$VoxelShape, new String[]{"getShape", "a"}, clazz$BlockGetter, clazz$BlockPos, clazz$CollisionContext)
);
public static final Method method$VoxelShape$isEmpty = requireNonNull(
@@ -1680,7 +1727,7 @@ public final class CoreReflections {
);
public static final Method method$LevelWriter$setBlock = requireNonNull(
ReflectionUtils.getMethod(clazz$LevelWriter, boolean.class, CoreReflections.clazz$BlockPos, clazz$BlockState, int.class)
ReflectionUtils.getMethod(clazz$LevelWriter, boolean.class, clazz$BlockPos, clazz$BlockState, int.class)
);
public static final Method method$CollisionContext$of = requireNonNull(
@@ -1692,15 +1739,15 @@ public final class CoreReflections {
);
public static final Method method$BlockStateBase$canSurvive = requireNonNull(
ReflectionUtils.getMethod(clazz$BlockStateBase, boolean.class, clazz$LevelReader, CoreReflections.clazz$BlockPos)
ReflectionUtils.getMethod(clazz$BlockStateBase, boolean.class, clazz$LevelReader, clazz$BlockPos)
);
public static final Method method$BlockStateBase$onPlace = requireNonNull(
ReflectionUtils.getMethod(clazz$BlockStateBase, void.class, clazz$Level, CoreReflections.clazz$BlockPos, clazz$BlockState, boolean.class)
ReflectionUtils.getMethod(clazz$BlockStateBase, void.class, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class)
);
public static final Method method$ItemStack$isTag = requireNonNull(
ReflectionUtils.getMethod(clazz$ItemStack, boolean.class, CoreReflections.clazz$TagKey)
ReflectionUtils.getMethod(clazz$ItemStack, boolean.class, clazz$TagKey)
);
public static final Class<?> clazz$FireBlock = requireNonNull(
@@ -1769,10 +1816,6 @@ public final class CoreReflections {
ReflectionUtils.getMethod(clazz$FlowingFluid, clazz$FluidState, boolean.class)
);
public static final Method method$Level$getFluidState = requireNonNull(
ReflectionUtils.getMethod(clazz$Level, clazz$FluidState, CoreReflections.clazz$BlockPos)
);
public static final Method method$FluidState$isSource = requireNonNull(
ReflectionUtils.getMethod(clazz$FluidState, boolean.class, new String[]{"isSource", "b"})
);
@@ -1824,7 +1867,7 @@ public final class CoreReflections {
);
public static final Field field$Inventory$items = requireNonNull(
ReflectionUtils.getInstanceDeclaredField(clazz$Inventory, CoreReflections.clazz$NonNullList, 0)
ReflectionUtils.getInstanceDeclaredField(clazz$Inventory, clazz$NonNullList, 0)
);
public static final Class<?> clazz$Ingredient = requireNonNull(
@@ -1863,7 +1906,7 @@ public final class CoreReflections {
// 1.20.1-1.20.2
public static final Field field$1_20_1$ShapedRecipe$recipeItems=
ReflectionUtils.getDeclaredField(clazz$ShapedRecipe, CoreReflections.clazz$NonNullList, 0);
ReflectionUtils.getDeclaredField(clazz$ShapedRecipe, clazz$NonNullList, 0);
// 1.20.3+
public static final Field field$1_20_3$ShapedRecipe$pattern=
@@ -1871,7 +1914,7 @@ public final class CoreReflections {
// 1.20.3-1.21.1
public static final Field field$ShapedRecipePattern$ingredients1_20_3 = Optional.ofNullable(clazz$ShapedRecipePattern)
.map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$NonNullList, 0))
.map(it -> ReflectionUtils.getDeclaredField(it, clazz$NonNullList, 0))
.orElse(null);
// 1.21.2+
@@ -1924,7 +1967,7 @@ public final class CoreReflections {
public static final Field field$ShapelessRecipe$ingredients =
Optional.ofNullable(ReflectionUtils.getDeclaredField(clazz$ShapelessRecipe, List.class, 0))
.orElse(ReflectionUtils.getDeclaredField(clazz$ShapelessRecipe, CoreReflections.clazz$NonNullList, 0));
.orElse(ReflectionUtils.getDeclaredField(clazz$ShapelessRecipe, clazz$NonNullList, 0));
// require ResourceLocation for 1.20.1-1.21.1
// require ResourceKey for 1.21.2+
@@ -2052,7 +2095,7 @@ public final class CoreReflections {
.orElse(null);
public static final Field field$AbstractFurnaceBlockEntity$items = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$AbstractFurnaceBlockEntity, CoreReflections.clazz$NonNullList, 0)
ReflectionUtils.getDeclaredField(clazz$AbstractFurnaceBlockEntity, clazz$NonNullList, 0)
);
public static final Class<?> clazz$SimpleContainer = requireNonNull(
@@ -2063,15 +2106,15 @@ public final class CoreReflections {
);
public static final Field field$SimpleContainer$items = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$SimpleContainer, CoreReflections.clazz$NonNullList, 0)
ReflectionUtils.getDeclaredField(clazz$SimpleContainer, clazz$NonNullList, 0)
);
public static final Method method$LevelReader$getMaxLocalRawBrightness = requireNonNull(
ReflectionUtils.getMethod(clazz$LevelReader, int.class, CoreReflections.clazz$BlockPos)
ReflectionUtils.getMethod(clazz$LevelReader, int.class, clazz$BlockPos)
);
public static final Method method$ConfiguredFeature$place = requireNonNull(
ReflectionUtils.getMethod(clazz$ConfiguredFeature, boolean.class, clazz$WorldGenLevel, clazz$ChunkGenerator, clazz$RandomSource, CoreReflections.clazz$BlockPos)
ReflectionUtils.getMethod(clazz$ConfiguredFeature, boolean.class, clazz$WorldGenLevel, clazz$ChunkGenerator, clazz$RandomSource, clazz$BlockPos)
);
public static final Class<?> clazz$BonemealableBlock = requireNonNull(
@@ -2083,12 +2126,12 @@ public final class CoreReflections {
public static final Method method$BonemealableBlock$isValidBonemealTarget = requireNonNull(
VersionHelper.isOrAbove1_20_2() ?
ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, CoreReflections.clazz$BlockPos, clazz$BlockState) :
ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, CoreReflections.clazz$BlockPos, clazz$BlockState, boolean.class)
ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState) :
ReflectionUtils.getInstanceMethod(clazz$BonemealableBlock, boolean.class, clazz$LevelReader, clazz$BlockPos, clazz$BlockState, boolean.class)
);
public static final Method method$BonemealableBlock$isBonemealSuccess = requireNonNull(
ReflectionUtils.getMethod(clazz$BonemealableBlock, boolean.class, clazz$Level, clazz$RandomSource, CoreReflections.clazz$BlockPos, clazz$BlockState)
ReflectionUtils.getMethod(clazz$BonemealableBlock, boolean.class, clazz$Level, clazz$RandomSource, clazz$BlockPos, clazz$BlockState)
);
public static final Method method$PalettedContainer$getAndSet = Objects.requireNonNull(
@@ -2138,11 +2181,11 @@ public final class CoreReflections {
);
public static final Constructor<?> constructor$JukeboxSong = Optional.ofNullable(clazz$JukeboxSong)
.map(it -> ReflectionUtils.getConstructor(it, CoreReflections.clazz$Holder, clazz$Component, float.class, int.class))
.map(it -> ReflectionUtils.getConstructor(it, clazz$Holder, clazz$Component, float.class, int.class))
.orElse(null);
public static final Field field$JukeboxSong$soundEvent = Optional.ofNullable(clazz$JukeboxSong)
.map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$Holder, 0))
.map(it -> ReflectionUtils.getDeclaredField(it, clazz$Holder, 0))
.orElse(null);
public static final Field field$JukeboxSong$description = Optional.ofNullable(clazz$JukeboxSong)
@@ -2157,10 +2200,6 @@ public final class CoreReflections {
.map(it -> ReflectionUtils.getDeclaredField(it, int.class, 0))
.orElse(null);
public static final Method method$FluidState$getType = requireNonNull(
ReflectionUtils.getMethod(clazz$FluidState, clazz$Fluid)
);
public static final Class<?> clazz$CustomRecipe = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.crafting.IRecipeComplex",
@@ -2259,15 +2298,15 @@ public final class CoreReflections {
);
public static final Method method$BlockHitResult$withPosition = requireNonNull(
ReflectionUtils.getMethod(clazz$BlockHitResult, clazz$BlockHitResult, CoreReflections.clazz$BlockPos)
ReflectionUtils.getMethod(clazz$BlockHitResult, clazz$BlockHitResult, clazz$BlockPos)
);
public static final Field field$BlockHitResul$blockPos = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$BlockHitResult, CoreReflections.clazz$BlockPos, 0)
ReflectionUtils.getDeclaredField(clazz$BlockHitResult, clazz$BlockPos, 0)
);
public static final Field field$BlockHitResul$direction = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$BlockHitResult, CoreReflections.clazz$Direction, 0)
ReflectionUtils.getDeclaredField(clazz$BlockHitResult, clazz$Direction, 0)
);
public static final Field field$BlockHitResul$miss = requireNonNull(
@@ -2298,22 +2337,22 @@ public final class CoreReflections {
public static final Method method$SimpleWaterloggedBlock$canPlaceLiquid = requireNonNull(
VersionHelper.isOrAbove1_21_5()
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$LivingEntity, clazz$BlockGetter, CoreReflections.clazz$BlockPos, clazz$BlockState, clazz$Fluid)
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$LivingEntity, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid)
: VersionHelper.isOrAbove1_20_2()
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$Player, clazz$BlockGetter, CoreReflections.clazz$BlockPos, clazz$BlockState, clazz$Fluid)
: ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$BlockGetter, CoreReflections.clazz$BlockPos, clazz$BlockState, clazz$Fluid)
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid)
: ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$BlockGetter, clazz$BlockPos, clazz$BlockState, clazz$Fluid)
);
public static final Method method$SimpleWaterloggedBlock$placeLiquid = requireNonNull(
ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$LevelAccessor, CoreReflections.clazz$BlockPos, clazz$BlockState, clazz$FluidState)
ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, boolean.class, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState, clazz$FluidState)
);
public static final Method method$SimpleWaterloggedBlock$pickupBlock = requireNonNull(
VersionHelper.isOrAbove1_21_5()
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LivingEntity, clazz$LevelAccessor, CoreReflections.clazz$BlockPos, clazz$BlockState)
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LivingEntity, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState)
: VersionHelper.isOrAbove1_20_2()
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$Player, clazz$LevelAccessor, CoreReflections.clazz$BlockPos, clazz$BlockState)
: ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LevelAccessor, CoreReflections.clazz$BlockPos, clazz$BlockState)
? ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$Player, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState)
: ReflectionUtils.getMethod(clazz$SimpleWaterloggedBlock, clazz$ItemStack, clazz$LevelAccessor, clazz$BlockPos, clazz$BlockState)
);
public static final Method method$Fluid$getTickDelay = requireNonNull(
@@ -2409,7 +2448,7 @@ public final class CoreReflections {
}
public static final Method method$BlockStateBase$isFaceSturdy = requireNonNull(
ReflectionUtils.getMethod(clazz$BlockStateBase, boolean.class, clazz$BlockGetter, CoreReflections.clazz$BlockPos, CoreReflections.clazz$Direction, clazz$SupportType)
ReflectionUtils.getMethod(clazz$BlockStateBase, boolean.class, clazz$BlockGetter, clazz$BlockPos, clazz$Direction, clazz$SupportType)
);
public static final Class<?> clazz$BlockInWorld = requireNonNull(
@@ -2441,7 +2480,7 @@ public final class CoreReflections {
);
public static final Method method$BlockAndTintGetter$getRawBrightness = requireNonNull(
ReflectionUtils.getMethod(clazz$BlockAndTintGetter, int.class, CoreReflections.clazz$BlockPos, int.class)
ReflectionUtils.getMethod(clazz$BlockAndTintGetter, int.class, clazz$BlockPos, int.class)
);
public static final Field field$Entity$boundingBox = requireNonNull(
@@ -2527,36 +2566,12 @@ public final class CoreReflections {
// 1.20.5+
public static final Constructor<?> constructor$AttributeInstance =
ReflectionUtils.getConstructor(clazz$AttributeInstance, CoreReflections.clazz$Holder, Consumer.class);
ReflectionUtils.getConstructor(clazz$AttributeInstance, clazz$Holder, Consumer.class);
public static final Method method$AttributeInstance$setBaseValue = requireNonNull(
ReflectionUtils.getMethod(clazz$AttributeInstance, void.class, double.class)
);
public static final Method method$Entity$canBeCollidedWith = requireNonNull(
VersionHelper.isOrAbove1_20_5()
? ReflectionUtils.getMethod(clazz$Entity, boolean.class, new String[]{"canBeCollidedWith"})
: VersionHelper.isOrAbove1_20_3()
? ReflectionUtils.getMethod(clazz$Entity, boolean.class, new String[]{"bz"})
: VersionHelper.isOrAbove1_20_2()
? ReflectionUtils.getMethod(clazz$Entity, boolean.class, new String[]{"bx"})
: VersionHelper.isOrAbove1_20()
? ReflectionUtils.getMethod(clazz$Entity, boolean.class, new String[]{"bu"})
: ReflectionUtils.getMethod(clazz$Entity, boolean.class, new String[]{"canBeCollidedWith", "bu", "bx", "bz"})
);
public static final Method method$Entity$getId = requireNonNull(
VersionHelper.isOrAbove1_20_5()
? ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"getId"})
: VersionHelper.isOrAbove1_20_3()
? ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"aj"})
: VersionHelper.isOrAbove1_20_2()
? ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"ah"})
: VersionHelper.isOrAbove1_20()
? ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"af"})
: ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"getId", "aj", "ah", "af"})
);
public static final Class<?> clazz$Rotation = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.EnumBlockRotation",
@@ -2715,9 +2730,9 @@ public final class CoreReflections {
public static final Method method$BlockBehaviour$neighborChanged = requireNonNull(
VersionHelper.isOrAbove1_21_2() ?
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, CoreReflections.clazz$BlockPos, clazz$Block, clazz$Orientation, boolean.class) :
Optional.ofNullable(ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, CoreReflections.clazz$BlockPos, clazz$Block, CoreReflections.clazz$BlockPos, boolean.class))
.orElse(ReflectionUtils.getMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, CoreReflections.clazz$BlockPos, clazz$Block, CoreReflections.clazz$BlockPos, boolean.class))
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Block, clazz$Orientation, boolean.class) :
Optional.ofNullable(ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Block, clazz$BlockPos, boolean.class))
.orElse(ReflectionUtils.getMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Block, clazz$BlockPos, boolean.class))
);
public static final Class<?> clazz$InventoryMenu = requireNonNull(
@@ -2769,7 +2784,7 @@ public final class CoreReflections {
);
public static final Method method$ServerLevel$getNoiseBiome = requireNonNull(
ReflectionUtils.getMethod(clazz$ServerLevel, CoreReflections.clazz$Holder, int.class, int.class, int.class)
ReflectionUtils.getMethod(clazz$ServerLevel, clazz$Holder, int.class, int.class, int.class)
);
public static final Class<?> clazz$MinecraftServer = requireNonNull(
@@ -2781,7 +2796,7 @@ public final class CoreReflections {
);
public static final Field field$MinecraftServer$registries = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$MinecraftServer, CoreReflections.clazz$LayeredRegistryAccess, 0)
ReflectionUtils.getDeclaredField(clazz$MinecraftServer, clazz$LayeredRegistryAccess, 0)
);
public static final Class<?> clazz$ServerConnectionListener = requireNonNull(
@@ -2878,7 +2893,7 @@ public final class CoreReflections {
public static final Method method$ServerPlayer$getAttribute = requireNonNull(
VersionHelper.isOrAbove1_20_5() ?
ReflectionUtils.getMethod(clazz$ServerPlayer, clazz$AttributeInstance, CoreReflections.clazz$Holder) :
ReflectionUtils.getMethod(clazz$ServerPlayer, clazz$AttributeInstance, clazz$Holder) :
ReflectionUtils.getMethod(clazz$ServerPlayer, clazz$AttributeInstance, clazz$Attribute)
);
@@ -2915,13 +2930,13 @@ public final class CoreReflections {
);
public static final Method method$ServerPlayerGameMode$destroyBlock = requireNonNull(
ReflectionUtils.getMethod(clazz$ServerPlayerGameMode, boolean.class, CoreReflections.clazz$BlockPos)
ReflectionUtils.getMethod(clazz$ServerPlayerGameMode, boolean.class, clazz$BlockPos)
);
public static final Method method$ServerPlayer$getEffect = requireNonNull(
!VersionHelper.isOrAbove1_20_5() ?
ReflectionUtils.getMethod(clazz$ServerPlayer, clazz$MobEffectInstance, clazz$MobEffect) :
ReflectionUtils.getMethod(clazz$ServerPlayer, clazz$MobEffectInstance, CoreReflections.clazz$Holder)
ReflectionUtils.getMethod(clazz$ServerPlayer, clazz$MobEffectInstance, clazz$Holder)
);
public static final Field field$ServerLevel$uuid = requireNonNull(
@@ -2929,7 +2944,7 @@ public final class CoreReflections {
);
public static final Method method$ServerLevel$checkEntityCollision = requireNonNull(
ReflectionUtils.getMethod(clazz$ServerLevel, boolean.class, clazz$BlockState, clazz$Entity, clazz$CollisionContext, CoreReflections.clazz$BlockPos, boolean.class)
ReflectionUtils.getMethod(clazz$ServerLevel, boolean.class, clazz$BlockState, clazz$Entity, clazz$CollisionContext, clazz$BlockPos, boolean.class)
);
public static final Class<?> clazz$ResourceManager = requireNonNull(
@@ -3046,19 +3061,19 @@ public final class CoreReflections {
public static final Method method$ServerLevel$sendBlockUpdated = requireNonNull(
ReflectionUtils.getMethod(
clazz$ServerLevel, void.class, CoreReflections.clazz$BlockPos, clazz$BlockState, clazz$BlockState, int.class
clazz$ServerLevel, void.class, clazz$BlockPos, clazz$BlockState, clazz$BlockState, int.class
)
);
public static final Method method$ServerLevel$levelEvent = requireNonNull(
VersionHelper.isOrAbove1_21_5()
? ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Entity, int.class, CoreReflections.clazz$BlockPos, int.class)
: ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Player, int.class, CoreReflections.clazz$BlockPos, int.class)
? ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Entity, int.class, clazz$BlockPos, int.class)
: ReflectionUtils.getMethod(clazz$ServerLevel, void.class, clazz$Player, int.class, clazz$BlockPos, int.class)
);
public static final Method method$ServerGamePacketListenerImpl$tryPickItem =
VersionHelper.isOrAbove1_21_5() ?
ReflectionUtils.getDeclaredMethod(clazz$ServerGamePacketListenerImpl, void.class, clazz$ItemStack, CoreReflections.clazz$BlockPos, clazz$Entity, boolean.class) :
ReflectionUtils.getDeclaredMethod(clazz$ServerGamePacketListenerImpl, void.class, clazz$ItemStack, clazz$BlockPos, clazz$Entity, boolean.class) :
ReflectionUtils.getDeclaredMethod(clazz$ServerGamePacketListenerImpl, void.class, clazz$ItemStack);
public static final Method method$ServerPlayer$nextContainerCounter = requireNonNull(
@@ -3201,15 +3216,45 @@ public final class CoreReflections {
);
public static final Method method$BonemealableBlock$performBonemeal = requireNonNull(
ReflectionUtils.getMethod(clazz$BonemealableBlock, void.class, clazz$ServerLevel, clazz$RandomSource, CoreReflections.clazz$BlockPos, clazz$BlockState)
ReflectionUtils.getMethod(clazz$BonemealableBlock, void.class, clazz$ServerLevel, clazz$RandomSource, clazz$BlockPos, clazz$BlockState)
);
public static final Method method$BlockBehaviour$tick = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"tick", "a"}, clazz$BlockState, clazz$ServerLevel, CoreReflections.clazz$BlockPos, clazz$RandomSource)
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"tick", "a"}, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$RandomSource)
);
public static final Method method$BlockBehaviour$randomTick = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"randomTick", "b"}, clazz$BlockState, clazz$ServerLevel, CoreReflections.clazz$BlockPos, clazz$RandomSource)
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"randomTick", "b"}, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, clazz$RandomSource)
);
public static final Class<?> clazz$InsideBlockEffectApplier = BukkitReflectionUtils.findReobfOrMojmapClass(
"world.entity.InsideBlockEffectApplier",
"world.entity.InsideBlockEffectApplier"
);
public static final Method method$BlockBehaviour$entityInside = requireNonNull(
VersionHelper.isOrAbove1_21_5() ?
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"entityInside", "a"}, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Entity, clazz$InsideBlockEffectApplier) :
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"entityInside", "a"}, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Entity)
);
// 1.21.5+
public static final Method method$BlockBehaviour$affectNeighborsAfterRemoval = ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, new String[]{"affectNeighborsAfterRemoval", "a"}, clazz$BlockState, clazz$ServerLevel, clazz$BlockPos, boolean.class);
public static final Method method$BlockBehaviour$getSignal = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, int.class, new String[]{"getSignal", "a"}, clazz$BlockState, clazz$BlockGetter, clazz$BlockPos, clazz$Direction)
);
public static final Method method$BlockBehaviour$getDirectSignal = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, int.class, new String[]{"getDirectSignal", "b"}, clazz$BlockState, clazz$BlockGetter, clazz$BlockPos, clazz$Direction)
);
public static final Method method$BlockBehaviour$isSignalSource = requireNonNull(
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, boolean.class, new String[]{
"isSignalSource",
!VersionHelper.isOrAbove1_20_5() ? "f_" : // 1.20.1-1.20.4
!VersionHelper.isOrAbove1_21_2() ? "e_" /* 1.20.5-1.21.1 */ : "f_" // 1.21.2+
}, clazz$BlockState)
);
public static final Method method$FileToIdConverter$listMatchingResources = requireNonNull(
@@ -3217,7 +3262,7 @@ public final class CoreReflections {
);
public static final Method method$RegistryOps$create = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$RegistryOps, clazz$RegistryOps, DynamicOps.class, CoreReflections.clazz$HolderLookup$Provider)
ReflectionUtils.getStaticMethod(clazz$RegistryOps, clazz$RegistryOps, DynamicOps.class, clazz$HolderLookup$Provider)
);
public static final Method method$DefaultedRegistry$get = requireNonNull(
@@ -3274,6 +3319,7 @@ public final class CoreReflections {
public static final MethodHandle methodHandle$ServerEntity$broadcastSetter;
public static final MethodHandle methodHandle$ServerEntity$updateIntervalSetter;
public static final MethodHandle methodHandle$ServerPlayer$connectionGetter;
public static final MethodHandle methodHandle$ServerPlayer$getAttributeMethod;
static {
try {
@@ -3289,6 +3335,10 @@ public final class CoreReflections {
ReflectionUtils.unreflectGetter(field$ServerPlayer$connection)
.asType(MethodType.methodType(Object.class, Object.class))
);
methodHandle$ServerPlayer$getAttributeMethod = requireNonNull(
ReflectionUtils.unreflectMethod(method$ServerPlayer$getAttribute)
.asType(MethodType.methodType(Object.class, Object.class, Object.class))
);
} catch (IllegalAccessException e) {
throw new ReflectionInitException("Failed to initialize reflection", e);
}
@@ -3308,4 +3358,149 @@ public final class CoreReflections {
public static final Field field$FireBlock$igniteOdds = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$FireBlock, Object2IntMap.class, 0)
);
public static final Class<?> clazz$EnchantmentMenu = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.inventory.ContainerEnchantTable",
"world.inventory.EnchantmentMenu"
)
);
public static final Class<?> clazz$RedStoneWireBlock = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.BlockRedstoneWire",
"world.level.block.RedStoneWireBlock"
)
);
public static final Class<?> clazz$Explosion = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.Explosion")
)
);
// 1.20.5+
public static final Field field$ItemStack$CODEC = ReflectionUtils.getDeclaredField(clazz$ItemStack, "CODEC", "b");
public static final Codec<?> instance$ItemStack$CODEC;
static {
try {
instance$ItemStack$CODEC = VersionHelper.isOrAbove1_20_5() ? (Codec<?>) field$ItemStack$CODEC.get(null) : null;
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init ItemStack$CODEC", e);
}
}
public static final Class<?> clazz$StairBlock = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.BlockStairs",
"world.level.block.StairBlock"
)
);
public static final Class<?> clazz$StairsShape = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.state.properties.BlockPropertyStairsShape",
"world.level.block.state.properties.StairsShape"
)
);
public static final Method method$StairsShape$values = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$StairsShape, clazz$StairsShape.arrayType())
);
public static final Method method$StairsShape$ordinal = requireNonNull(
ReflectionUtils.getMethod(
clazz$StairsShape, new String[]{"ordinal"}
)
);
public static final Object instance$StairsShape$STRAIGHT;
public static final Object instance$StairsShape$INNER_LEFT;
public static final Object instance$StairsShape$INNER_RIGHT;
public static final Object instance$StairsShape$OUTER_LEFT;
public static final Object instance$StairsShape$OUTER_RIGHT;
static {
try {
Object[] values = (Object[]) method$StairsShape$values.invoke(null);
instance$StairsShape$STRAIGHT = values[0];
instance$StairsShape$INNER_LEFT = values[1];
instance$StairsShape$INNER_RIGHT = values[2];
instance$StairsShape$OUTER_LEFT = values[3];
instance$StairsShape$OUTER_RIGHT = values[4];
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static final Class<?> clazz$EnumProperty = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.state.properties.BlockStateEnum",
"world.level.block.state.properties.EnumProperty"
)
);
// 1.20~1.21.1
public static final Class<?> clazz$DirectionProperty =
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.state.properties.BlockStateDirection",
"world.level.block.state.properties.DirectionProperty"
);
public static final Field field$StairBlock$FACING = requireNonNull(
VersionHelper.isOrAbove1_21_2()
? ReflectionUtils.getDeclaredField(clazz$StairBlock, clazz$EnumProperty, 0)
: ReflectionUtils.getDeclaredField(clazz$StairBlock, clazz$DirectionProperty, 0)
);
public static final Field field$StairBlock$HALF = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$StairBlock, clazz$EnumProperty, VersionHelper.isOrAbove1_21_2() ? 1 : 0
)
);
public static final Field field$StairBlock$SHAPE = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$StairBlock, clazz$EnumProperty, VersionHelper.isOrAbove1_21_2() ? 2 : 1
)
);
public static final Object instance$StairBlock$FACING;
public static final Object instance$StairBlock$HALF;
public static final Object instance$StairBlock$SHAPE;
static {
try {
instance$StairBlock$FACING = requireNonNull(field$StairBlock$FACING.get(null));
instance$StairBlock$HALF = requireNonNull(field$StairBlock$HALF.get(null));
instance$StairBlock$SHAPE = requireNonNull(field$StairBlock$SHAPE.get(null));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static final Class<?> clazz$BasePressurePlateBlock = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.BlockPressurePlateAbstract",
"world.level.block.BasePressurePlateBlock"
)
);
public static final Field field$BasePressurePlateBlock$TOUCH_AABB = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$BasePressurePlateBlock, clazz$AABB, 0
)
);
public static final Object instance$BasePressurePlateBlock$TOUCH_AABB;
static {
try {
instance$BasePressurePlateBlock$TOUCH_AABB = requireNonNull(field$BasePressurePlateBlock$TOUCH_AABB.get(null));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -25,6 +25,10 @@ public final class MEntityTypes {
public static final int OAK_BOAT$registryId;
public static final Object TRIDENT;
public static final int TRIDENT$registryId;
public static final Object ARROW;
public static final int ARROW$registryId;
public static final Object SPECTRAL_ARROW;
public static final int SPECTRAL_ARROW$registryId;
public static final Object SNOWBALL;
public static final int SNOWBALL$registryId;
public static final Object FIREBALL;
@@ -51,6 +55,10 @@ public final class MEntityTypes {
public static final int EXPERIENCE_BOTTLE$registryId;
public static final Object POTION;
public static final int POTION$registryId;
public static final Object HAPPY_GHAST;
public static final int HAPPY_GHAST$registryId;
public static final Object PLAYER;
public static final int PLAYER$registryId;
private static Object getById(String id) throws ReflectiveOperationException {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -108,6 +116,14 @@ public final class MEntityTypes {
POTION$registryId = getRegistryId(POTION);
OMINOUS_ITEM_SPAWNER = VersionHelper.isOrAbove1_20_5() ? getById("ominous_item_spawner") : null;
OMINOUS_ITEM_SPAWNER$registryId = getRegistryId(OMINOUS_ITEM_SPAWNER);
HAPPY_GHAST = VersionHelper.isOrAbove1_21_6() ? getById("happy_ghast") : null;
HAPPY_GHAST$registryId = getRegistryId(HAPPY_GHAST);
PLAYER = getById("player");
PLAYER$registryId = getRegistryId(PLAYER);
ARROW = getById("arrow");
ARROW$registryId = getRegistryId(ARROW);
SPECTRAL_ARROW = getById("spectral_arrow");
SPECTRAL_ARROW$registryId = getRegistryId(SPECTRAL_ARROW);
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init EntityTypes", e);
}

View File

@@ -6,12 +6,12 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitExcepti
public final class MFluids {
private MFluids() {}
public static final Object instance$Fluids$WATER;
public static final Object instance$Fluids$FLOWING_WATER;
public static final Object instance$Fluids$LAVA;
public static final Object instance$Fluids$FLOWING_LAVA;
public static final Object instance$Fluids$EMPTY;
public static final Object instance$Fluids$EMPTY$defaultState;
public static final Object WATER;
public static final Object FLOWING_WATER;
public static final Object LAVA;
public static final Object FLOWING_LAVA;
public static final Object EMPTY;
public static final Object EMPTY$defaultState;
private static Object getById(String id) throws ReflectiveOperationException {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -20,12 +20,12 @@ public final class MFluids {
static {
try {
instance$Fluids$WATER = getById("water");
instance$Fluids$FLOWING_WATER = getById("flowing_water");
instance$Fluids$LAVA = getById("lava");
instance$Fluids$FLOWING_LAVA = getById("flowing_lava");
instance$Fluids$EMPTY = getById("empty");
instance$Fluids$EMPTY$defaultState = CoreReflections.method$Fluid$defaultFluidState.invoke(instance$Fluids$EMPTY);
WATER = getById("water");
FLOWING_WATER = getById("flowing_water");
LAVA = getById("lava");
FLOWING_LAVA = getById("flowing_lava");
EMPTY = getById("empty");
EMPTY$defaultState = CoreReflections.method$Fluid$defaultFluidState.invoke(EMPTY);
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init Fluids", e);
}

View File

@@ -7,7 +7,6 @@ public final class MItems {
private MItems() {}
public static final Object AIR;
public static final Object Air$ItemStack;
public static final Object WATER_BUCKET;
private static Object getById(String id) throws ReflectiveOperationException {
@@ -18,7 +17,6 @@ public final class MItems {
static {
try {
AIR = getById("air");
Air$ItemStack = CoreReflections.constructor$ItemStack.newInstance(AIR);
WATER_BUCKET = getById("water_bucket");
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init Items", e);

View File

@@ -16,7 +16,7 @@ import net.momirealms.sparrow.nbt.codec.NBTOps;
import static java.util.Objects.requireNonNull;
@SuppressWarnings("unchecked")
public class MRegistryOps {
public final class MRegistryOps {
public static final DynamicOps<Object> NBT;
public static final DynamicOps<Tag> SPARROW_NBT;
public static final DynamicOps<Object> JAVA;

View File

@@ -6,11 +6,11 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitExcepti
public final class MSoundEvents {
private MSoundEvents() {}
public static final Object instance$SoundEvent$EMPTY;
public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_1;
public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_2;
public static final Object instance$SoundEvent$TRIDENT_RIPTIDE_3;
public static final Object instance$SoundEvent$TRIDENT_THROW;
public static final Object EMPTY;
public static final Object TRIDENT_RIPTIDE_1;
public static final Object TRIDENT_RIPTIDE_2;
public static final Object TRIDENT_RIPTIDE_3;
public static final Object TRIDENT_THROW;
private static Object getById(String id) throws ReflectiveOperationException {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -19,11 +19,11 @@ public final class MSoundEvents {
static {
try {
instance$SoundEvent$EMPTY = getById("intentionally_empty");
instance$SoundEvent$TRIDENT_RIPTIDE_1 = getById("item.trident_riptide_1");
instance$SoundEvent$TRIDENT_RIPTIDE_2 = getById("item.trident_riptide_2");
instance$SoundEvent$TRIDENT_RIPTIDE_3 = getById("item.trident.riptide_3");
instance$SoundEvent$TRIDENT_THROW = getById("item.trident.throw");
EMPTY = getById("intentionally_empty");
TRIDENT_RIPTIDE_1 = getById("item.trident_riptide_1");
TRIDENT_RIPTIDE_2 = getById("item.trident_riptide_2");
TRIDENT_RIPTIDE_3 = getById("item.trident.riptide_3");
TRIDENT_THROW = getById("item.trident.throw");
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to init SoundEvents", e);
}

View File

@@ -0,0 +1,18 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import java.util.Objects;
public final class MTagKeys {
private MTagKeys() {}
public static final Object Item$WOOL = create(MRegistries.ITEM, "wool");
public static final Object Block$WALLS = create(MRegistries.BLOCK, "walls");
private static Object create(Object registry, String location) {
Object resourceLocation = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", location);
Object tagKey = FastNMS.INSTANCE.method$TagKey$create(registry, resourceLocation);
return Objects.requireNonNull(tagKey);
}
}

View File

@@ -1329,6 +1329,9 @@ public final class NetworkReflections {
public static final MethodHandle methodHandle$ServerboundPickItemFromBlockPacket$posGetter;
public static final MethodHandle methodHandle$ServerboundPickItemFromEntityPacket$idGetter;
public static final MethodHandle methodHandle$ServerboundCustomPayloadPacket$payloadGetter;
public static final MethodHandle methodHandle$ClientboundRotateHeadPacket$entityIdGetter;
public static final MethodHandle methodHandle$ClientboundSetEntityMotionPacket$idGetter;
public static final MethodHandle methodHandle$ClientboundUpdateAttributesPacket0Constructor;
static {
try {
@@ -1400,6 +1403,18 @@ public final class NetworkReflections {
ReflectionUtils.unreflectGetter(field$ClientIntentionPacket$protocolVersion)
.asType(MethodType.methodType(int.class, Object.class))
);
methodHandle$ClientboundRotateHeadPacket$entityIdGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundRotateHeadPacket$entityId)
.asType(MethodType.methodType(int.class, Object.class))
);
methodHandle$ClientboundSetEntityMotionPacket$idGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ClientboundSetEntityMotionPacket$id)
.asType(MethodType.methodType(int.class, Object.class))
);
methodHandle$ClientboundUpdateAttributesPacket0Constructor = requireNonNull(
ReflectionUtils.unreflectConstructor(constructor$ClientboundUpdateAttributesPacket0)
.asType(MethodType.methodType(Object.class, int.class, List.class))
);
if (field$ServerboundCustomPayloadPacket$payload != null) {
methodHandle$ServerboundCustomPayloadPacket$payloadGetter = requireNonNull(
ReflectionUtils.unreflectGetter(field$ServerboundCustomPayloadPacket$payload)

View File

@@ -35,7 +35,6 @@ import net.momirealms.craftengine.core.util.Direction;
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.Position;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldEvents;
import org.bukkit.*;
@@ -499,8 +498,8 @@ public class BukkitServerPlayer extends Player {
if (canBreak) {
if (VersionHelper.isOrAbove1_20_5()) {
Object serverPlayer = serverPlayer();
Object attributeInstance = CoreReflections.method$ServerPlayer$getAttribute.invoke(serverPlayer, MAttributeHolders.BLOCK_BREAK_SPEED);
Object newPacket = NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityID(), Lists.newArrayList(attributeInstance));
Object attributeInstance = CoreReflections.methodHandle$ServerPlayer$getAttributeMethod.invokeExact(serverPlayer, MAttributeHolders.BLOCK_BREAK_SPEED);
Object newPacket = NetworkReflections.methodHandle$ClientboundUpdateAttributesPacket0Constructor.invokeExact(entityID(), (List<?>) Lists.newArrayList(attributeInstance));
sendPacket(newPacket, true);
} else {
resetEffect(MMobEffects.MINING_FATIGUE);
@@ -520,7 +519,7 @@ public class BukkitServerPlayer extends Player {
sendPackets(List.of(fatiguePacket, hastePacket), true);
}
}
} catch (ReflectiveOperationException e) {
} catch (Throwable e) {
plugin.logger().warn("Failed to set attribute for player " + platformPlayer().getName(), e);
}
}

View File

@@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MItems;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.world.BlockPos;
@@ -35,7 +34,7 @@ public class AdventureModeUtils {
public static boolean canPlace(Item<?> itemStack, World world, BlockPos pos, Object state) {
Object blockPos = LocationUtils.toBlockPos(pos);
Object item = itemStack == null ? MItems.Air$ItemStack : itemStack.getLiteralObject();
Object item = itemStack == null ? CoreReflections.instance$ItemStack$EMPTY : itemStack.getLiteralObject();
Object blockInWorld = FastNMS.INSTANCE.constructor$BlockInWorld(FastNMS.INSTANCE.field$CraftWorld$ServerLevel((org.bukkit.World) world.platformWorld()), blockPos, false);
if (state != null) {
try {

View File

@@ -197,6 +197,10 @@ public class BlockStateUtils {
CoreReflections.field$BlockStateBase$burnable.set(state, burnable);
}
public static void setUseShapeForLightOcclusion(Object state, boolean useShapeForLightOcclusion) throws ReflectiveOperationException {
CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(state, useShapeForLightOcclusion);
}
public static void setPushReaction(Object state, PushReaction reaction) throws ReflectiveOperationException {
Object pushReaction = ((Object[]) CoreReflections.method$PushReaction$values.invoke(null))[reaction.ordinal()];
CoreReflections.field$BlockStateBase$pushReaction.set(state, pushReaction);
@@ -206,10 +210,6 @@ public class BlockStateUtils {
CoreReflections.field$BlockStateBase$isRandomlyTicking.set(state, randomlyTicking);
}
public static void setPropagatesSkylightDown(Object state, boolean propagatesSkylightDown) throws ReflectiveOperationException {
CoreReflections.field$BlockStateBase$propagatesSkylightDown.set(state, propagatesSkylightDown);
}
public static void setReplaceable(Object state, boolean replaceable) throws ReflectiveOperationException {
CoreReflections.field$BlockStateBase$replaceable.set(state, replaceable);
}

Some files were not shown because too many files have changed in this diff Show More