9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 09:59:20 +00:00

Merge pull request #297 from jhqwqmc/dev-hotfix

fix(network): 修复启用客户端模组显示问题
This commit is contained in:
XiaoMoMi
2025-07-20 00:38:31 +08:00
committed by GitHub
5 changed files with 104 additions and 76 deletions

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 = 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;
}
user.setClientModState(true);
} else if (dataType == NetWorkDataTypes.CANCEL_BLOCK_UPDATE) {
if (!VersionHelper.isOrAbove1_20_2()) return;
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());
}
}
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

@@ -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

@@ -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);