mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-29 11:59:11 +00:00
根据版本加载配置文件
This commit is contained in:
@@ -34,6 +34,7 @@ import net.momirealms.craftengine.core.pack.ResourceLocation;
|
||||
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
|
||||
import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor;
|
||||
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
@@ -50,9 +51,14 @@ import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
@@ -605,22 +611,30 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
|
||||
private void loadMappingsAndAdditionalBlocks() {
|
||||
this.plugin.logger().info("Loading mappings.yml.");
|
||||
File mappingFile = new File(plugin.dataFolderFile(), "mappings.yml");
|
||||
YamlDocument mappings = Config.instance().loadOrCreateYamlData("mappings.yml");
|
||||
Map<String, String> blockStateMappings = loadBlockStateMappings(mappings);
|
||||
this.validateBlockStateMappings(mappingFile, blockStateMappings);
|
||||
Map<Integer, String> stateMap = new Int2ObjectOpenHashMap<>();
|
||||
Map<Key, Integer> blockTypeCounter = new LinkedHashMap<>();
|
||||
Map<Integer, Integer> appearanceMapper = new Int2IntOpenHashMap();
|
||||
Map<Key, List<Integer>> appearanceArranger = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : blockStateMappings.entrySet()) {
|
||||
this.processBlockStateMapping(mappingFile, entry, stateMap, blockTypeCounter, appearanceMapper, appearanceArranger);
|
||||
Path mappingsFile = this.plugin.dataFolderPath().resolve("mappings.yml");
|
||||
if (!Files.exists(mappingsFile)) {
|
||||
this.plugin.saveResource("mappings.yml");
|
||||
}
|
||||
File mappingFile = new File(plugin.dataFolderFile(), "mappings.yml");
|
||||
Yaml yaml = new Yaml(new StringKeyConstructor(null, new LoaderOptions()));
|
||||
try (InputStream inputStream = Files.newInputStream(mappingsFile)) {
|
||||
Map<String, String> blockStateMappings = loadBlockStateMappings(yaml.load(inputStream));
|
||||
this.validateBlockStateMappings(mappingFile, blockStateMappings);
|
||||
Map<Integer, String> stateMap = new Int2ObjectOpenHashMap<>();
|
||||
Map<Key, Integer> blockTypeCounter = new LinkedHashMap<>();
|
||||
Map<Integer, Integer> appearanceMapper = new Int2IntOpenHashMap();
|
||||
Map<Key, List<Integer>> appearanceArranger = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : blockStateMappings.entrySet()) {
|
||||
this.processBlockStateMapping(mappingFile, entry, stateMap, blockTypeCounter, appearanceMapper, appearanceArranger);
|
||||
}
|
||||
this.blockAppearanceMapper = ImmutableMap.copyOf(appearanceMapper);
|
||||
this.blockAppearanceArranger = ImmutableMap.copyOf(appearanceArranger);
|
||||
this.plugin.logger().info("Freed " + this.blockAppearanceMapper.size() + " block state appearances.");
|
||||
YamlDocument additionalYaml = Config.instance().loadOrCreateYamlData("additional-real-blocks.yml");
|
||||
this.registeredRealBlockSlots = this.buildRegisteredRealBlockSlots(blockTypeCounter, additionalYaml);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to init mappings.yml", e);
|
||||
}
|
||||
this.blockAppearanceMapper = ImmutableMap.copyOf(appearanceMapper);
|
||||
this.blockAppearanceArranger = ImmutableMap.copyOf(appearanceArranger);
|
||||
this.plugin.logger().info("Freed " + this.blockAppearanceMapper.size() + " block state appearances.");
|
||||
YamlDocument additionalYaml = Config.instance().loadOrCreateYamlData("additional-real-blocks.yml");
|
||||
this.registeredRealBlockSlots = this.buildRegisteredRealBlockSlots(blockTypeCounter, additionalYaml);
|
||||
}
|
||||
|
||||
private void recordVanillaNoteBlocks() {
|
||||
@@ -655,9 +669,9 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
return open ? this.affectedOpenableBlockSounds.get(block).left() : this.affectedOpenableBlockSounds.get(block).right();
|
||||
}
|
||||
|
||||
private Map<String, String> loadBlockStateMappings(YamlDocument mappings) {
|
||||
private Map<String, String> loadBlockStateMappings(Map<String, Object> mappings) {
|
||||
Map<String, String> blockStateMappings = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : mappings.getStringRouteMappedValues(false).entrySet()) {
|
||||
for (Map.Entry<String, Object> entry : mappings.entrySet()) {
|
||||
if (entry.getValue() instanceof String afterValue) {
|
||||
blockStateMappings.put(entry.getKey(), afterValue);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.block.properties;
|
||||
import net.momirealms.craftengine.core.block.state.properties.DoorHinge;
|
||||
import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf;
|
||||
import net.momirealms.craftengine.core.block.state.properties.SingleBlockHalf;
|
||||
import net.momirealms.craftengine.core.block.state.properties.StairsShape;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
@@ -22,6 +23,7 @@ public class Properties {
|
||||
public static final Key SINGLE_BLOCK_HALF = Key.of("craftengine:single_block_half");
|
||||
public static final Key DOUBLE_BLOCK_HALF = Key.of("craftengine:double_block_half");
|
||||
public static final Key HINGE = Key.of("craftengine:hinge");
|
||||
public static final Key STAIRS_SHAPE = Key.of("craftengine:stairs_shape");
|
||||
|
||||
static {
|
||||
register(BOOLEAN, BooleanProperty.FACTORY);
|
||||
@@ -35,6 +37,7 @@ public class Properties {
|
||||
register(SINGLE_BLOCK_HALF, new EnumProperty.Factory<>(SingleBlockHalf.class));
|
||||
register(DOUBLE_BLOCK_HALF, new EnumProperty.Factory<>(DoubleBlockHalf.class));
|
||||
register(HINGE, new EnumProperty.Factory<>(DoorHinge.class));
|
||||
register(STAIRS_SHAPE, new EnumProperty.Factory<>(StairsShape.class));
|
||||
}
|
||||
|
||||
public static void register(Key key, PropertyFactory factory) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.momirealms.craftengine.core.block.state.properties;
|
||||
|
||||
public enum StairsShape {
|
||||
STRAIGHT,
|
||||
INNER_LEFT,
|
||||
INNER_RIGHT,
|
||||
OUTER_LEFT,
|
||||
OUTER_RIGHT
|
||||
}
|
||||
@@ -1,38 +1,145 @@
|
||||
package net.momirealms.craftengine.core.plugin.config;
|
||||
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
import org.yaml.snakeyaml.nodes.ScalarNode;
|
||||
import org.yaml.snakeyaml.nodes.*;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class StringKeyConstructor extends SafeConstructor {
|
||||
private final Path path;
|
||||
private static final String VERSION_PREFIX = "$$";
|
||||
|
||||
public StringKeyConstructor(Path path, LoaderOptions loaderOptions) {
|
||||
super(loaderOptions);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
private boolean isVersionMatch(String versionSpec) {
|
||||
int index = versionSpec.indexOf('~');
|
||||
// 没有范围值
|
||||
if (index == -1) {
|
||||
char firstChar = versionSpec.charAt(0);
|
||||
if (firstChar == '>') {
|
||||
int version = VersionHelper.parseVersionToInteger(versionSpec);
|
||||
return versionSpec.charAt(1) == '=' ? VersionHelper.version() >= version : VersionHelper.version() > version;
|
||||
} else if (firstChar == '<') {
|
||||
int version = VersionHelper.parseVersionToInteger(versionSpec);
|
||||
return versionSpec.charAt(1) == '=' ? VersionHelper.version() <= version : VersionHelper.version() < version;
|
||||
} else {
|
||||
return VersionHelper.parseVersionToInteger(versionSpec) == VersionHelper.version();
|
||||
}
|
||||
} else {
|
||||
int min = VersionHelper.parseVersionToInteger(versionSpec.substring(0, index));
|
||||
int max = VersionHelper.parseVersionToInteger(versionSpec.substring(index + 1));
|
||||
return VersionHelper.version() >= min && VersionHelper.version() <= max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatcher: 决定一个节点是应该被解析为“版本化值”还是一个普通的Map。
|
||||
*/
|
||||
@Override
|
||||
public Object constructObject(Node node) {
|
||||
if (node instanceof MappingNode mappingNode) {
|
||||
if (isValueSelectorNode(mappingNode)) {
|
||||
// 场景B: 这是一个值选择器,解析它以获得单个值
|
||||
return constructVersionedValue(mappingNode);
|
||||
}
|
||||
}
|
||||
// 对于所有其他情况 (包括需要合并的Map),使用默认的构造逻辑
|
||||
// super.constructObject 会最终调用我们重写的 constructMapping
|
||||
return super.constructObject(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景A (块合并): 构造一个Map,同时处理其中的版本化块合并。
|
||||
*/
|
||||
@Override
|
||||
protected Map<Object, Object> constructMapping(MappingNode node) {
|
||||
Map<Object, Object> map = new LinkedHashMap<>();
|
||||
for (NodeTuple tuple : node.getValue()) {
|
||||
Node keyNode = tuple.getKeyNode();
|
||||
Node valueNode = tuple.getValueNode();
|
||||
if (!(keyNode instanceof ScalarNode)) continue;
|
||||
|
||||
String key = constructScalar((ScalarNode) keyNode);
|
||||
Object value = constructObject(valueNode);
|
||||
Object previous = map.put(key, value);
|
||||
if (previous != null) {
|
||||
TranslationManager.instance().log("warning.config.yaml.duplicated_key", this.path.toAbsolutePath().toString(), key, String.valueOf(node.getStartMark().getLine() + 1));
|
||||
|
||||
if (key.startsWith(VERSION_PREFIX)) {
|
||||
String versionSpec = key.substring(VERSION_PREFIX.length());
|
||||
if (isVersionMatch(versionSpec)) {
|
||||
Node valueNode = tuple.getValueNode();
|
||||
if (valueNode instanceof MappingNode) {
|
||||
map.putAll(constructMapping((MappingNode) valueNode));
|
||||
} else {
|
||||
logWarning("versioned_key_not_a_map", key, valueNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Node valueNode = tuple.getValueNode();
|
||||
Object value = constructObject(valueNode);
|
||||
Object previous = map.put(key, value);
|
||||
if (previous != null) {
|
||||
logWarning("duplicated_key", key, keyNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查一个MappingNode是否是“值选择器”(即所有键都以 '$$' 开头)。
|
||||
*/
|
||||
private boolean isValueSelectorNode(MappingNode node) {
|
||||
if (node.getValue().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (NodeTuple tuple : node.getValue()) {
|
||||
if (tuple.getKeyNode() instanceof ScalarNode scalarNode) {
|
||||
String key = scalarNode.getValue();
|
||||
if (!key.startsWith(VERSION_PREFIX)) {
|
||||
return false; // 发现一个普通键,因此它不是值选择器
|
||||
}
|
||||
} else {
|
||||
return false; // 键不是一个简单的字符串,不可能是值选择器
|
||||
}
|
||||
}
|
||||
return true; // 所有键都是版本化的
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景B (值选择): 从“值选择器”节点中解析出最终的单个值。
|
||||
*/
|
||||
private Object constructVersionedValue(MappingNode node) {
|
||||
Object fallbackValue = null;
|
||||
Object matchedValue = null;
|
||||
// 遍历所有版本键,寻找匹配项
|
||||
for (NodeTuple tuple : node.getValue()) {
|
||||
String key = ((ScalarNode) tuple.getKeyNode()).getValue();
|
||||
String versionSpec = key.substring(VERSION_PREFIX.length());
|
||||
if ("fallback".equals(versionSpec)) {
|
||||
// 找到备用值,先存起来
|
||||
fallbackValue = constructObject(tuple.getValueNode());
|
||||
continue;
|
||||
}
|
||||
if (isVersionMatch(versionSpec)) {
|
||||
// 找到一个匹配项,因为YAML是顺序的,后面的会覆盖前面的
|
||||
matchedValue = constructObject(tuple.getValueNode());
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有精确匹配的值,则使用它;否则,使用备用值
|
||||
return matchedValue != null ? matchedValue : fallbackValue;
|
||||
}
|
||||
|
||||
private void logWarning(String keyInLocale, String configKey, Node node) {
|
||||
TranslationManager.instance().log("warning.config.yaml." + keyInLocale,
|
||||
this.path.toAbsolutePath().toString(),
|
||||
configKey,
|
||||
String.valueOf(node.getStartMark().getLine() + 1));
|
||||
}
|
||||
}
|
||||
@@ -42,22 +42,22 @@ public class VersionHelper {
|
||||
|
||||
// 2001 = 1.20.1
|
||||
// 2104 = 1.21.4
|
||||
version = major * 100 + minor;
|
||||
version = parseVersionToInteger(versionString);
|
||||
|
||||
v1_20 = version >= 2000;
|
||||
v1_20_1 = version >= 2001;
|
||||
v1_20_2 = version >= 2002;
|
||||
v1_20_3 = version >= 2003;
|
||||
v1_20_4 = version >= 2004;
|
||||
v1_20_5 = version >= 2005;
|
||||
v1_20_6 = version >= 2006;
|
||||
v1_21 = version >= 2100;
|
||||
v1_21_1 = version >= 2101;
|
||||
v1_21_2 = version >= 2102;
|
||||
v1_21_3 = version >= 2103;
|
||||
v1_21_4 = version >= 2104;
|
||||
v1_21_5 = version >= 2105;
|
||||
v1_21_6 = version >= 2106;
|
||||
v1_20 = version >= 12000;
|
||||
v1_20_1 = version >= 12001;
|
||||
v1_20_2 = version >= 12002;
|
||||
v1_20_3 = version >= 12003;
|
||||
v1_20_4 = version >= 12004;
|
||||
v1_20_5 = version >= 12005;
|
||||
v1_20_6 = version >= 12006;
|
||||
v1_21 = version >= 12100;
|
||||
v1_21_1 = version >= 12101;
|
||||
v1_21_2 = version >= 12102;
|
||||
v1_21_3 = version >= 12103;
|
||||
v1_21_4 = version >= 12104;
|
||||
v1_21_5 = version >= 12105;
|
||||
v1_21_6 = version >= 12106;
|
||||
|
||||
majorVersion = major;
|
||||
minorVersion = minor;
|
||||
@@ -70,6 +70,34 @@ public class VersionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseVersionToInteger(String versionString) {
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
int currentNumber = 0;
|
||||
int part = 0;
|
||||
for (int i = 0; i < versionString.length(); i++) {
|
||||
char c = versionString.charAt(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
currentNumber = currentNumber * 10 + (c - '0');
|
||||
} else if (c == '.') {
|
||||
if (part == 1) {
|
||||
major = currentNumber;
|
||||
}
|
||||
part++;
|
||||
currentNumber = 0;
|
||||
if (part > 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (part == 1) {
|
||||
major = currentNumber;
|
||||
} else if (part == 2) {
|
||||
minor = currentNumber;
|
||||
}
|
||||
return 10000 + major * 100 + minor;
|
||||
}
|
||||
|
||||
public static int majorVersion() {
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user