mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-19 15:09:15 +00:00
Merge pull request #61 from jhqwqmc/dev
feat: 优化客户端注册及服务端同步客户端方块及修复方块谓词刷物品
This commit is contained in:
@@ -8,6 +8,8 @@ metrics: true
|
|||||||
update-checker: true
|
update-checker: true
|
||||||
# Forces a specific locale (e.g., zh_cn)
|
# Forces a specific locale (e.g., zh_cn)
|
||||||
forced-locale: ''
|
forced-locale: ''
|
||||||
|
# Filter configuration phase player disconnection logs
|
||||||
|
filter-configuration-phase-disconnect: false
|
||||||
|
|
||||||
resource-pack:
|
resource-pack:
|
||||||
# Should those images in minecraft:default font also work in minecraft:uniform
|
# Should those images in minecraft:default font also work in minecraft:uniform
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ items#misc:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.chinese_lantern"
|
||||||
loot:
|
loot:
|
||||||
template: "default:loot_table/basic"
|
template: "default:loot_table/basic"
|
||||||
arguments:
|
arguments:
|
||||||
@@ -53,6 +54,7 @@ items#misc:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.netherite_anvil"
|
||||||
loot:
|
loot:
|
||||||
template: "default:loot_table/basic"
|
template: "default:loot_table/basic"
|
||||||
arguments:
|
arguments:
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ items:
|
|||||||
|
|
||||||
blocks:
|
blocks:
|
||||||
default:topaz_ore:
|
default:topaz_ore:
|
||||||
|
name: "i18n:item.topaz_ore"
|
||||||
loot:
|
loot:
|
||||||
template: "default:loot_table/ore"
|
template: "default:loot_table/ore"
|
||||||
arguments:
|
arguments:
|
||||||
@@ -63,6 +64,7 @@ blocks:
|
|||||||
arguments:
|
arguments:
|
||||||
path: "minecraft:block/custom/topaz_ore"
|
path: "minecraft:block/custom/topaz_ore"
|
||||||
default:deepslate_topaz_ore:
|
default:deepslate_topaz_ore:
|
||||||
|
name: "i18n:item.deepslate_topaz_ore"
|
||||||
loot:
|
loot:
|
||||||
template: "default:loot_table/ore"
|
template: "default:loot_table/ore"
|
||||||
arguments:
|
arguments:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ items:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.palm_log"
|
||||||
behavior:
|
behavior:
|
||||||
type: strippable_block
|
type: strippable_block
|
||||||
stripped: default:stripped_palm_log
|
stripped: default:stripped_palm_log
|
||||||
@@ -64,6 +65,7 @@ items:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.stripped_palm_log"
|
||||||
loot:
|
loot:
|
||||||
template: "default:loot_table/basic"
|
template: "default:loot_table/basic"
|
||||||
arguments:
|
arguments:
|
||||||
@@ -107,6 +109,7 @@ items:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.palm_wood"
|
||||||
behavior:
|
behavior:
|
||||||
type: strippable_block
|
type: strippable_block
|
||||||
stripped: default:stripped_palm_wood
|
stripped: default:stripped_palm_wood
|
||||||
@@ -153,6 +156,7 @@ items:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.stripped_palm_wood"
|
||||||
loot:
|
loot:
|
||||||
template: "default:loot_table/basic"
|
template: "default:loot_table/basic"
|
||||||
arguments:
|
arguments:
|
||||||
@@ -195,6 +199,7 @@ items:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.palm_planks"
|
||||||
settings:
|
settings:
|
||||||
template: "default:settings/planks"
|
template: "default:settings/planks"
|
||||||
overrides:
|
overrides:
|
||||||
@@ -225,6 +230,7 @@ items:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.palm_sapling"
|
||||||
settings:
|
settings:
|
||||||
template: "default:settings/sapling"
|
template: "default:settings/sapling"
|
||||||
overrides:
|
overrides:
|
||||||
@@ -287,6 +293,7 @@ items:
|
|||||||
behavior:
|
behavior:
|
||||||
type: block_item
|
type: block_item
|
||||||
block:
|
block:
|
||||||
|
name: "i18n:item.palm_leaves"
|
||||||
behavior:
|
behavior:
|
||||||
type: leaves_block
|
type: leaves_block
|
||||||
loot:
|
loot:
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ items:
|
|||||||
block: default:reed
|
block: default:reed
|
||||||
blocks:
|
blocks:
|
||||||
default:fairy_flower:
|
default:fairy_flower:
|
||||||
|
name: "i18n:item.fairy_flower"
|
||||||
settings:
|
settings:
|
||||||
template:
|
template:
|
||||||
- default:hardness/none
|
- default:hardness/none
|
||||||
@@ -64,6 +65,7 @@ blocks:
|
|||||||
"0": "minecraft:block/custom/fairy_flower_4"
|
"0": "minecraft:block/custom/fairy_flower_4"
|
||||||
default:reed:
|
default:reed:
|
||||||
settings:
|
settings:
|
||||||
|
name: "i18n:item.reed"
|
||||||
template:
|
template:
|
||||||
- default:hardness/none
|
- default:hardness/none
|
||||||
- default:sound/grass
|
- default:sound/grass
|
||||||
|
|||||||
@@ -452,7 +452,18 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
|||||||
// create block
|
// create block
|
||||||
Map<String, Object> behaviorSection = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
|
Map<String, Object> 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
|
// bind appearance
|
||||||
bindAppearance(block);
|
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) {
|
private void bindAppearance(CustomBlock block) {
|
||||||
for (ImmutableBlockState state : block.variantProvider().states()) {
|
for (ImmutableBlockState state : block.variantProvider().states()) {
|
||||||
ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
|
ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.block.*;
|
|||||||
import net.momirealms.craftengine.core.block.properties.Property;
|
import net.momirealms.craftengine.core.block.properties.Property;
|
||||||
import net.momirealms.craftengine.core.loot.LootTable;
|
import net.momirealms.craftengine.core.loot.LootTable;
|
||||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
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.registry.Holder;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.util.Tristate;
|
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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.network.impl.*;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
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.CraftEngine;
|
||||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||||
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
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.Listener;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
public class BukkitNetworkManager implements NetworkManager, Listener {
|
public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener {
|
||||||
private static BukkitNetworkManager instance;
|
private static BukkitNetworkManager instance;
|
||||||
private static final Map<Class<?>, TriConsumer<NetWorkUser, NMSPacketEvent, Object>> nmsPacketFunctions = new HashMap<>();
|
private static final Map<Class<?>, TriConsumer<NetWorkUser, NMSPacketEvent, Object>> nmsPacketFunctions = new HashMap<>();
|
||||||
private static final Map<Integer, BiConsumer<NetWorkUser, ByteBufPacketEvent>> byteBufPacketFunctions = new HashMap<>();
|
private static final Map<Integer, BiConsumer<NetWorkUser, ByteBufPacketEvent>> byteBufPacketFunctions = new HashMap<>();
|
||||||
@@ -131,6 +129,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
|
|||||||
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
|
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
|
||||||
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
|
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
|
||||||
registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket);
|
registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket);
|
||||||
|
registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, Reflections.clazz$ServerboundCustomPayloadPacket);
|
||||||
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
|
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
|
||||||
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
|
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
|
||||||
registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket());
|
registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket());
|
||||||
@@ -161,29 +160,21 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
|
|||||||
this.onlineUsers.remove(player.getUniqueId());
|
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
|
@Override
|
||||||
public Collection<BukkitServerPlayer> onlineUsers() {
|
public Collection<BukkitServerPlayer> onlineUsers() {
|
||||||
return onlineUsers.values();
|
return onlineUsers.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// 保留仅注册入频道用
|
||||||
|
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
if (init) return;
|
if (init) return;
|
||||||
try {
|
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 server = Reflections.method$MinecraftServer$getServer.invoke(null);
|
||||||
Object serverConnection = Reflections.field$MinecraftServer$connection.get(server);
|
Object serverConnection = Reflections.field$MinecraftServer$connection.get(server);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.plugin.network;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
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.CraftEngineFurniture;
|
||||||
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
|
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
|
||||||
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
|
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.config.ConfigManager;
|
||||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||||
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
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.util.*;
|
||||||
import net.momirealms.craftengine.core.world.BlockPos;
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
import net.momirealms.craftengine.core.world.chunk.Palette;
|
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.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
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 {
|
private static void handlePlayerActionPacketOnMainThread(BukkitServerPlayer player, World world, BlockPos pos, Object packet) throws Exception {
|
||||||
|
|
||||||
Object action = Reflections.field$ServerboundPlayerActionPacket$action.get(packet);
|
Object action = Reflections.field$ServerboundPlayerActionPacket$action.get(packet);
|
||||||
if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) {
|
if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) {
|
||||||
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
|
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
|
||||||
@@ -306,6 +309,28 @@ public class PacketConsumers {
|
|||||||
}
|
}
|
||||||
return;
|
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));
|
player.startMiningBlock(world, pos, blockState, true, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId));
|
||||||
} else if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) {
|
} else if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) {
|
||||||
if (player.isMiningBlock()) {
|
if (player.isMiningBlock()) {
|
||||||
@@ -875,4 +900,43 @@ public class PacketConsumers {
|
|||||||
callback.accept(new String(newCodepoints, 0, newCodepoints.length));
|
callback.accept(new String(newCodepoints, 0, newCodepoints.length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
|||||||
import net.momirealms.craftengine.bukkit.util.*;
|
import net.momirealms.craftengine.bukkit.util.*;
|
||||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
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.InteractionHand;
|
||||||
import net.momirealms.craftengine.core.entity.player.Player;
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
@@ -310,7 +311,8 @@ public class BukkitServerPlayer extends Player {
|
|||||||
if (custom && getDestroyProgress(state, pos) >= 1f) {
|
if (custom && getDestroyProgress(state, pos) >= 1f) {
|
||||||
assert immutableBlockState != null;
|
assert immutableBlockState != null;
|
||||||
// not an instant break on client side
|
// 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 {
|
try {
|
||||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(this.destroyedState), false);
|
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(this.destroyedState), false);
|
||||||
sendPacket(levelEventPacket, false);
|
sendPacket(levelEventPacket, false);
|
||||||
@@ -365,6 +367,13 @@ public class BukkitServerPlayer extends Player {
|
|||||||
setIsDestroyingBlock(false, false);
|
setIsDestroyingBlock(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preventMiningBlock() {
|
||||||
|
setCanBreakBlock(false);
|
||||||
|
setIsDestroyingBlock(false, false);
|
||||||
|
abortMiningBlock();
|
||||||
|
}
|
||||||
|
|
||||||
private void resetEffect(Object mobEffect) throws ReflectiveOperationException {
|
private void resetEffect(Object mobEffect) throws ReflectiveOperationException {
|
||||||
Object effectInstance = Reflections.method$ServerPlayer$getEffect.invoke(serverPlayer(), mobEffect);
|
Object effectInstance = Reflections.method$ServerPlayer$getEffect.invoke(serverPlayer(), mobEffect);
|
||||||
Object packet;
|
Object packet;
|
||||||
|
|||||||
@@ -5572,4 +5572,106 @@ public class Reflections {
|
|||||||
clazz$CraftEventFactory, boolean.class, new String[] { "handleBlockFormEvent" }, clazz$Level, clazz$BlockPos, clazz$BlockState, int.class
|
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
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,44 +2,42 @@ package net.momirealms.craftengine.fabric.client;
|
|||||||
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
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.ClientConfigurationConnectionEvents;
|
||||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
|
||||||
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
||||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.color.world.BiomeColors;
|
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.client.render.RenderLayer;
|
||||||
import net.minecraft.network.DisconnectionInfo;
|
|
||||||
import net.minecraft.registry.Registries;
|
import net.minecraft.registry.Registries;
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.world.biome.FoliageColors;
|
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.config.ModConfig;
|
||||||
import net.momirealms.craftengine.fabric.client.network.CraftEnginePayload;
|
import net.momirealms.craftengine.fabric.client.network.CraftEnginePayload;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class CraftEngineFabricModClient implements ClientModInitializer {
|
public class CraftEngineFabricModClient implements ClientModInitializer {
|
||||||
public static final String MOD_ID = "craftengine";
|
public static final String MOD_ID = "craftengine";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
PayloadTypeRegistry.playS2C().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC);
|
PayloadTypeRegistry.configurationS2C().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC);
|
||||||
initChannel(MinecraftClient.getInstance().getNetworkHandler());
|
PayloadTypeRegistry.configurationC2S().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC);
|
||||||
registerRenderLayer();
|
registerRenderLayer();
|
||||||
ClientPlayConnectionEvents.INIT.register((handler, client) -> initChannel(handler));
|
ClientConfigurationConnectionEvents.START.register(CraftEngineFabricModClient::initChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerRenderLayer() {
|
public static void registerRenderLayer() {
|
||||||
Registries.BLOCK.forEach(block -> {
|
Registries.BLOCK.forEach(block -> {
|
||||||
Identifier id = Registries.BLOCK.getId(block);
|
Identifier id = Registries.BLOCK.getId(block);
|
||||||
if (id.getNamespace().equals(CraftEngineFabricModClient.MOD_ID)) {
|
if (block instanceof CustomBlock customBlock) {
|
||||||
BlockRenderLayerMap.INSTANCE.putBlock(block, RenderLayer.getCutoutMipped());
|
if (customBlock.isTransparent()) {
|
||||||
|
BlockRenderLayerMap.INSTANCE.putBlock(customBlock, RenderLayer.getCutoutMipped());
|
||||||
|
}
|
||||||
if (id.getPath().contains("leaves")) {
|
if (id.getPath().contains("leaves")) {
|
||||||
registerColor(block);
|
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) {
|
if (ModConfig.enableNetwork) {
|
||||||
registerChannel(handler);
|
registerChannel(handler);
|
||||||
} else {
|
} else {
|
||||||
ClientPlayNetworking.unregisterGlobalReceiver(CraftEnginePayload.ID.id());
|
ClientConfigurationNetworking.unregisterGlobalReceiver(CraftEnginePayload.ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerChannel(ClientPlayNetworkHandler handler) {
|
private static void registerChannel(ClientConfigurationNetworkHandler handler) {
|
||||||
ClientPlayNetworking.registerGlobalReceiver(CraftEnginePayload.ID, (payload, context) -> {
|
ClientConfigurationNetworking.send(new CraftEnginePayload((":" + Block.STATE_IDS.size() + ":init").getBytes(StandardCharsets.UTF_8)));
|
||||||
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"))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package net.momirealms.craftengine.fabric.client.network;
|
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.codec.PacketCodec;
|
||||||
import net.minecraft.network.packet.CustomPayload;
|
import net.minecraft.network.packet.CustomPayload;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
@@ -8,10 +8,9 @@ import net.minecraft.util.Identifier;
|
|||||||
public record CraftEnginePayload(byte[] data) implements CustomPayload {
|
public record CraftEnginePayload(byte[] data) implements CustomPayload {
|
||||||
public static final Identifier CRAFTENGINE_PAYLOAD = Identifier.of("craftengine", "payload");
|
public static final Identifier CRAFTENGINE_PAYLOAD = Identifier.of("craftengine", "payload");
|
||||||
public static final Id<CraftEnginePayload> ID = new Id<>(CraftEnginePayload.CRAFTENGINE_PAYLOAD);
|
public static final Id<CraftEnginePayload> ID = new Id<>(CraftEnginePayload.CRAFTENGINE_PAYLOAD);
|
||||||
public static final PacketCodec<RegistryByteBuf, CraftEnginePayload> CODEC = PacketCodec.tuple(
|
public static final PacketCodec<PacketByteBuf, CraftEnginePayload> CODEC = PacketCodec.of(
|
||||||
new ByteArrayCodec(), CraftEnginePayload::data,
|
(payload, byteBuf) -> byteBuf.writeByteArray(payload.data()),
|
||||||
CraftEnginePayload::new
|
buf -> new CraftEnginePayload(buf.readByteArray()));
|
||||||
);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Id<? extends CustomPayload> getId() {
|
public Id<? extends CustomPayload> getId() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.fabric;
|
|||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.piston.PistonBehavior;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.momirealms.craftengine.fabric.client.config.ModConfig;
|
import net.momirealms.craftengine.fabric.client.config.ModConfig;
|
||||||
import net.momirealms.craftengine.fabric.util.BlockUtils;
|
import net.momirealms.craftengine.fabric.util.BlockUtils;
|
||||||
@@ -38,7 +39,9 @@ public class CraftEngineFabricMod implements ModInitializer {
|
|||||||
RegisterBlocks.register(
|
RegisterBlocks.register(
|
||||||
replacedBlockId.getPath() + "_" + i,
|
replacedBlockId.getPath() + "_" + i,
|
||||||
BlockUtils.canPassThrough(blockState),
|
BlockUtils.canPassThrough(blockState),
|
||||||
BlockUtils.getShape(blockState)
|
BlockUtils.getShape(blockState),
|
||||||
|
BlockUtils.isTransparent(blockState),
|
||||||
|
BlockUtils.canPush(blockState)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package net.momirealms.craftengine.fabric.util;
|
|||||||
import net.minecraft.block.AbstractBlock;
|
import net.minecraft.block.AbstractBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
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.math.BlockPos;
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
import net.minecraft.util.shape.VoxelShapes;
|
import net.minecraft.util.shape.VoxelShapes;
|
||||||
@@ -63,4 +65,20 @@ public class BlockUtils {
|
|||||||
return VoxelShapes.fullCube();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,29 @@ package net.momirealms.craftengine.fabric.util;
|
|||||||
|
|
||||||
import net.minecraft.block.AbstractBlock;
|
import net.minecraft.block.AbstractBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.piston.PistonBehavior;
|
||||||
import net.minecraft.block.ShapeContext;
|
|
||||||
import net.minecraft.registry.Registries;
|
import net.minecraft.registry.Registries;
|
||||||
import net.minecraft.registry.Registry;
|
import net.minecraft.registry.Registry;
|
||||||
import net.minecraft.registry.RegistryKey;
|
import net.minecraft.registry.RegistryKey;
|
||||||
import net.minecraft.registry.RegistryKeys;
|
import net.minecraft.registry.RegistryKeys;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
import net.minecraft.util.shape.VoxelShapes;
|
import net.minecraft.util.shape.VoxelShapes;
|
||||||
import net.minecraft.world.BlockView;
|
|
||||||
import net.momirealms.craftengine.fabric.CraftEngineFabricMod;
|
import net.momirealms.craftengine.fabric.CraftEngineFabricMod;
|
||||||
|
import net.momirealms.craftengine.fabric.client.blocks.CustomBlock;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class RegisterBlocks {
|
public class RegisterBlocks {
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
public static Block register(String name, boolean canPassThrough, VoxelShape outlineShape) {
|
public static Block register(String name, boolean canPassThrough,
|
||||||
AbstractBlock.Settings settings = Block.Settings.create().nonOpaque().strength(-1.0F, 3600000.0F);
|
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;
|
VoxelShape collisionShape;
|
||||||
if (canPassThrough) {
|
if (canPassThrough) {
|
||||||
collisionShape = VoxelShapes.empty();
|
collisionShape = VoxelShapes.empty();
|
||||||
@@ -28,7 +32,7 @@ public class RegisterBlocks {
|
|||||||
} else {
|
} else {
|
||||||
collisionShape = outlineShape;
|
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<AbstractBlock.Settings, Block> blockFactory, AbstractBlock.Settings settings) {
|
public static Block register(String name, Function<AbstractBlock.Settings, Block> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=${loader_version}",
|
"fabricloader": ">=${loader_version}",
|
||||||
"fabric": "*",
|
"fabric": "*",
|
||||||
"minecraft": "${minecraft_version}",
|
"minecraft": ">=${minecraft_version}",
|
||||||
"modmenu": ">=${modmenu_version}",
|
"modmenu": ">=${modmenu_version}",
|
||||||
"cloth-config": ">=${cloth_version}"
|
"cloth-config": ">=${cloth_version}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.item.context.BlockPlaceContext;
|
||||||
import net.momirealms.craftengine.core.loot.LootTable;
|
import net.momirealms.craftengine.core.loot.LootTable;
|
||||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
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.registry.Holder;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
public abstract class CustomBlock {
|
public abstract class CustomBlock {
|
||||||
@@ -30,6 +28,8 @@ public abstract class CustomBlock {
|
|||||||
protected final ImmutableBlockState defaultState;
|
protected final ImmutableBlockState defaultState;
|
||||||
@Nullable
|
@Nullable
|
||||||
protected final LootTable<?> lootTable;
|
protected final LootTable<?> lootTable;
|
||||||
|
private Map<String, I18NData> blockName = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
public CustomBlock(
|
public CustomBlock(
|
||||||
@NotNull Key id,
|
@NotNull Key id,
|
||||||
@@ -159,4 +159,19 @@ public abstract class CustomBlock {
|
|||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, I18NData> 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<String, I18NData> blockName) {
|
||||||
|
this.blockName = blockName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ public abstract class Player extends Entity implements NetWorkUser {
|
|||||||
|
|
||||||
public abstract void stopMiningBlock();
|
public abstract void stopMiningBlock();
|
||||||
|
|
||||||
|
public abstract void preventMiningBlock();
|
||||||
|
|
||||||
public abstract void abortMiningBlock();
|
public abstract void abortMiningBlock();
|
||||||
|
|
||||||
public abstract double getInteractionRange();
|
public abstract double getInteractionRange();
|
||||||
|
|||||||
@@ -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.TranslationManager;
|
||||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManagerImpl;
|
import net.momirealms.craftengine.core.plugin.locale.TranslationManagerImpl;
|
||||||
import net.momirealms.craftengine.core.plugin.logger.PluginLogger;
|
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.logger.filter.LogFilter;
|
||||||
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
|
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
|
||||||
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter;
|
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter;
|
||||||
@@ -72,6 +73,7 @@ public abstract class CraftEngine implements Plugin {
|
|||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new LogFilter());
|
((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);
|
this.dependencyManager = new DependencyManagerImpl(this);
|
||||||
ArrayList<Dependency> dependenciesToLoad = new ArrayList<>();
|
ArrayList<Dependency> dependenciesToLoad = new ArrayList<>();
|
||||||
dependenciesToLoad.addAll(commonDependencies());
|
dependenciesToLoad.addAll(commonDependencies());
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class ConfigManager implements Reloadable {
|
|||||||
protected boolean debug;
|
protected boolean debug;
|
||||||
protected boolean checkUpdate;
|
protected boolean checkUpdate;
|
||||||
protected boolean metrics;
|
protected boolean metrics;
|
||||||
|
protected boolean filterConfigurationPhaseDisconnect;
|
||||||
|
|
||||||
protected boolean resource_pack$generate_mod_assets;
|
protected boolean resource_pack$generate_mod_assets;
|
||||||
protected boolean resource_pack$override_uniform_font;
|
protected boolean resource_pack$override_uniform_font;
|
||||||
@@ -181,6 +182,7 @@ public class ConfigManager implements Reloadable {
|
|||||||
debug = config.getBoolean("debug", false);
|
debug = config.getBoolean("debug", false);
|
||||||
metrics = config.getBoolean("metrics", false);
|
metrics = config.getBoolean("metrics", false);
|
||||||
checkUpdate = config.getBoolean("update-checker", false);
|
checkUpdate = config.getBoolean("update-checker", false);
|
||||||
|
filterConfigurationPhaseDisconnect = config.getBoolean("filter-configuration-phase-disconnect", false);
|
||||||
|
|
||||||
// resource pack
|
// resource pack
|
||||||
resource_pack$override_uniform_font = config.getBoolean("resource-pack.override-uniform-font", false);
|
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;
|
return instance.metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean filterConfigurationPhaseDisconnect() {
|
||||||
|
return instance.filterConfigurationPhaseDisconnect;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean resourcePack$overrideUniform() {
|
public static boolean resourcePack$overrideUniform() {
|
||||||
return instance.resource_pack$override_uniform_font;
|
return instance.resource_pack$override_uniform_font;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ public class ClientLangMangerImpl implements ClientLangManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, I18NData> langData() {
|
public Map<String, I18NData> langData() {
|
||||||
|
this.plugin.blockManager().blocks().forEach((key, block) -> {
|
||||||
|
Map<String, I18NData> blockName = block.blockName();
|
||||||
|
if (blockName != null) {
|
||||||
|
I18NData.merge(i18nData, blockName);
|
||||||
|
}
|
||||||
|
});
|
||||||
return Collections.unmodifiableMap(i18nData);
|
return Collections.unmodifiableMap(i18nData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,20 @@ public class I18NData {
|
|||||||
public String translate(String key) {
|
public String translate(String key) {
|
||||||
return this.translations.get(key);
|
return this.translations.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "I18NData{" + translations + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void merge(Map<String, I18NData> target, Map<String, I18NData> 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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface TranslationManager extends Reloadable, ConfigSectionParser {
|
public interface TranslationManager extends Reloadable, ConfigSectionParser {
|
||||||
String CONFIG_SECTION_NAME = "i18n";
|
String CONFIG_SECTION_NAME = "i18n";
|
||||||
@@ -38,6 +39,8 @@ public interface TranslationManager extends Reloadable, ConfigSectionParser {
|
|||||||
return locale == null || locale.isEmpty() ? null : Translator.parseLocale(locale);
|
return locale == null || locale.isEmpty() ? null : Translator.parseLocale(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<Locale, I18NData> i18nData();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default int loadingSequence() {
|
default int loadingSequence() {
|
||||||
return LoadingSequence.TRANSLATION;
|
return LoadingSequence.TRANSLATION;
|
||||||
|
|||||||
@@ -277,4 +277,9 @@ public class TranslationManagerImpl implements TranslationManager {
|
|||||||
return newFileContents;
|
return newFileContents;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Locale, I18NData> i18nData() {
|
||||||
|
return Collections.unmodifiableMap(this.i18nData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G
|
|||||||
# Project settings
|
# Project settings
|
||||||
# Rule: [major update].[feature update].[bug fix]
|
# Rule: [major update].[feature update].[bug fix]
|
||||||
project_version=0.0.39
|
project_version=0.0.39
|
||||||
config_version=17
|
config_version=18
|
||||||
lang_version=3
|
lang_version=3
|
||||||
project_group=net.momirealms
|
project_group=net.momirealms
|
||||||
latest_minecraft_version=1.21.4
|
latest_minecraft_version=1.21.4
|
||||||
|
|||||||
Reference in New Issue
Block a user