9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 10:29:20 +00:00

Merge branch 'Xiao-MoMi:dev' into dev

This commit is contained in:
Halogly
2025-07-21 11:22:37 +08:00
committed by GitHub
38 changed files with 609 additions and 313 deletions

View File

@@ -10,7 +10,8 @@ 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.mythicmobs.MythicItemDropListener;
import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicSkillHelper;
import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils;
import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners;
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
@@ -39,6 +40,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
private final Map<String, LevelerProvider> levelerProviders;
private boolean hasPlaceholderAPI;
private boolean hasViaVersion;
private MythicSkillHelper skillExecute;
public BukkitCompatibilityManager(BukkitCraftEngine plugin) {
this.plugin = plugin;
@@ -131,11 +133,16 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
}
if (this.isPluginEnabled("MythicMobs")) {
BukkitItemManager.instance().registerExternalItemSource(new MythicMobsSource());
new MythicMobsListener(this.plugin);
new MythicItemDropListener(this.plugin);
logHook("MythicMobs");
}
}
@Override
public void executeMMSkill(String skill, float power, Player player) {
MythicSkillHelper.execute(skill, power, player);
}
@Override
public void registerLevelerProvider(String plugin, LevelerProvider provider) {
this.levelerProviders.put(plugin, provider);

View File

@@ -21,12 +21,12 @@ import org.bukkit.inventory.ItemStack;
import java.lang.reflect.Constructor;
public class CraftEngineItemDrop extends ItemDrop implements IItemDrop {
public class MythicItemDrop extends ItemDrop implements IItemDrop {
private final CustomItem<ItemStack> customItem;
private static final Constructor<?> constructor$BukkitItemStack = ReflectionUtils.getConstructor(BukkitItemStack.class, ItemStack.class);
private static final boolean useReflection = constructor$BukkitItemStack != null;
public CraftEngineItemDrop(String line, MythicLineConfig config, CustomItem<ItemStack> customItem) {
public MythicItemDrop(String line, MythicLineConfig config, CustomItem<ItemStack> customItem) {
super(line, config);
this.customItem = customItem;
}

View File

@@ -8,10 +8,10 @@ import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class MythicMobsListener implements Listener {
public class MythicItemDropListener implements Listener {
private final BukkitCraftEngine plugin;
public MythicMobsListener(BukkitCraftEngine plugin) {
public MythicItemDropListener(BukkitCraftEngine plugin) {
this.plugin = plugin;
Bukkit.getPluginManager().registerEvents(this, plugin.javaPlugin());
}
@@ -24,7 +24,7 @@ public class MythicMobsListener implements Listener {
this.plugin.itemManager().getCustomItem(itemId).ifPresent(customItem -> {
String line = event.getContainer().getConfigLine();
MythicLineConfig config = event.getConfig();
event.register(new CraftEngineItemDrop(line, config, customItem));
event.register(new MythicItemDrop(line, config, customItem));
});
}
}

View File

@@ -0,0 +1,27 @@
package net.momirealms.craftengine.bukkit.compatibility.mythicmobs;
import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.mythic.core.utils.MythicUtil;
import net.momirealms.craftengine.core.entity.player.Player;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import java.util.ArrayList;
import java.util.List;
public final class MythicSkillHelper {
public static void execute(String skill, float power, Player player) {
org.bukkit.entity.Player casterPlayer = (org.bukkit.entity.Player) player.platformPlayer();
Location location = casterPlayer.getLocation();
LivingEntity target = MythicUtil.getTargetedEntity(casterPlayer);
List<Entity> targets = new ArrayList<>();
List<Location> locations = null;
if (target != null) {
targets.add(target);
locations = List.of(target.getLocation());
}
MythicBukkit.inst().getAPIHelper().castSkill(casterPlayer, skill, casterPlayer, location, targets, locations, power);
}
}

View File

@@ -1,9 +1,12 @@
package net.momirealms.craftengine.bukkit.compatibility.worldedit;
import com.fastasyncworldedit.bukkit.FaweBukkitWorld;
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.ExtentTraverser;
import com.fastasyncworldedit.core.util.ProcessorTraverser;
import com.sk89q.worldedit.EditSession;
@@ -24,6 +27,7 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.core.block.EmptyBlock;
@@ -89,12 +93,14 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
if (levelChunk != null) {
Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk);
CESection[] ceSections = ceChunk.sections();
for (int i = 0; i < ceSections.length; i++) {
CESection ceSection = ceSections[i];
Object section = sections[i];
int finalI = i;
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
(injected) -> sections[finalI] = injected);
synchronized (sections) {
for (int i = 0; i < ceSections.length; i++) {
CESection ceSection = ceSections[i];
Object section = sections[i];
int finalI = i;
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
(injected) -> sections[finalI] = injected);
}
}
}
}
@@ -174,18 +180,18 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
@Override
public @Nullable Operation commit() {
Operation operation = super.commit();
saveAllChunks();
Operation operation = super.commit();
List<ChunkPos> chunks = new ArrayList<>(this.brokenChunks);
this.brokenChunks.clear();
Object worldServer = this.ceWorld.world().serverWorld();
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
for (ChunkPos chunk : chunks) {
CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey());
// only inject loaded chunks
if (loaded == null) continue;
injectLevelChunk(chunkSource, loaded);
}
Object worldServer = this.ceWorld.world().serverWorld();
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
for (ChunkPos chunk : chunks) {
CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey());
// only inject loaded chunks
if (loaded == null) continue;
injectLevelChunk(chunkSource, loaded);
}
return operation;
}
@@ -214,11 +220,12 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
try {
CEChunk ceChunk = Optional.ofNullable(this.ceWorld.getChunkAtIfLoaded(chunkX, chunkZ))
.orElse(this.ceWorld.worldDataStorage().readChunkAt(this.ceWorld, new ChunkPos(chunkX, chunkZ)));
CESection ceSection = ceChunk.sectionById(SectionPos.blockToSectionCoord(blockY));
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(newStateId);
if (immutableBlockState == null) {
ceChunk.setBlockState(blockX, blockY, blockZ, EmptyBlock.STATE);
ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, EmptyBlock.STATE);
} else {
ceChunk.setBlockState(blockX, blockY, blockZ, immutableBlockState);
ceSection.setBlockState(blockX & 15, blockY & 15, blockZ & 15, immutableBlockState);
}
this.chunksToSave.add(ceChunk);
} catch (IOException e) {

View File

@@ -1,23 +1,12 @@
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.bukkit.plugin.network.BukkitNetworkManager;
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.bukkit.parser.location.LocationParser;
import org.incendo.cloud.parser.standard.IntegerParser;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TestCommand extends BukkitCommandFeature<CommandSender> {
@@ -29,35 +18,9 @@ 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("location", LocationParser.locationParser())
.required("remove", IntegerParser.integerParser())
.handler(context -> {
Player player = context.sender();
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);
player.sendMessage("客户端模组状态: " + BukkitNetworkManager.instance().getUser(player).clientModEnabled());
});
}

View File

@@ -29,6 +29,7 @@ import net.momirealms.craftengine.bukkit.plugin.network.handler.*;
import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload;
import net.momirealms.craftengine.bukkit.plugin.network.payload.NetWorkDataTypes;
import net.momirealms.craftengine.bukkit.plugin.network.payload.Payload;
import net.momirealms.craftengine.bukkit.plugin.network.payload.UnknownPayload;
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.MEntityTypes;
@@ -105,7 +106,7 @@ public class PacketConsumers {
byte yHeadRot = buf.readByte();
int data = buf.readVarInt();
// Falling blocks
int remapped = remap(data);
int remapped = user.clientModEnabled() ? remapMOD(data) : remap(data);
if (remapped != data) {
int xa = buf.readShort();
int ya = buf.readShort();
@@ -428,7 +429,7 @@ public class PacketConsumers {
if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) {
return;
}
int state = remap(before);
int state = user.clientModEnabled() ? remapMOD(before) : remap(before);
if (state == before) {
return;
}
@@ -450,7 +451,7 @@ public class PacketConsumers {
BlockPos blockPos = buf.readBlockPos();
int state = buf.readInt();
boolean global = buf.readBoolean();
int newState = remap(state);
int newState = user.clientModEnabled() ? remapMOD(state) : remap(state);
if (newState == state) {
return;
}
@@ -1006,7 +1007,7 @@ public class PacketConsumers {
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
int id = BlockStateUtils.blockStateToId(blockState);
int remapped = remap(id);
int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id);
if (remapped == id) return;
Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option);
Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped));
@@ -1046,7 +1047,7 @@ public class PacketConsumers {
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
int id = BlockStateUtils.blockStateToId(blockState);
int remapped = remap(id);
int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id);
if (remapped == id) return;
Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option);
Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped));
@@ -1086,7 +1087,7 @@ public class PacketConsumers {
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
int id = BlockStateUtils.blockStateToId(blockState);
int remapped = remap(id);
int remapped = user.clientModEnabled() ? remapMOD(id) : remap(id);
if (remapped == id) return;
Object type = FastNMS.INSTANCE.method$BlockParticleOption$getType(option);
Object remappedOption = FastNMS.INSTANCE.constructor$BlockParticleOption(type, BlockStateUtils.idToBlockState(remapped));
@@ -1884,34 +1885,38 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CUSTOM_PAYLOAD = (user, event, packet) -> {
try {
if (!VersionHelper.isOrAbove1_20_5()) return;
if (!VersionHelper.isOrAbove1_20_2()) return;
Object payload = NetworkReflections.methodHandle$ServerboundCustomPayloadPacket$payloadGetter.invokeExact(packet);
Payload clientPayload;
if (NetworkReflections.clazz$DiscardedPayload.isInstance(payload)) {
Payload discardedPayload = DiscardedPayload.from(payload);
if (discardedPayload == null || !discardedPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY))
clientPayload = DiscardedPayload.from(payload);
} else if (!VersionHelper.isOrAbove1_20_5() && NetworkReflections.clazz$UnknownPayload.isInstance(payload)) {
clientPayload = UnknownPayload.from(payload);
} else {
return;
}
if (clientPayload == null || !clientPayload.channel().equals(NetworkManager.MOD_CHANNEL_KEY))
return;
FriendlyByteBuf buf = clientPayload.toBuffer();
NetWorkDataTypes dataType = buf.readEnumConstant(NetWorkDataTypes.class);
if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) {
int clientBlockRegistrySize = dataType.decode(buf);
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
if (clientBlockRegistrySize != serverBlockRegistrySize) {
user.kick(Component.translatable(
"disconnect.craftengine.block_registry_mismatch",
TranslationArgument.numeric(clientBlockRegistrySize),
TranslationArgument.numeric(serverBlockRegistrySize)
));
return;
FriendlyByteBuf buf = discardedPayload.toBuffer();
NetWorkDataTypes<?> dataType = NetWorkDataTypes.readType(buf);
if (dataType == NetWorkDataTypes.CLIENT_CUSTOM_BLOCK) {
int clientBlockRegistrySize = dataType.as(Integer.class).decode(buf);
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
if (clientBlockRegistrySize != serverBlockRegistrySize) {
user.kick(Component.translatable(
"disconnect.craftengine.block_registry_mismatch",
TranslationArgument.numeric(clientBlockRegistrySize),
TranslationArgument.numeric(serverBlockRegistrySize)
));
return;
}
user.setClientModState(true);
} else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) {
if (!VersionHelper.isOrAbove1_20_2()) return;
if (dataType.as(Boolean.class).decode(buf)) {
FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer());
dataType.writeType(bufPayload);
dataType.as(Boolean.class).encode(bufPayload, true);
user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array());
}
}
user.setClientModState(true);
} else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) {
if (dataType.decode(buf)) {
FriendlyByteBuf bufPayload = new FriendlyByteBuf(Unpooled.buffer());
bufPayload.writeEnumConstant(dataType);
dataType.encode(bufPayload, true);
user.sendCustomPayload(NetworkManager.MOD_CHANNEL_KEY, bufPayload.array());
}
}
} catch (Throwable e) {

View File

@@ -1,64 +1,28 @@
package net.momirealms.craftengine.bukkit.plugin.network.payload;
import io.netty.buffer.ByteBuf;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
public enum NetWorkDataTypes {
CLIENT_CUSTOM_BLOCK(NetWorkCodecs.INTEGER),
CANCEL_BLOCK_UPDATE(NetWorkCodecs.BOOLEAN);
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
private final NetWorkCodec<?> codec;
public class NetWorkDataTypes<T> {
private static final Map<Integer, NetWorkDataTypes<?>> id2NetWorkDataTypes = new HashMap<>();
public static final NetWorkDataTypes<Integer> CLIENT_CUSTOM_BLOCK =
new NetWorkDataTypes<>(0, FriendlyByteBuf::readInt, FriendlyByteBuf::writeInt);
public static final NetWorkDataTypes<Boolean> CANCEL_BLOCK_UPDATE =
new NetWorkDataTypes<>(1, FriendlyByteBuf::readBoolean, FriendlyByteBuf::writeBoolean);
static {
register(CLIENT_CUSTOM_BLOCK);
register(CANCEL_BLOCK_UPDATE);
NetWorkDataTypes(NetWorkCodec<?> codec) {
this.codec = codec;
}
private static void register(NetWorkDataTypes<?> type) {
id2NetWorkDataTypes.put(type.id, type);
public NetWorkCodec<?> codec() {
return codec;
}
private final int id;
private final Function<FriendlyByteBuf, T> decoder;
private final BiConsumer<FriendlyByteBuf, T> encoder;
public NetWorkDataTypes(int id, Function<FriendlyByteBuf, T> decoder, BiConsumer<FriendlyByteBuf, T> encoder) {
this.id = id;
this.decoder = decoder;
this.encoder = encoder;
@SuppressWarnings("unchecked")
public <V> V decode(ByteBuf buf) {
return (V) codec.decode(buf);
}
public T decode(FriendlyByteBuf buf) {
return decoder.apply(buf);
}
public void encode(FriendlyByteBuf buf, T data) {
encoder.accept(buf, data);
}
public int id() {
return id;
}
public void writeType(FriendlyByteBuf buf) {
buf.writeVarInt(id);
}
public static NetWorkDataTypes<?> readType(FriendlyByteBuf buf) {
int id = buf.readVarInt();
return id2NetWorkDataTypes.get(id);
}
@SuppressWarnings({"unchecked", "unused"})
public <R> NetWorkDataTypes<R> as(Class<R> clazz) {
return (NetWorkDataTypes<R>) this;
@SuppressWarnings("unchecked")
public <V> void encode(ByteBuf buf, V value) {
((NetWorkCodec<V>) codec).encode(buf, value);
}
}

View File

@@ -0,0 +1,28 @@
package net.momirealms.craftengine.bukkit.plugin.network.payload;
import io.netty.buffer.ByteBuf;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.Key;
public record UnknownPayload(Key channel, ByteBuf rawPayload) implements Payload{
public static UnknownPayload from(Object payload) {
try {
Object id = NetworkReflections.field$UnknownPayload$id.get(payload);
ByteBuf data = (ByteBuf) NetworkReflections.field$UnknownPayload$data.get(payload);
Key channel = KeyUtils.resourceLocationToKey(id);
return new UnknownPayload(channel, data);
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to create UnknownPayload", e);
return null;
}
}
@Override
public FriendlyByteBuf toBuffer() {
return new FriendlyByteBuf(rawPayload);
}
}

View File

@@ -3873,4 +3873,27 @@ public final class CoreReflections {
throw new ReflectionInitException("Failed to initialize HashOps", e);
}
}
public static final Class<?> clazz$SnowLayerBlock = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.block.BlockSnow",
"world.level.block.SnowLayerBlock"
)
);
public static final Field field$SnowLayerBlock$LAYERS = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$SnowLayerBlock, clazz$IntegerProperty, 0
)
);
public static final Object instance$SnowLayerBlock$LAYERS;
static {
try {
instance$SnowLayerBlock$LAYERS = field$SnowLayerBlock$LAYERS.get(null);
} catch (IllegalAccessException e) {
throw new ReflectionInitException("Failed to initialize SnowLayerBlock$LAYERS", e);
}
}
}

View File

@@ -17,6 +17,7 @@ public final class MBlocks {
public static final Object SHORT_GRASS$defaultState;
public static final Object SHULKER_BOX;
public static final Object COMPOSTER;
public static final Object SNOW;
private static Object getById(String id) {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -35,5 +36,6 @@ public final class MBlocks {
SHORT_GRASS$defaultState = FastNMS.INSTANCE.method$Block$defaultState(SHORT_GRASS);
SHULKER_BOX = getById("shulker_box");
COMPOSTER = getById("composter");
SNOW = getById("snow");
}
}

View File

@@ -1248,7 +1248,9 @@ public final class NetworkReflections {
);
public static final Constructor<?> constructor$ClientboundCustomPayloadPacket = requireNonNull(
ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, 0)
VersionHelper.isOrAbove1_20_2()
? ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, clazz$CustomPacketPayload)
: ReflectionUtils.getConstructor(clazz$ClientboundCustomPayloadPacket, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$FriendlyByteBuf)
);
// 1.20.2+
@@ -1621,4 +1623,27 @@ public final class NetworkReflections {
throw new ReflectionInitException("Failed to initialize HashedStack$STREAM_CODEC", e);
}
}
// 1.20.2~1.20.4
public static final Class<?> clazz$UnknownPayload = MiscUtils.requireNonNullIf(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket$UnknownPayload")
),
VersionHelper.isOrAbove1_20_2() && !VersionHelper.isOrAbove1_20_5()
);
// 1.20.2~1.20.4
public static final Field field$UnknownPayload$id = Optional.ofNullable(clazz$UnknownPayload)
.map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$ResourceLocation, 0))
.orElse(null);
// 1.20.2~1.20.4
public static final Field field$UnknownPayload$data = Optional.ofNullable(clazz$UnknownPayload)
.map(it -> ReflectionUtils.getDeclaredField(it, ByteBuf.class, 0))
.orElse(null);
// 1.20.2~1.20.4
public static final Constructor<?> constructor$UnknownPayload = Optional.ofNullable(clazz$UnknownPayload)
.map(ReflectionUtils::getTheOnlyConstructor)
.orElse(null);
}

View File

@@ -306,13 +306,20 @@ public class BukkitServerPlayer extends Player {
public void sendCustomPayload(Key channel, byte[] data) {
try {
Object channelKey = KeyUtils.toResourceLocation(channel);
Object dataPayload;
if (DiscardedPayload.useNewMethod) {
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data);
Object responsePacket;
if (VersionHelper.isOrAbove1_20_2()) {
Object dataPayload;
if (NetworkReflections.clazz$UnknownPayload != null) {
dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
} else if (DiscardedPayload.useNewMethod) {
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data);
} else {
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
}
responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload);
} else {
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(channelKey, FastNMS.INSTANCE.constructor$FriendlyByteBuf(Unpooled.wrappedBuffer(data)));
}
Object responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload);
this.sendPacket(responsePacket, true);
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to send custom payload to " + name(), e);

View File

@@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.block.DelegatingBlockState;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -142,4 +143,10 @@ public class BlockStateUtils {
Object blockOwner = getBlockOwner(state);
return IGNITE_ODDS.getOrDefault(blockOwner, 0) > 0;
}
public static Object getBlockState(Block block) {
Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld());
Object blockPos = LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ());
return FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos);
}
}

View File

@@ -2,7 +2,10 @@ package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
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.LocationUtils;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
@@ -21,11 +24,15 @@ public class BukkitBlockInWorld implements BlockInWorld {
@Override
public boolean canBeReplaced(BlockPlaceContext context) {
ImmutableBlockState customState = CraftEngineBlocks.getCustomBlockState(this.block);
Object state = BlockStateUtils.getBlockState(this.block);
ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null);
if (customState != null && !customState.isEmpty()) {
return customState.behavior().canBeReplaced(context, customState);
}
return this.block.isReplaceable();
if (BlockStateUtils.getBlockOwner(state) == MBlocks.SNOW) {
return (int) FastNMS.INSTANCE.method$StateHolder$getValue(state, CoreReflections.instance$SnowLayerBlock$LAYERS) == 1;
}
return BlockStateUtils.isReplaceable(state);
}
@Override

View File

@@ -88,6 +88,11 @@ public class BukkitWorldManager implements WorldManager, Listener {
}
}
@Override
public CEWorld[] getWorlds() {
return this.worldArray;
}
private void resetWorldArray() {
this.worldArray = this.worlds.values().toArray(new CEWorld[0]);
}
@@ -218,7 +223,6 @@ public class BukkitWorldManager implements WorldManager, Listener {
this.lastVisitedUUID = null;
}
this.resetWorldArray();
} finally {
this.worldMapLock.writeLock().unlock();
}
@@ -328,74 +332,76 @@ public class BukkitWorldManager implements WorldManager, Listener {
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
Object levelChunk = FastNMS.INSTANCE.method$ServerChunkCache$getChunkAtIfLoadedMainThread(chunkSource, chunk.getX(), chunk.getZ());
Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(levelChunk);
for (int i = 0; i < ceSections.length; i++) {
CESection ceSection = ceSections[i];
Object section = sections[i];
if (Config.syncCustomBlocks()) {
Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section);
Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer);
Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data);
boolean requiresSync = false;
if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) {
Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette);
if (BlockStateUtils.isCustomBlock(onlyBlockState)) {
synchronized (sections) {
for (int i = 0; i < ceSections.length; i++) {
CESection ceSection = ceSections[i];
Object section = sections[i];
if (Config.syncCustomBlocks()) {
Object statesContainer = FastNMS.INSTANCE.field$LevelChunkSection$states(section);
Object data = CoreReflections.varHandle$PalettedContainer$data.get(statesContainer);
Object palette = CoreReflections.field$PalettedContainer$Data$palette.get(data);
boolean requiresSync = false;
if (CoreReflections.clazz$SingleValuePalette.isInstance(palette)) {
Object onlyBlockState = CoreReflections.field$SingleValuePalette$value.get(palette);
if (BlockStateUtils.isCustomBlock(onlyBlockState)) {
requiresSync = true;
}
} else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) {
Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette);
for (Object blockState : blockStates) {
if (blockState != null) {
if (BlockStateUtils.isCustomBlock(blockState)) {
requiresSync = true;
break;
}
}
}
} else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) {
Object biMap = CoreReflections.field$HashMapPalette$values.get(palette);
Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap);
for (Object blockState : blockStates) {
if (blockState != null) {
if (BlockStateUtils.isCustomBlock(blockState)) {
requiresSync = true;
break;
}
}
}
} else {
requiresSync = true;
}
} else if (CoreReflections.clazz$LinearPalette.isInstance(palette)) {
Object[] blockStates = (Object[]) CoreReflections.field$LinearPalette$values.get(palette);
for (Object blockState : blockStates) {
if (blockState != null) {
if (BlockStateUtils.isCustomBlock(blockState)) {
requiresSync = true;
break;
}
}
}
} else if (CoreReflections.clazz$HashMapPalette.isInstance(palette)) {
Object biMap = CoreReflections.field$HashMapPalette$values.get(palette);
Object[] blockStates = (Object[]) CoreReflections.field$CrudeIncrementalIntIdentityHashBiMap$keys.get(biMap);
for (Object blockState : blockStates) {
if (blockState != null) {
if (BlockStateUtils.isCustomBlock(blockState)) {
requiresSync = true;
break;
}
}
}
} else {
requiresSync = true;
}
if (requiresSync) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z);
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState);
if (optionalCustomState.isPresent()) {
ceSection.setBlockState(x, y, z, optionalCustomState.get());
if (requiresSync) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
Object mcState = FastNMS.INSTANCE.method$LevelChunkSection$getBlockState(section, x, y, z);
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(mcState);
if (optionalCustomState.isPresent()) {
ceSection.setBlockState(x, y, z, optionalCustomState.get());
}
}
}
}
}
}
}
if (Config.restoreCustomBlocks()) {
if (!ceSection.statesContainer().isEmpty()) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
ImmutableBlockState customState = ceSection.getBlockState(x, y, z);
if (!customState.isEmpty() && customState.customBlockState() != null) {
FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false);
if (Config.restoreCustomBlocks()) {
if (!ceSection.statesContainer().isEmpty()) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
ImmutableBlockState customState = ceSection.getBlockState(x, y, z);
if (!customState.isEmpty() && customState.customBlockState() != null) {
FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.customBlockState().handle(), false);
}
}
}
}
}
}
int finalI = i;
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
(injected) -> sections[finalI] = injected);
}
int finalI = i;
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(pos.x, ceChunk.sectionY(i), pos.z),
(injected) -> sections[finalI] = injected);
}
if (Config.enableRecipeSystem()) {
@SuppressWarnings("unchecked")