diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 2a1105a77..5624e7bd8 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -8,6 +8,8 @@ metrics: true update-checker: true # Forces a specific locale (e.g., zh_cn) forced-locale: '' +# Filter configuration phase player disconnection logs +filter-configuration-phase-disconnect: false resource-pack: # Should those images in minecraft:default font also work in minecraft:uniform diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/blocks.yml b/bukkit/loader/src/main/resources/resources/default/configuration/blocks.yml index 5001b8aff..83a7c0c52 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/blocks.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/blocks.yml @@ -12,6 +12,7 @@ items#misc: behavior: type: block_item block: + name: "i18n:item.chinese_lantern" loot: template: "default:loot_table/basic" arguments: @@ -53,6 +54,7 @@ items#misc: behavior: type: block_item block: + name: "i18n:item.netherite_anvil" loot: template: "default:loot_table/basic" arguments: diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/ores.yml b/bukkit/loader/src/main/resources/resources/default/configuration/ores.yml index 6d81990b6..d843616f0 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/ores.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/ores.yml @@ -42,6 +42,7 @@ items: blocks: default:topaz_ore: + name: "i18n:item.topaz_ore" loot: template: "default:loot_table/ore" arguments: @@ -63,6 +64,7 @@ blocks: arguments: path: "minecraft:block/custom/topaz_ore" default:deepslate_topaz_ore: + name: "i18n:item.deepslate_topaz_ore" loot: template: "default:loot_table/ore" arguments: diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/palm_tree.yml b/bukkit/loader/src/main/resources/resources/default/configuration/palm_tree.yml index d5ce8c5c4..084f69921 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/palm_tree.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/palm_tree.yml @@ -18,6 +18,7 @@ items: behavior: type: block_item block: + name: "i18n:item.palm_log" behavior: type: strippable_block stripped: default:stripped_palm_log @@ -64,6 +65,7 @@ items: behavior: type: block_item block: + name: "i18n:item.stripped_palm_log" loot: template: "default:loot_table/basic" arguments: @@ -107,6 +109,7 @@ items: behavior: type: block_item block: + name: "i18n:item.palm_wood" behavior: type: strippable_block stripped: default:stripped_palm_wood @@ -153,6 +156,7 @@ items: behavior: type: block_item block: + name: "i18n:item.stripped_palm_wood" loot: template: "default:loot_table/basic" arguments: @@ -195,6 +199,7 @@ items: behavior: type: block_item block: + name: "i18n:item.palm_planks" settings: template: "default:settings/planks" overrides: @@ -225,6 +230,7 @@ items: behavior: type: block_item block: + name: "i18n:item.palm_sapling" settings: template: "default:settings/sapling" overrides: @@ -287,6 +293,7 @@ items: behavior: type: block_item block: + name: "i18n:item.palm_leaves" behavior: type: leaves_block loot: diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml b/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml index 41ccd6499..b14bc4aa3 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml @@ -25,6 +25,7 @@ items: block: default:reed blocks: default:fairy_flower: + name: "i18n:item.fairy_flower" settings: template: - default:hardness/none @@ -64,6 +65,7 @@ blocks: "0": "minecraft:block/custom/fairy_flower_4" default:reed: settings: + name: "i18n:item.reed" template: - default:hardness/none - default:sound/grass diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 447816970..cc52404bc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -452,7 +452,18 @@ public class BukkitBlockManager extends AbstractBlockManager { // create block Map behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false); - CustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable); + BukkitCustomBlock block = new BukkitCustomBlock(id, holder, properties, appearances, variants, settings, behaviorSection, lootTable); + // read block name + String blockName = (String) section.getOrDefault("name", null); + if (blockName != null && blockName.startsWith("i18n:")) { + plugin.translationManager().i18nData().forEach((locale, i18nData) -> { + plugin.debug(() -> "locale: " + toMinecraftLocale(locale) + ": " + i18nData.translate(blockName.substring(5))); + block.addBlockName(toMinecraftLocale(locale), i18nData.translate(blockName.substring(5))); + } + ); + } else { + block.addBlockName(blockName); + } // bind appearance bindAppearance(block); @@ -467,6 +478,18 @@ public class BukkitBlockManager extends AbstractBlockManager { } } + public static String toMinecraftLocale(Locale locale) { + String language = locale.getLanguage().toLowerCase(); + String country = locale.getCountry().toLowerCase(); + if ("en".equals(language) && country.isEmpty()) { + return "en_us"; + } + if (country.isEmpty()) { + return language; + } + return language + "_" + country; + } + private void bindAppearance(CustomBlock block) { for (ImmutableBlockState state : block.variantProvider().states()) { ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()]; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index 1b89860b0..7437b832a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.I18NData; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Tristate; @@ -137,4 +138,24 @@ public class BukkitCustomBlock extends CustomBlock { CraftEngine.instance().logger().warn("Failed to init block settings", e); } } + + public void addBlockName(String lang, String blockName) { + I18NData i18nData = new I18NData(); + for (ImmutableBlockState state : this.variantProvider().states()) { + try { + Object blockState = state.customBlockState().handle(); + Object block = Reflections.method$BlockStateBase$getBlock.invoke(blockState); + String translationKey = (String) Reflections.method$BlockBehaviour$getDescriptionId.invoke(block); + i18nData.addTranslation(translationKey, blockName); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to get the " + state.owner().value().id() + " translationKey"); + } + } + this.addBlockName(lang, i18nData); + } + + public void addBlockName(String blockName) { + if (blockName == null) return; + addBlockName("en_us", blockName); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 1226fb980..d57cfdf3d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -10,7 +10,6 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.impl.*; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.bukkit.util.RegistryUtils; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; @@ -27,16 +26,15 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerRegisterChannelEvent; +import org.bukkit.plugin.messaging.PluginMessageListener; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; -public class BukkitNetworkManager implements NetworkManager, Listener { +public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener { private static BukkitNetworkManager instance; private static final Map, TriConsumer> nmsPacketFunctions = new HashMap<>(); private static final Map> byteBufPacketFunctions = new HashMap<>(); @@ -131,6 +129,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener { registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket); registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket); registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket); + registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, Reflections.clazz$ServerboundCustomPayloadPacket); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket()); @@ -161,29 +160,21 @@ public class BukkitNetworkManager implements NetworkManager, Listener { this.onlineUsers.remove(player.getUniqueId()); } - // for mod - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerRegisterChannel(PlayerRegisterChannelEvent event) { - if (!event.getChannel().equals(MOD_CHANNEL)) return; - Player player = event.getPlayer(); - NetWorkUser user = getUser(player); - if (user == null) return; - user.setClientModState(true); - int blockRegistrySize = RegistryUtils.currentBlockRegistrySize(); - byte[] payload = ("cp:" + blockRegistrySize).getBytes(StandardCharsets.UTF_8); - player.sendPluginMessage(plugin.bootstrap(), MOD_CHANNEL, payload); - } - @Override public Collection onlineUsers() { return onlineUsers.values(); } + @Override + // 保留仅注册入频道用 + public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) {} + @Override public void init() { if (init) return; try { - plugin.bootstrap().getServer().getMessenger().registerOutgoingPluginChannel(plugin.bootstrap(), "craftengine:payload"); + plugin.bootstrap().getServer().getMessenger().registerIncomingPluginChannel(plugin.bootstrap(), MOD_CHANNEL, this); + plugin.bootstrap().getServer().getMessenger().registerOutgoingPluginChannel(plugin.bootstrap(), MOD_CHANNEL); Object server = Reflections.method$MinecraftServer$getServer.invoke(null); Object serverConnection = Reflections.field$MinecraftServer$connection.get(server); @SuppressWarnings("unchecked") diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index d588863b8..a220f5f1b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.plugin.network; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; 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.FurnitureBreakEvent; import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; @@ -19,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigManager; import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.NetworkManager; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.chunk.Palette; @@ -33,6 +36,7 @@ import org.bukkit.util.RayTraceResult; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -286,7 +290,6 @@ public class PacketConsumers { }; private static void handlePlayerActionPacketOnMainThread(BukkitServerPlayer player, World world, BlockPos pos, Object packet) throws Exception { - Object action = Reflections.field$ServerboundPlayerActionPacket$action.get(packet); if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) { Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world); @@ -306,6 +309,28 @@ public class PacketConsumers { } return; } + if (player.isAdventureMode()) { + Object itemStack = Reflections.method$CraftItemStack$asNMSCopy.invoke(null, player.platformPlayer().getInventory().getItemInMainHand()); + Object blockPos = Reflections.constructor$BlockPos.newInstance(pos.x(), pos.y(), pos.z()); + Object blockInWorld = Reflections.constructor$BlockInWorld.newInstance(serverLevel, blockPos, false); + if (VersionHelper.isVersionNewerThan1_20_5()) { + if (Reflections.method$ItemStack$canBreakBlockInAdventureMode != null + && !(boolean) Reflections.method$ItemStack$canBreakBlockInAdventureMode.invoke( + itemStack, blockInWorld + )) { + player.preventMiningBlock(); + return; + } + } else { + if (Reflections.method$ItemStack$canDestroy != null + && !(boolean) Reflections.method$ItemStack$canDestroy.invoke( + itemStack, Reflections.instance$BuiltInRegistries$BLOCK, blockInWorld + )) { + player.preventMiningBlock(); + return; + } + } + } player.startMiningBlock(world, pos, blockState, true, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId)); } else if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) { if (player.isMiningBlock()) { @@ -875,4 +900,43 @@ public class PacketConsumers { callback.accept(new String(newCodepoints, 0, newCodepoints.length)); } } + public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { + try { + if (!VersionHelper.isVersionNewerThan1_20_5()) return; + Object payload = Reflections.field$ServerboundCustomPayloadPacket$payload.get(packet); + if (payload.getClass().equals(Reflections.clazz$DiscardedPayload)) { + Object type = Reflections.method$CustomPacketPayload$type.invoke(payload); + Object id = Reflections.method$CustomPacketPayload$Type$id.invoke(type); + String channel = id.toString(); + if (!channel.equals(NetworkManager.MOD_CHANNEL)) return; + ByteBuf buf = (ByteBuf) Reflections.method$DiscardedPayload$data.invoke(payload); + byte[] data = new byte[buf.readableBytes()]; + buf.readBytes(data); + String decodeData = new String(data, StandardCharsets.UTF_8); + if (!decodeData.endsWith("init")) return; + int firstColon = decodeData.indexOf(':'); + if (firstColon == -1) return; + int secondColon = decodeData.indexOf(':', firstColon + 1); + if (secondColon == -1) return; + int clientBlockRegistrySize = Integer.parseInt(decodeData.substring(firstColon + 1, secondColon)); + int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); + if (clientBlockRegistrySize != serverBlockRegistrySize) { + Object kickPacket = Reflections.constructor$ClientboundDisconnectPacket.newInstance( + ComponentUtils.adventureToMinecraft( + Component.translatable( + "disconnect.craftengine.block_registry_mismatch", + TranslationArgument.numeric(clientBlockRegistrySize), + TranslationArgument.numeric(serverBlockRegistrySize) + ) + ) + ); + user.nettyChannel().writeAndFlush(kickPacket); + user.nettyChannel().disconnect(); + } + user.setClientModState(true); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e); + } + }; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 84698bc75..3fb0627e6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.PackedBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; @@ -310,7 +311,8 @@ public class BukkitServerPlayer extends Player { if (custom && getDestroyProgress(state, pos) >= 1f) { assert immutableBlockState != null; // not an instant break on client side - if (getDestroyProgress(immutableBlockState.vanillaBlockState().handle(), pos) < 1f) { + PackedBlockState vanillaBlockState = immutableBlockState.vanillaBlockState(); + if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.handle(), pos) < 1f) { try { Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(this.destroyedState), false); sendPacket(levelEventPacket, false); @@ -365,6 +367,13 @@ public class BukkitServerPlayer extends Player { setIsDestroyingBlock(false, false); } + @Override + public void preventMiningBlock() { + setCanBreakBlock(false); + setIsDestroyingBlock(false, false); + abortMiningBlock(); + } + private void resetEffect(Object mobEffect) throws ReflectiveOperationException { Object effectInstance = Reflections.method$ServerPlayer$getEffect.invoke(serverPlayer(), mobEffect); Object packet; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index d7604cfce..707181184 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -5572,4 +5572,106 @@ public class Reflections { clazz$CraftEventFactory, boolean.class, new String[] { "handleBlockFormEvent" }, clazz$Level, clazz$BlockPos, clazz$BlockState, int.class ) ); + + public static final Constructor constructor$ClientboundLevelChunkWithLightPacket = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ClientboundLevelChunkWithLightPacket, clazz$LevelChunk, clazz$LevelLightEngine, BitSet.class, BitSet.class + ) + ); + + public static final Class clazz$BlockInWorld = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.level.block.state.pattern.BlockInWorld"), + BukkitReflectionUtils.assembleMCClass("world.level.block.state.pattern.ShapeDetectorBlock") + ) + ); + + public static final Constructor constructor$BlockInWorld = requireNonNull( + ReflectionUtils.getConstructor( + clazz$BlockInWorld, 0 + ) + ); + + // 1.20.5+ + public static final Method method$ItemStack$canBreakBlockInAdventureMode = + ReflectionUtils.getMethod( + clazz$ItemStack, new String[]{"canBreakBlockInAdventureMode"}, clazz$BlockInWorld + ); + + // 1.20 ~ 1.20.4 + // instance$BuiltInRegistries$BLOCK + public static final Method method$ItemStack$canDestroy = + ReflectionUtils.getMethod( + clazz$ItemStack,new String[]{"b"}, clazz$Registry, clazz$BlockInWorld + ); + + public static final Method method$BlockStateBase$getBlock = requireNonNull( + ReflectionUtils.getMethod( + clazz$BlockStateBase, clazz$Block + ) + ); + + public static final Method method$BlockBehaviour$getDescriptionId = requireNonNull( + VersionHelper.isVersionNewerThan1_21_2() + ? ReflectionUtils.getMethod(clazz$BlockBehaviour, String.class) + : ReflectionUtils.getMethod(clazz$Block, String.class) + ); + + public static final Class clazz$ServerboundCustomPayloadPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInCustomPayload") + ) + ); + + // 1.20.2+ + public static final Class clazz$CustomPacketPayload = + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.common.custom.CustomPacketPayload") + ); + + // 1.20.5+ + public static final Class clazz$CustomPacketPayload$Type = + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.common.custom.CustomPacketPayload$Type") + ); + + // 1.20.2+ + public static final Field field$ServerboundCustomPayloadPacket$payload = Optional.ofNullable(clazz$CustomPacketPayload) + .map(it -> ReflectionUtils.getDeclaredField(clazz$ServerboundCustomPayloadPacket, it, 0)) + .orElse(null); + + // 1.20.2+ + public static final Class clazz$DiscardedPayload = + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.common.custom.DiscardedPayload") + ); + + // 1.20.5+ + public static final Method method$CustomPacketPayload$type = Optional.ofNullable(clazz$CustomPacketPayload$Type) + .map(it -> ReflectionUtils.getMethod(clazz$CustomPacketPayload, it)) + .orElse(null); + + // 1.20.5+ + public static final Method method$CustomPacketPayload$Type$id = Optional.ofNullable(clazz$CustomPacketPayload$Type) + .map(it -> ReflectionUtils.getMethod(it, clazz$ResourceLocation)) + .orElse(null); + + // 1.20.5+ + public static final Method method$DiscardedPayload$data = Optional.ofNullable(clazz$DiscardedPayload) + .map(it -> ReflectionUtils.getMethod(it, ByteBuf.class)) + .orElse(null); + + public static final Class clazz$ClientboundDisconnectPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.common.ClientboundDisconnectPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutKickDisconnect") + ) + ); + + public static final Constructor constructor$ClientboundDisconnectPacket = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ClientboundDisconnectPacket, clazz$Component + ) + ); } diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java index db505bfef..d3bdbf562 100644 --- a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/CraftEngineFabricModClient.java @@ -2,44 +2,42 @@ package net.momirealms.craftengine.fabric.client; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking; import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.block.Block; import net.minecraft.client.MinecraftClient; import net.minecraft.client.color.world.BiomeColors; -import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientConfigurationNetworkHandler; import net.minecraft.client.render.RenderLayer; -import net.minecraft.network.DisconnectionInfo; import net.minecraft.registry.Registries; -import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.world.biome.FoliageColors; +import net.momirealms.craftengine.fabric.client.blocks.CustomBlock; import net.momirealms.craftengine.fabric.client.config.ModConfig; import net.momirealms.craftengine.fabric.client.network.CraftEnginePayload; -import java.net.URI; import java.nio.charset.StandardCharsets; -import java.util.Optional; public class CraftEngineFabricModClient implements ClientModInitializer { public static final String MOD_ID = "craftengine"; @Override public void onInitializeClient() { - PayloadTypeRegistry.playS2C().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC); - initChannel(MinecraftClient.getInstance().getNetworkHandler()); + PayloadTypeRegistry.configurationS2C().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC); + PayloadTypeRegistry.configurationC2S().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC); registerRenderLayer(); - ClientPlayConnectionEvents.INIT.register((handler, client) -> initChannel(handler)); + ClientConfigurationConnectionEvents.START.register(CraftEngineFabricModClient::initChannel); } public static void registerRenderLayer() { Registries.BLOCK.forEach(block -> { Identifier id = Registries.BLOCK.getId(block); - if (id.getNamespace().equals(CraftEngineFabricModClient.MOD_ID)) { - BlockRenderLayerMap.INSTANCE.putBlock(block, RenderLayer.getCutoutMipped()); + if (block instanceof CustomBlock customBlock) { + if (customBlock.isTransparent()) { + BlockRenderLayerMap.INSTANCE.putBlock(customBlock, RenderLayer.getCutoutMipped()); + } if (id.getPath().contains("leaves")) { registerColor(block); } @@ -59,30 +57,15 @@ public class CraftEngineFabricModClient implements ClientModInitializer { ); } - private static void initChannel(ClientPlayNetworkHandler handler) { + private static void initChannel(ClientConfigurationNetworkHandler handler, MinecraftClient client) { if (ModConfig.enableNetwork) { registerChannel(handler); } else { - ClientPlayNetworking.unregisterGlobalReceiver(CraftEnginePayload.ID.id()); + ClientConfigurationNetworking.unregisterGlobalReceiver(CraftEnginePayload.ID); } } - private static void registerChannel(ClientPlayNetworkHandler handler) { - ClientPlayNetworking.registerGlobalReceiver(CraftEnginePayload.ID, (payload, context) -> { - byte[] data = payload.data(); - String decoded = new String(data, StandardCharsets.UTF_8); - if (decoded.startsWith("cp:")) { - int blockRegistrySize = Integer.parseInt(decoded.substring(3)); - if (Block.STATE_IDS.size() != blockRegistrySize) { - handler.getConnection().disconnect( - new DisconnectionInfo( - Text.translatable("disconnect.craftengine.block_registry_mismatch", Block.STATE_IDS.size(), blockRegistrySize), - Optional.of(FabricLoader.getInstance().getConfigDir().resolve("craft-engine-fabric-mod/mappings.yml")), - Optional.of(URI.create("https://github.com/Xiao-MoMi/craft-engine")) - ) - ); - } - } - }); + private static void registerChannel(ClientConfigurationNetworkHandler handler) { + ClientConfigurationNetworking.send(new CraftEnginePayload((":" + Block.STATE_IDS.size() + ":init").getBytes(StandardCharsets.UTF_8))); } } diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/blocks/CustomBlock.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/blocks/CustomBlock.java new file mode 100644 index 000000000..c53045cdf --- /dev/null +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/blocks/CustomBlock.java @@ -0,0 +1,35 @@ +package net.momirealms.craftengine.fabric.client.blocks; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; + +public class CustomBlock extends Block { + private final VoxelShape outlineShape; + private final VoxelShape collisionShape; + private final boolean isTransparent; + + public CustomBlock(Settings settings, VoxelShape outlineShape, VoxelShape collisionShape, boolean isTransparent) { + super(settings); + this.outlineShape = outlineShape; + this.collisionShape = collisionShape; + this.isTransparent = isTransparent; + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return this.outlineShape; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return this.collisionShape; + } + + public boolean isTransparent() { + return this.isTransparent; + } +} diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java index eb8e5add2..c975c1f7f 100644 --- a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/network/CraftEnginePayload.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.fabric.client.network; -import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.PacketByteBuf; import net.minecraft.network.codec.PacketCodec; import net.minecraft.network.packet.CustomPayload; import net.minecraft.util.Identifier; @@ -8,10 +8,9 @@ import net.minecraft.util.Identifier; public record CraftEnginePayload(byte[] data) implements CustomPayload { public static final Identifier CRAFTENGINE_PAYLOAD = Identifier.of("craftengine", "payload"); public static final Id ID = new Id<>(CraftEnginePayload.CRAFTENGINE_PAYLOAD); - public static final PacketCodec CODEC = PacketCodec.tuple( - new ByteArrayCodec(), CraftEnginePayload::data, - CraftEnginePayload::new - ); + public static final PacketCodec CODEC = PacketCodec.of( + (payload, byteBuf) -> byteBuf.writeByteArray(payload.data()), + buf -> new CraftEnginePayload(buf.readByteArray())); @Override public Id getId() { diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java index 805f23b52..1eb473f1a 100644 --- a/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/CraftEngineFabricMod.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.fabric; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.block.BlockState; +import net.minecraft.block.piston.PistonBehavior; import net.minecraft.util.Identifier; import net.momirealms.craftengine.fabric.client.config.ModConfig; import net.momirealms.craftengine.fabric.util.BlockUtils; @@ -38,7 +39,9 @@ public class CraftEngineFabricMod implements ModInitializer { RegisterBlocks.register( replacedBlockId.getPath() + "_" + i, BlockUtils.canPassThrough(blockState), - BlockUtils.getShape(blockState) + BlockUtils.getShape(blockState), + BlockUtils.isTransparent(blockState), + BlockUtils.canPush(blockState) ); } } diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/BlockUtils.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/BlockUtils.java index d9c3f36e8..1069ca5d7 100644 --- a/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/BlockUtils.java +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/BlockUtils.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.fabric.util; import net.minecraft.block.AbstractBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.TransparentBlock; +import net.minecraft.block.piston.PistonBehavior; import net.minecraft.util.math.BlockPos; import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; @@ -63,4 +65,20 @@ public class BlockUtils { return VoxelShapes.fullCube(); } } + + public static boolean isTransparent(BlockState state) { + if (state == null) return true; + Block block = state.getBlock(); + if (block instanceof TransparentBlock) { + return true; + } + return !state.isOpaque(); + } + + public static int canPush(BlockState state) { + if (state == null) return 0; + if (state.getPistonBehavior() == PistonBehavior.NORMAL) return 1; + if (state.getPistonBehavior() == PistonBehavior.PUSH_ONLY) return 2; + return 0; + } } diff --git a/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/RegisterBlocks.java b/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/RegisterBlocks.java index 33234b3aa..dc971041d 100644 --- a/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/RegisterBlocks.java +++ b/client-mod/src/main/java/net/momirealms/craftengine/fabric/util/RegisterBlocks.java @@ -2,25 +2,29 @@ package net.momirealms.craftengine.fabric.util; import net.minecraft.block.AbstractBlock; import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.ShapeContext; +import net.minecraft.block.piston.PistonBehavior; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKeys; import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; -import net.minecraft.world.BlockView; import net.momirealms.craftengine.fabric.CraftEngineFabricMod; +import net.momirealms.craftengine.fabric.client.blocks.CustomBlock; import java.util.function.Function; public class RegisterBlocks { @SuppressWarnings("UnusedReturnValue") - public static Block register(String name, boolean canPassThrough, VoxelShape outlineShape) { - AbstractBlock.Settings settings = Block.Settings.create().nonOpaque().strength(-1.0F, 3600000.0F); + public static Block register(String name, boolean canPassThrough, + VoxelShape outlineShape, boolean isTransparent, + int canPush) { + AbstractBlock.Settings settings = Block.Settings.create() + .nonOpaque() + .strength(canPush != 0 ? 3600000.0F : -1.0F, 3600000.0F); + if (canPush == 1) settings.pistonBehavior(PistonBehavior.NORMAL); + if (canPush == 2) settings.pistonBehavior(PistonBehavior.PUSH_ONLY); VoxelShape collisionShape; if (canPassThrough) { collisionShape = VoxelShapes.empty(); @@ -28,7 +32,7 @@ public class RegisterBlocks { } else { collisionShape = outlineShape; } - return register(name, (settingsParam) -> new CustomBlock(settingsParam, outlineShape, collisionShape), settings); + return register(name, (settingsParam) -> new CustomBlock(settingsParam, outlineShape, collisionShape, isTransparent), settings); } public static Block register(String name, Function blockFactory, AbstractBlock.Settings settings) { @@ -44,23 +48,3 @@ public class RegisterBlocks { } -class CustomBlock extends Block { - private final VoxelShape outlineShape; - private final VoxelShape collisionShape; - - public CustomBlock(Settings settings, VoxelShape outlineShape, VoxelShape collisionShape) { - super(settings); - this.outlineShape = outlineShape; - this.collisionShape = collisionShape; - } - - @Override - public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { - return this.outlineShape; - } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { - return this.collisionShape; - } -} diff --git a/client-mod/src/main/resources/fabric.mod.json b/client-mod/src/main/resources/fabric.mod.json index 534f4f8f7..fc6261618 100644 --- a/client-mod/src/main/resources/fabric.mod.json +++ b/client-mod/src/main/resources/fabric.mod.json @@ -27,7 +27,7 @@ "depends": { "fabricloader": ">=${loader_version}", "fabric": "*", - "minecraft": "${minecraft_version}", + "minecraft": ">=${minecraft_version}", "modmenu": ">=${modmenu_version}", "cloth-config": ">=${cloth_version}" } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index 54727da4d..89c09e11c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.I18NData; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.shared.block.BlockBehavior; @@ -14,10 +15,7 @@ import net.momirealms.sparrow.nbt.Tag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.BiFunction; public abstract class CustomBlock { @@ -30,6 +28,8 @@ public abstract class CustomBlock { protected final ImmutableBlockState defaultState; @Nullable protected final LootTable lootTable; + private Map blockName = new HashMap<>(); + public CustomBlock( @NotNull Key id, @@ -159,4 +159,19 @@ public abstract class CustomBlock { } return state; } + + public Map blockName() { + return blockName; + } + + public void addBlockName(String lang, I18NData i18NData) { + if (this.blockName == null) { + this.blockName = new HashMap<>(); + } + this.blockName.put(lang, i18NData); + } + + public void setBlockName(Map blockName) { + this.blockName = blockName; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 7465a7fbd..0c79bebf1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -27,6 +27,8 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract void stopMiningBlock(); + public abstract void preventMiningBlock(); + public abstract void abortMiningBlock(); public abstract double getInteractionRange(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index 906751b4e..5e3792702 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -24,6 +24,7 @@ import net.momirealms.craftengine.core.plugin.gui.category.ItemBrowserManagerImp import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.locale.TranslationManagerImpl; import net.momirealms.craftengine.core.plugin.logger.PluginLogger; +import net.momirealms.craftengine.core.plugin.logger.filter.DisconnectLogFilter; import net.momirealms.craftengine.core.plugin.logger.filter.LogFilter; import net.momirealms.craftengine.core.plugin.network.NetworkManager; import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter; @@ -72,6 +73,7 @@ public abstract class CraftEngine implements Plugin { @Override public void load() { ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new LogFilter()); + ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new DisconnectLogFilter()); this.dependencyManager = new DependencyManagerImpl(this); ArrayList dependenciesToLoad = new ArrayList<>(); dependenciesToLoad.addAll(commonDependencies()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java index 1914dbb78..73ffa8ba7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigManager.java @@ -41,6 +41,7 @@ public class ConfigManager implements Reloadable { protected boolean debug; protected boolean checkUpdate; protected boolean metrics; + protected boolean filterConfigurationPhaseDisconnect; protected boolean resource_pack$generate_mod_assets; protected boolean resource_pack$override_uniform_font; @@ -181,6 +182,7 @@ public class ConfigManager implements Reloadable { debug = config.getBoolean("debug", false); metrics = config.getBoolean("metrics", false); checkUpdate = config.getBoolean("update-checker", false); + filterConfigurationPhaseDisconnect = config.getBoolean("filter-configuration-phase-disconnect", false); // resource pack resource_pack$override_uniform_font = config.getBoolean("resource-pack.override-uniform-font", false); @@ -309,6 +311,10 @@ public class ConfigManager implements Reloadable { return instance.metrics; } + public static boolean filterConfigurationPhaseDisconnect() { + return instance.filterConfigurationPhaseDisconnect; + } + public static boolean resourcePack$overrideUniform() { return instance.resource_pack$override_uniform_font; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/ClientLangMangerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/ClientLangMangerImpl.java index 29cc9fe41..b6bd0310f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/ClientLangMangerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/ClientLangMangerImpl.java @@ -67,6 +67,12 @@ public class ClientLangMangerImpl implements ClientLangManager { @Override public Map langData() { + this.plugin.blockManager().blocks().forEach((key, block) -> { + Map blockName = block.blockName(); + if (blockName != null) { + I18NData.merge(i18nData, blockName); + } + }); return Collections.unmodifiableMap(i18nData); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java index 9c05a8f43..febcbe746 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/I18NData.java @@ -20,4 +20,20 @@ public class I18NData { public String translate(String key) { return this.translations.get(key); } + + @Override + public String toString() { + return "I18NData{" + translations + "}"; + } + + public static void merge(Map target, Map source) { + source.forEach((key, value) -> { + I18NData copy = new I18NData(); + copy.addTranslations(value.translations); + target.merge(key, copy, (existing, newData) -> { + existing.addTranslations(newData.translations); + return existing; + }); + }); + } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java index cb8ec17e6..637fb75e9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import org.jetbrains.annotations.Nullable; import java.util.Locale; +import java.util.Map; public interface TranslationManager extends Reloadable, ConfigSectionParser { String CONFIG_SECTION_NAME = "i18n"; @@ -38,6 +39,8 @@ public interface TranslationManager extends Reloadable, ConfigSectionParser { return locale == null || locale.isEmpty() ? null : Translator.parseLocale(locale); } + Map i18nData(); + @Override default int loadingSequence() { return LoadingSequence.TRANSLATION; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java index 6c1640fde..43f99aa67 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java @@ -277,4 +277,9 @@ public class TranslationManagerImpl implements TranslationManager { return newFileContents; } } + + @Override + public Map i18nData() { + return Collections.unmodifiableMap(this.i18nData); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/filter/DisconnectLogFilter.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/filter/DisconnectLogFilter.java new file mode 100644 index 000000000..4e9e8136e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/filter/DisconnectLogFilter.java @@ -0,0 +1,36 @@ +package net.momirealms.craftengine.core.plugin.logger.filter; + + +import net.momirealms.craftengine.core.plugin.config.ConfigManager; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.Node; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.filter.AbstractFilter; +import org.apache.logging.log4j.core.impl.MutableLogEvent; + +@Plugin(name = "DisconnectLogFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE) +public class DisconnectLogFilter extends AbstractFilter { + private static final String TARGET_LOGGER = "net.minecraft.server.network.ServerConfigurationPacketListenerImpl"; + private static final String TARGET_MESSAGE_PATTERN = "{} lost connection: {}"; + + @Override + public Result filter(LogEvent event) { + if (!ConfigManager.filterConfigurationPhaseDisconnect()) { + return Result.NEUTRAL; + } + + if (!event.getLoggerName().equals(TARGET_LOGGER)) { + return Result.NEUTRAL; + } + + if (event.getMessage() instanceof MutableLogEvent msg) { + String format = msg.getFormat(); + + if (TARGET_MESSAGE_PATTERN.equals(format)) { + return Result.DENY; + } + } + return Result.NEUTRAL; + } +} diff --git a/gradle.properties b/gradle.properties index 70e7e59b2..fb5b60a46 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] project_version=0.0.39 -config_version=17 +config_version=18 lang_version=3 project_group=net.momirealms latest_minecraft_version=1.21.4