9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-23 17:09:19 +00:00

初步分配

This commit is contained in:
XiaoMoMi
2025-09-27 18:32:02 +08:00
parent 8d17e34985
commit e64a8d5fed
32 changed files with 715 additions and 575 deletions

View File

@@ -120,7 +120,7 @@ public class BukkitAdvancementManager extends AbstractAdvancementManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (advancements.containsKey(id)) { if (advancements.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.advancement.duplicate", path, id); throw new LocalizedResourceConfigException("warning.config.advancement.duplicate", path, id);
} }

View File

@@ -131,7 +131,7 @@ public class AxeItemBehavior extends ItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
return INSTANCE; return INSTANCE;
} }
} }

View File

@@ -234,7 +234,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
Object id = arguments.get("block"); Object id = arguments.get("block");
if (id == null) { if (id == null) {
throw new LocalizedResourceConfigException("warning.config.item.behavior.block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for block_item behavior")); throw new LocalizedResourceConfigException("warning.config.item.behavior.block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for block_item behavior"));
@@ -242,9 +242,9 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
if (id instanceof Map<?, ?> map) { if (id instanceof Map<?, ?> map) {
if (map.containsKey(key.toString())) { if (map.containsKey(key.toString())) {
// 防呆 // 防呆
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false));
} else { } else {
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map, false));
} }
return new BlockItemBehavior(key); return new BlockItemBehavior(key);
} else { } else {

View File

@@ -79,7 +79,7 @@ public class CompostableItemBehavior extends ItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
double chance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("chance", 0.55), "chance"); double chance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("chance", 0.55), "chance");
return new CompostableItemBehavior(chance); return new CompostableItemBehavior(chance);
} }

View File

@@ -40,7 +40,7 @@ public class DoubleHighBlockItemBehavior extends BlockItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
Object id = arguments.get("block"); Object id = arguments.get("block");
if (id == null) { if (id == null) {
throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block", new IllegalArgumentException("Missing required parameter 'block' for double_high_block_item behavior")); throw new LocalizedResourceConfigException("warning.config.item.behavior.double_high.missing_block", new IllegalArgumentException("Missing required parameter 'block' for double_high_block_item behavior"));
@@ -48,9 +48,9 @@ public class DoubleHighBlockItemBehavior extends BlockItemBehavior {
if (id instanceof Map<?, ?> map) { if (id instanceof Map<?, ?> map) {
if (map.containsKey(key.toString())) { if (map.containsKey(key.toString())) {
// 防呆 // 防呆
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false));
} else { } else {
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map, false));
} }
return new DoubleHighBlockItemBehavior(key); return new DoubleHighBlockItemBehavior(key);
} else { } else {

View File

@@ -161,7 +161,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key id, Map<String, Object> arguments) {
return INSTANCE; return INSTANCE;
} }
} }

View File

@@ -177,7 +177,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
Object id = arguments.get("furniture"); Object id = arguments.get("furniture");
if (id == null) { if (id == null) {
throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior")); throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior"));
@@ -185,9 +185,9 @@ public class FurnitureItemBehavior extends ItemBehavior {
if (id instanceof Map<?,?> map) { if (id instanceof Map<?,?> map) {
if (map.containsKey(key.toString())) { if (map.containsKey(key.toString())) {
// 防呆 // 防呆
BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); BukkitFurnitureManager.instance().parser().parseSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false));
} else { } else {
BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); BukkitFurnitureManager.instance().parser().parseSection(pack, path, node, key, MiscUtils.castToMap(map, false));
} }
return new FurnitureItemBehavior(key); return new FurnitureItemBehavior(key);
} else { } else {

View File

@@ -70,7 +70,7 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
Object id = arguments.get("block"); Object id = arguments.get("block");
if (id == null) { if (id == null) {
throw new LocalizedResourceConfigException("warning.config.item.behavior.liquid_collision.missing_block", new IllegalArgumentException("Missing required parameter 'block' for liquid_collision_block_item behavior")); throw new LocalizedResourceConfigException("warning.config.item.behavior.liquid_collision.missing_block", new IllegalArgumentException("Missing required parameter 'block' for liquid_collision_block_item behavior"));
@@ -79,9 +79,9 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
if (id instanceof Map<?, ?> map) { if (id instanceof Map<?, ?> map) {
if (map.containsKey(key.toString())) { if (map.containsKey(key.toString())) {
// 防呆 // 防呆
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false));
} else { } else {
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map, false));
} }
return new LiquidCollisionBlockItemBehavior(key, offset); return new LiquidCollisionBlockItemBehavior(key, offset);
} else { } else {

View File

@@ -35,7 +35,7 @@ public class WallBlockItemBehavior extends BlockItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
Object id = arguments.get("block"); Object id = arguments.get("block");
if (id == null) { if (id == null) {
throw new LocalizedResourceConfigException("warning.config.item.behavior.wall_block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for wall_block_item behavior")); throw new LocalizedResourceConfigException("warning.config.item.behavior.wall_block.missing_block", new IllegalArgumentException("Missing required parameter 'block' for wall_block_item behavior"));
@@ -43,9 +43,9 @@ public class WallBlockItemBehavior extends BlockItemBehavior {
if (id instanceof Map<?, ?> map) { if (id instanceof Map<?, ?> map) {
if (map.containsKey(key.toString())) { if (map.containsKey(key.toString())) {
// 防呆 // 防呆
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false));
} else { } else {
BukkitBlockManager.instance().blockParser().parseSection(pack, path, key, MiscUtils.castToMap(map, false)); BukkitBlockManager.instance().blockParser().parseSection(pack, path, node, key, MiscUtils.castToMap(map, false));
} }
return new WallBlockItemBehavior(key); return new WallBlockItemBehavior(key);
} else { } else {

View File

@@ -105,7 +105,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type"); String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type");
VanillaLoot.Type typeEnum; VanillaLoot.Type typeEnum;
try { try {

View File

@@ -150,6 +150,11 @@ item:
click-in-inventory: false # this option won't work for players in creative mode click-in-inventory: false # this option won't work for players in creative mode
drop: false drop: false
pick-up: false pick-up: false
# Decided the starting value for automatic custom model data assignment.
custom-model-data-starting-value:
default: 10000
overrides:
paper: 20000
equipment: equipment:
# The sacrificed-vanilla-armor argument determines which vanilla armor gets completely removed (loses all its trims) # The sacrificed-vanilla-armor argument determines which vanilla armor gets completely removed (loses all its trims)

View File

@@ -20,7 +20,6 @@ import net.momirealms.craftengine.core.plugin.config.*;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.*;
import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.Suggestion;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -238,6 +237,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
@Override @Override
public void parseSection(Pack pack, Path path, Map<String, Object> section) throws LocalizedException { public void parseSection(Pack pack, Path path, Map<String, Object> section) throws LocalizedException {
ExceptionCollector<LocalizedResourceConfigException> exceptionCollector = new ExceptionCollector<>();
for (Map.Entry<String, Object> entry : section.entrySet()) { for (Map.Entry<String, Object> entry : section.entrySet()) {
String before = entry.getKey(); String before = entry.getKey();
String after = entry.getValue().toString(); String after = entry.getValue().toString();
@@ -245,21 +245,25 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
BlockStateWrapper beforeState = createVanillaBlockState(before); BlockStateWrapper beforeState = createVanillaBlockState(before);
BlockStateWrapper afterState = createVanillaBlockState(before); BlockStateWrapper afterState = createVanillaBlockState(before);
if (beforeState == null) { if (beforeState == null) {
TranslationManager.instance().log("warning.config.block_state_mapping.invalid_state", path.toString(), before); exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block_state_mapping.invalid_state", before));
return; continue;
} }
if (afterState == null) { if (afterState == null) {
TranslationManager.instance().log("warning.config.block_state_mapping.invalid_state", path.toString(), after); exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block_state_mapping.invalid_state", after));
return; continue;
} }
int previous = AbstractBlockManager.this.blockStateMappings[beforeState.registryId()]; int previous = AbstractBlockManager.this.blockStateMappings[beforeState.registryId()];
if (previous != -1 && previous != afterState.registryId()) { if (previous != -1 && previous != afterState.registryId()) {
TranslationManager.instance().log("warning.config.block_state_mapping.conflict", path.toString(), beforeState.toString(), afterState.toString(), BlockRegistryMirror.byId(previous).toString()); exceptionCollector.add(new LocalizedResourceConfigException("warning.config.block_state_mapping.conflict",
return; beforeState.toString(),
afterState.toString(),
BlockRegistryMirror.byId(previous).toString()));
continue;
} }
AbstractBlockManager.this.blockStateMappings[beforeState.registryId()] = afterState.registryId(); AbstractBlockManager.this.blockStateMappings[beforeState.registryId()] = afterState.registryId();
AbstractBlockManager.this.blockStateArranger.computeIfAbsent(getBlockOwnerId(beforeState), k -> new ArrayList<>()).add(afterState); AbstractBlockManager.this.blockStateArranger.computeIfAbsent(getBlockOwnerId(beforeState), k -> new ArrayList<>()).add(afterState);
} }
exceptionCollector.throwIfPresent();
} }
} }
@@ -277,19 +281,19 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (isVanillaBlock(id)) { if (isVanillaBlock(id)) {
parseVanillaBlock(pack, path, id, section); parseVanillaBlock(id, section);
} else { } else {
// check duplicated config // check duplicated config
if (AbstractBlockManager.this.byId.containsKey(id)) { if (AbstractBlockManager.this.byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.block.duplicate"); throw new LocalizedResourceConfigException("warning.config.block.duplicate");
} }
parseCustomBlock(pack, path, id, section); parseCustomBlock(id, section);
} }
} }
private void parseVanillaBlock(Pack pack, Path path, Key id, Map<String, Object> section) { private void parseVanillaBlock(Key id, Map<String, Object> section) {
Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), true); Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), true);
if (settings != null) { if (settings != null) {
Object clientBoundTags = settings.get("client-bound-tags"); Object clientBoundTags = settings.get("client-bound-tags");
@@ -300,7 +304,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
} }
} }
private void parseCustomBlock(Pack pack, Path path, Key id, Map<String, Object> section) { private void parseCustomBlock(Key id, Map<String, Object> section) {
// 获取方块设置 // 获取方块设置
BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true)); BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true));
// 读取基础外观配置 // 读取基础外观配置

View File

@@ -89,7 +89,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (byId.containsKey(id)) { if (byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.furniture.duplicate"); throw new LocalizedResourceConfigException("warning.config.furniture.duplicate");
} }

View File

@@ -386,18 +386,18 @@ public abstract class AbstractFontManager implements FontManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (emojis.containsKey(id)) { if (emojis.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.emoji.duplicate", path, id); throw new LocalizedResourceConfigException("warning.config.emoji.duplicate");
} }
String permission = (String) section.get("permission"); String permission = (String) section.get("permission");
Object keywordsRaw = section.get("keywords"); Object keywordsRaw = section.get("keywords");
if (keywordsRaw == null) { if (keywordsRaw == null) {
throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id); throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords");
} }
List<String> keywords = MiscUtils.getAsStringList(keywordsRaw); List<String> keywords = MiscUtils.getAsStringList(keywordsRaw);
if (keywords.isEmpty()) { if (keywords.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords", path, id); throw new LocalizedResourceConfigException("warning.config.emoji.missing_keywords");
} }
Object rawContent = section.getOrDefault("content", "<white><arg:emoji></white>"); Object rawContent = section.getOrDefault("content", "<white><arg:emoji></white>");
String content; String content;
@@ -416,7 +416,7 @@ public abstract class AbstractFontManager implements FontManager {
if (bitmapImage.isPresent()) { if (bitmapImage.isPresent()) {
image = bitmapImage.get().miniMessageAt(0, 0); image = bitmapImage.get().miniMessageAt(0, 0);
} else { } else {
throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage);
} }
} else if (split.length == 4) { } else if (split.length == 4) {
Key imageId = new Key(split[0], split[1]); Key imageId = new Key(split[0], split[1]);
@@ -425,13 +425,13 @@ public abstract class AbstractFontManager implements FontManager {
try { try {
image = bitmapImage.get().miniMessageAt(Integer.parseInt(split[2]), Integer.parseInt(split[3])); image = bitmapImage.get().miniMessageAt(Integer.parseInt(split[2]), Integer.parseInt(split[3]));
} catch (ArrayIndexOutOfBoundsException e) { } catch (ArrayIndexOutOfBoundsException e) {
throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage);
} }
} else { } else {
throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage);
} }
} else { } else {
throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", path, id, rawImage); throw new LocalizedResourceConfigException("warning.config.emoji.invalid_image", rawImage);
} }
} }
Emoji emoji = new Emoji(content, permission, image, keywords); Emoji emoji = new Emoji(content, permission, image, keywords);
@@ -453,24 +453,24 @@ public abstract class AbstractFontManager implements FontManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (images.containsKey(id)) { if (images.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.image.duplicate", path, id); throw new LocalizedResourceConfigException("warning.config.image.duplicate");
} }
Object file = section.get("file"); Object file = section.get("file");
if (file == null) { if (file == null) {
throw new LocalizedResourceConfigException("warning.config.image.missing_file", path, id); throw new LocalizedResourceConfigException("warning.config.image.missing_file");
} }
String resourceLocation = CharacterUtils.replaceBackslashWithSlash(file.toString()); String resourceLocation = CharacterUtils.replaceBackslashWithSlash(file.toString());
if (!ResourceLocation.isValid(resourceLocation)) { if (!ResourceLocation.isValid(resourceLocation)) {
throw new LocalizedResourceConfigException("warning.config.image.invalid_file_chars", path, id, resourceLocation); throw new LocalizedResourceConfigException("warning.config.image.invalid_file_chars", resourceLocation);
} }
String fontName = section.getOrDefault("font", "minecraft:default").toString(); String fontName = section.getOrDefault("font", "minecraft:default").toString();
if (!ResourceLocation.isValid(fontName)) { if (!ResourceLocation.isValid(fontName)) {
throw new LocalizedResourceConfigException("warning.config.image.invalid_font_chars", path, id, fontName); throw new LocalizedResourceConfigException("warning.config.image.invalid_font_chars", fontName);
} }
Key fontKey = Key.withDefaultNamespace(fontName, id.namespace()); Key fontKey = Key.withDefaultNamespace(fontName, id.namespace());
@@ -478,7 +478,7 @@ public abstract class AbstractFontManager implements FontManager {
List<char[]> chars; List<char[]> chars;
Object charsObj = ResourceConfigUtils.get(section, "chars", "char"); Object charsObj = ResourceConfigUtils.get(section, "chars", "char");
if (charsObj == null) { if (charsObj == null) {
throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); throw new LocalizedResourceConfigException("warning.config.image.missing_char");
} }
if (charsObj instanceof List<?> list) { if (charsObj instanceof List<?> list) {
chars = MiscUtils.getAsStringList(list).stream().map(it -> { chars = MiscUtils.getAsStringList(list).stream().map(it -> {
@@ -489,7 +489,7 @@ public abstract class AbstractFontManager implements FontManager {
} }
}).toList(); }).toList();
if (chars.isEmpty()) { if (chars.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); throw new LocalizedResourceConfigException("warning.config.image.missing_char");
} }
} else { } else {
if (charsObj instanceof Integer integer) { if (charsObj instanceof Integer integer) {
@@ -497,7 +497,7 @@ public abstract class AbstractFontManager implements FontManager {
} else { } else {
String character = charsObj.toString(); String character = charsObj.toString();
if (character.isEmpty()) { if (character.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); throw new LocalizedResourceConfigException("warning.config.image.missing_char");
} }
if (character.length() == 1) { if (character.length() == 1) {
chars = List.of(character.toCharArray()); chars = List.of(character.toCharArray());
@@ -522,7 +522,7 @@ public abstract class AbstractFontManager implements FontManager {
for (int codepoint : codepoints) { for (int codepoint : codepoints) {
if (font.isCodepointInUse(codepoint)) { if (font.isCodepointInUse(codepoint)) {
BitmapImage image = font.bitmapImageByCodepoint(codepoint); BitmapImage image = font.bitmapImageByCodepoint(codepoint);
throw new LocalizedResourceConfigException("warning.config.image.codepoint_conflict", path, id, throw new LocalizedResourceConfigException("warning.config.image.codepoint_conflict",
fontKey.toString(), fontKey.toString(),
CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)), CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)),
new String(Character.toChars(codepoint)), new String(Character.toChars(codepoint)),
@@ -530,12 +530,12 @@ public abstract class AbstractFontManager implements FontManager {
} }
} }
if (codepoints.length == 0) { if (codepoints.length == 0) {
throw new LocalizedResourceConfigException("warning.config.image.missing_char", path, id); throw new LocalizedResourceConfigException("warning.config.image.missing_char");
} }
codepointGrid[i] = codepoints; codepointGrid[i] = codepoints;
if (size == -1) size = codepoints.length; if (size == -1) size = codepoints.length;
if (size != codepoints.length) { if (size != codepoints.length) {
throw new LocalizedResourceConfigException("warning.config.image.invalid_codepoint_grid", path, id); throw new LocalizedResourceConfigException("warning.config.image.invalid_codepoint_grid");
} }
} }
@@ -558,14 +558,14 @@ public abstract class AbstractFontManager implements FontManager {
return; return;
} }
} else { } else {
throw new LocalizedResourceConfigException("warning.config.image.missing_height", path, id); throw new LocalizedResourceConfigException("warning.config.image.missing_height");
} }
} }
int height = ResourceConfigUtils.getAsInt(heightObj, "height"); int height = ResourceConfigUtils.getAsInt(heightObj, "height");
int ascent = ResourceConfigUtils.getAsInt(section.getOrDefault("ascent", height - 1), "ascent"); int ascent = ResourceConfigUtils.getAsInt(section.getOrDefault("ascent", height - 1), "ascent");
if (height < ascent) { if (height < ascent) {
throw new LocalizedResourceConfigException("warning.config.image.height_ascent_conflict", path, id, String.valueOf(height), String.valueOf(ascent)); throw new LocalizedResourceConfigException("warning.config.image.height_ascent_conflict", String.valueOf(height), String.valueOf(ascent));
} }
BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, resourceLocation, codepointGrid); BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, resourceLocation, codepointGrid);

View File

@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.pack.AbstractPackManager;
import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.ResourceLocation;
import net.momirealms.craftengine.core.pack.cache.IdAllocator;
import net.momirealms.craftengine.core.pack.model.*; import net.momirealms.craftengine.core.pack.model.*;
import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator; import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
@@ -32,8 +33,10 @@ import net.momirealms.craftengine.core.util.*;
import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.type.Either; import org.incendo.cloud.type.Either;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -282,7 +285,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (AbstractItemManager.this.equipments.containsKey(id)) { if (AbstractItemManager.this.equipments.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.equipment.duplicate"); throw new LocalizedResourceConfigException("warning.config.equipment.duplicate");
} }
@@ -313,6 +316,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
public class ItemParser implements IdSectionConfigParser { public class ItemParser implements IdSectionConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"}; public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"};
private final Map<Key, IdAllocator> idAllocators = new HashMap<>();
private boolean isModernFormatRequired() { private boolean isModernFormatRequired() {
return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4); return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4);
@@ -322,6 +326,18 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4); return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4);
} }
private boolean needsCustomModelDataCompatibility() {
return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_2);
}
private boolean needsItemModelCompatibility() {
return Config.packMaxVersion().isAbove(MinecraftVersions.V1_21_2);
}
public Map<Key, IdAllocator> idAllocators() {
return this.idAllocators;
}
@Override @Override
public String[] sectionId() { public String[] sectionId() {
return CONFIG_SECTION_NAME; return CONFIG_SECTION_NAME;
@@ -333,55 +349,112 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void preProcess() {
this.idAllocators.clear();
}
@Override
public void postProcess() {
for (Map.Entry<Key, IdAllocator> entry : this.idAllocators.entrySet()) {
entry.getValue().processPendingAllocations();
try {
entry.getValue().saveToCache();
} catch (IOException e) {
AbstractItemManager.this.plugin.logger().warn("Error while saving custom model data allocation for material " + entry.getKey().asString(), e);
}
}
}
// 创建或获取已有的自动分配器
private IdAllocator getOrCreateIdAllocator(Key key) {
return this.idAllocators.computeIfAbsent(key, k -> {
IdAllocator newAllocator = new IdAllocator(plugin.dataFolderPath().resolve("cache").resolve("custom-model-data").resolve(k.value() + ".json"));
newAllocator.reset(Config.customModelDataStartingValue(k), 16_777_216);
try {
newAllocator.loadFromCache();
} catch (IOException e) {
AbstractItemManager.this.plugin.logger().warn("Error while loading custom model data from cache for material " + k.asString(), e);
}
return newAllocator;
});
}
@Override
public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (AbstractItemManager.this.customItemsById.containsKey(id)) { if (AbstractItemManager.this.customItemsById.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.item.duplicate"); throw new LocalizedResourceConfigException("warning.config.item.duplicate");
} }
// 创建UniqueKey仅缓存用
UniqueKey uniqueId = UniqueKey.create(id); UniqueKey uniqueId = UniqueKey.create(id);
// 判断是不是原版物品 // 判断是不是原版物品
boolean isVanillaItem = isVanillaItem(id); boolean isVanillaItem = isVanillaItem(id);
Key material = Key.from(isVanillaItem ? id.value() : ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("material"), "warning.config.item.missing_material").toLowerCase(Locale.ENGLISH));
Key clientBoundMaterial = section.containsKey("client-bound-material") ? Key.from(section.get("client-bound-material").toString().toLowerCase(Locale.ENGLISH)) : material; // 读取服务端侧材质
// 如果是原版物品那么custom-model-data只能是0即使用户设置了其他值 Key material = isVanillaItem ? id : Key.from(ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("material"), "warning.config.item.missing_material").toLowerCase(Locale.ROOT));
int customModelData = isVanillaItem ? 0 : ResourceConfigUtils.getAsInt(section.getOrDefault("custom-model-data", 0), "custom-model-data"); // 读取客户端侧材质
boolean clientBoundModel = section.containsKey("client-bound-model") ? ResourceConfigUtils.getAsBoolean(section.get("client-bound-model"), "client-bound-model") : Config.globalClientboundModel(); Key clientBoundMaterial = VersionHelper.PREMIUM && section.containsKey("client-bound-material") ? Key.from(section.get("client-bound-material").toString().toLowerCase(Locale.ROOT)) : material;
// custom model data
CompletableFuture<Integer> customModelDataFuture;
if (!isVanillaItem) {
// 如果用户指定了,说明要手动分配,不管他是什么版本,都强制设置模型值
if (section.containsKey("custom-model-data")) {
int customModelData = ResourceConfigUtils.getAsInt(section.getOrDefault("custom-model-data", 0), "custom-model-data");
if (customModelData < 0) { if (customModelData < 0) {
throw new LocalizedResourceConfigException("warning.config.item.invalid_custom_model_data", String.valueOf(customModelData)); throw new LocalizedResourceConfigException("warning.config.item.invalid_custom_model_data", String.valueOf(customModelData));
} }
if (customModelData > 16_777_216) { if (customModelData > 16_777_216) {
throw new LocalizedResourceConfigException("warning.config.item.bad_custom_model_data", String.valueOf(customModelData)); throw new LocalizedResourceConfigException("warning.config.item.bad_custom_model_data", String.valueOf(customModelData));
} }
customModelDataFuture = getOrCreateIdAllocator(clientBoundMaterial).assignFixedId(id.asString(), customModelData);
}
// 用户没指定custom-model-data则看当前资源包版本兼容需求
else {
// 如果最低版本要1.21.1以下支持
if (needsCustomModelDataCompatibility()) {
customModelDataFuture = getOrCreateIdAllocator(clientBoundMaterial).requestAutoId(id.asString());
}
// 否则不主动分配模型值
else {
customModelDataFuture = CompletableFuture.completedFuture(0);
}
}
} else {
// 原版物品不应该有这个
customModelDataFuture = CompletableFuture.completedFuture(0);
}
// item-model值 // 是否使用客户端侧模型
Key itemModelKey = null; boolean clientBoundModel = VersionHelper.PREMIUM && (section.containsKey("client-bound-model") ? ResourceConfigUtils.getAsBoolean(section.get("client-bound-model"), "client-bound-model") : Config.globalClientboundModel());
// 当模型值完成分配的时候
customModelDataFuture.thenAccept(customModelData -> ResourceConfigUtils.runCatching(path, node, () -> {
// item model
Key itemModel = null;
// 如果这个版本可以使用 item model
if (!isVanillaItem && needsItemModelCompatibility()) {
// 如果用户主动设定了item model那么肯定要设置
if (section.containsKey("item-model")) {
itemModel = Key.from(section.get("item-model").toString());
}
// 用户没设置item model也没设置custom model data那么为他生成一个基于物品id的item model
else if (customModelData == 0) {
itemModel = id;
}
// 用户没设置item model但是有custom model data那么就使用custom model data
}
CustomItem.Builder<I> itemBuilder = createPlatformItemBuilder(uniqueId, material, clientBoundMaterial); CustomItem.Builder<I> itemBuilder = createPlatformItemBuilder(uniqueId, material, clientBoundMaterial);
boolean hasItemModelSection = section.containsKey("item-model");
// 如果custom-model-data不为0
if (customModelData > 0) { if (customModelData > 0) {
if (clientBoundModel) itemBuilder.clientBoundDataModifier(new CustomModelDataModifier<>(customModelData)); if (clientBoundModel) itemBuilder.clientBoundDataModifier(new CustomModelDataModifier<>(customModelData));
else itemBuilder.dataModifier(new CustomModelDataModifier<>(customModelData)); else itemBuilder.dataModifier(new CustomModelDataModifier<>(customModelData));
} }
// 如果没有item-model选项被配置,同时这个物品又含有 model 区域 if (itemModel != null) {
else if (!hasItemModelSection && section.containsKey("model") && VersionHelper.isOrAbove1_21_2()) { if (clientBoundModel) itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModel));
// 那么使用物品id当成item-model的值 else itemBuilder.dataModifier(new ItemModelModifier<>(itemModel));
itemModelKey = Key.from(section.getOrDefault("item-model", id.toString()).toString());
// 但是有个前提id必须是有效的resource location
if (ResourceLocation.isValid(itemModelKey.toString())) {
if (clientBoundModel) itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey));
else itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey));
} else {
itemModelKey = null;
}
}
// 如果有item-model
if (hasItemModelSection && VersionHelper.isOrAbove1_21_2()) {
itemModelKey = Key.from(section.get("item-model").toString());
if (clientBoundModel) itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey));
else itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey));
} }
// 对于不重要的配置,可以仅警告,不返回 // 对于不重要的配置,可以仅警告,不返回
@@ -393,6 +466,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
} catch (LocalizedResourceConfigException e) { } catch (LocalizedResourceConfigException e) {
collector.add(e); collector.add(e);
} }
// 应用客户端侧数据 // 应用客户端侧数据
try { try {
if (VersionHelper.PREMIUM) { if (VersionHelper.PREMIUM) {
@@ -430,7 +504,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
// 行为 // 行为
List<ItemBehavior> behaviors; List<ItemBehavior> behaviors;
try { try {
behaviors = ItemBehaviors.fromObj(pack, path, id, ResourceConfigUtils.get(section, "behavior", "behaviors")); behaviors = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(section, "behavior", "behaviors"), map -> ItemBehaviors.fromMap(pack, path, node, id, map));
} catch (LocalizedResourceConfigException e) { } catch (LocalizedResourceConfigException e) {
collector.add(e); collector.add(e);
behaviors = Collections.emptyList(); behaviors = Collections.emptyList();
@@ -471,6 +545,14 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
AbstractItemManager.this.plugin.itemBrowserManager().addExternalCategoryMember(id, MiscUtils.getAsStringList(section.get("category")).stream().map(Key::of).toList()); AbstractItemManager.this.plugin.itemBrowserManager().addExternalCategoryMember(id, MiscUtils.getAsStringList(section.get("category")).stream().map(Key::of).toList());
} }
/*
* ========================
*
* 模型配置分界线
*
* ========================
*/
// 模型配置区域如果这里被配置了那么用户必须要配置custom-model-data或item-model // 模型配置区域如果这里被配置了那么用户必须要配置custom-model-data或item-model
Map<String, Object> modelSection = MiscUtils.castToMap(section.get("model"), true); Map<String, Object> modelSection = MiscUtils.castToMap(section.get("model"), true);
Map<String, Object> legacyModelSection = MiscUtils.castToMap(section.get("legacy-model"), true); Map<String, Object> legacyModelSection = MiscUtils.castToMap(section.get("legacy-model"), true);
@@ -480,13 +562,10 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
} }
boolean needsModelSection = isModernFormatRequired() || (needsLegacyCompatibility() && legacyModelSection == null); boolean needsModelSection = isModernFormatRequired() || (needsLegacyCompatibility() && legacyModelSection == null);
// 只对自定义物品有这个限制 // 只对自定义物品有这个限制既没有模型值也没有item-model
if (!isVanillaItem) { if (!isVanillaItem && customModelData == 0 && itemModel == null) {
// 既没有模型值也没有item-model
if (customModelData == 0 && itemModelKey == null) {
collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model_id")); collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.missing_model_id"));
} }
}
// 新版格式 // 新版格式
ItemModel modernModel = null; ItemModel modernModel = null;
@@ -532,11 +611,11 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
// 自定义物品的model处理 // 自定义物品的model处理
if (!isVanillaItem) { if (!isVanillaItem) {
// 这个item-model是否存在且是原版item-model // 这个item-model是否存在且是原版item-model
boolean isVanillaItemModel = itemModelKey != null && AbstractPackManager.PRESET_ITEMS.containsKey(itemModelKey); boolean isVanillaItemModel = itemModel != null && AbstractPackManager.PRESET_ITEMS.containsKey(itemModel);
// 使用了自定义模型值 // 使用了自定义模型值
if (customModelData != 0) { if (customModelData != 0) {
// 如果用户主动设置了item-model且为原版物品则使用item-model为基础模型否则使用其视觉材质对应的item-model // 如果用户主动设置了item-model且为原版物品则使用item-model为基础模型否则使用其视觉材质对应的item-model
Key finalBaseModel = isVanillaItemModel ? itemModelKey : clientBoundMaterial; Key finalBaseModel = isVanillaItemModel ? itemModel : clientBoundMaterial;
// 检查cmd冲突 // 检查cmd冲突
Map<Integer, Key> conflict = AbstractItemManager.this.cmdConflictChecker.computeIfAbsent(finalBaseModel, k -> new HashMap<>()); Map<Integer, Key> conflict = AbstractItemManager.this.cmdConflictChecker.computeIfAbsent(finalBaseModel, k -> new HashMap<>());
if (conflict.containsKey(customModelData)) { if (conflict.containsKey(customModelData)) {
@@ -558,20 +637,20 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
lom.addAll(legacyOverridesModels); lom.addAll(legacyOverridesModels);
} }
} else if (isVanillaItemModel) { } else if (isVanillaItemModel) {
collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModelKey.asString())); collector.addAndThrow(new LocalizedResourceConfigException("warning.config.item.item_model.conflict", itemModel.asString()));
} }
// 使用了item-model组件且不是原版物品的 // 使用了item-model组件且不是原版物品的
if (itemModelKey != null && !isVanillaItemModel) { if (itemModel != null && !isVanillaItemModel) {
if (isModernFormatRequired() && modernModel != null) { if (isModernFormatRequired() && modernModel != null) {
AbstractItemManager.this.modernItemModels1_21_4.put(itemModelKey, new ModernItemModel( AbstractItemManager.this.modernItemModels1_21_4.put(itemModel, new ModernItemModel(
modernModel, modernModel,
ResourceConfigUtils.getAsBoolean(section.getOrDefault("oversized-in-gui", true), "oversized-in-gui"), ResourceConfigUtils.getAsBoolean(section.getOrDefault("oversized-in-gui", true), "oversized-in-gui"),
ResourceConfigUtils.getAsBoolean(section.getOrDefault("hand-animation-on-swap", true), "hand-animation-on-swap") ResourceConfigUtils.getAsBoolean(section.getOrDefault("hand-animation-on-swap", true), "hand-animation-on-swap")
)); ));
} }
if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && needsLegacyCompatibility() && legacyOverridesModels != null && !legacyOverridesModels.isEmpty()) { if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && needsLegacyCompatibility() && legacyOverridesModels != null && !legacyOverridesModels.isEmpty()) {
TreeSet<LegacyOverridesModel> lom = AbstractItemManager.this.modernItemModels1_21_2.computeIfAbsent(itemModelKey, k -> new TreeSet<>()); TreeSet<LegacyOverridesModel> lom = AbstractItemManager.this.modernItemModels1_21_2.computeIfAbsent(itemModel, k -> new TreeSet<>());
lom.addAll(legacyOverridesModels); lom.addAll(legacyOverridesModels);
} }
} }
@@ -588,6 +667,8 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
// 抛出异常 // 抛出异常
collector.throwIfPresent(); collector.throwIfPresent();
}, () -> GsonHelper.get().toJson(section)));
} }
} }

View File

@@ -13,7 +13,7 @@ public class EmptyItemBehavior extends ItemBehavior {
public static class Factory implements ItemBehaviorFactory { public static class Factory implements ItemBehaviorFactory {
@Override @Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) { public ItemBehavior create(Pack pack, Path path, String node, Key id, Map<String, Object> arguments) {
return INSTANCE; return INSTANCE;
} }
} }

View File

@@ -8,5 +8,5 @@ import java.util.Map;
public interface ItemBehaviorFactory { public interface ItemBehaviorFactory {
ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments); ItemBehavior create(Pack pack, Path path, String node, Key id, Map<String, Object> arguments);
} }

View File

@@ -23,7 +23,7 @@ public class ItemBehaviors {
.register(ResourceKey.create(Registries.ITEM_BEHAVIOR_FACTORY.location(), key), factory); .register(ResourceKey.create(Registries.ITEM_BEHAVIOR_FACTORY.location(), key), factory);
} }
public static ItemBehavior fromMap(Pack pack, Path path, Key id, Map<String, Object> map) { public static ItemBehavior fromMap(Pack pack, Path path, String node, Key id, Map<String, Object> map) {
if (map == null || map.isEmpty()) return EmptyItemBehavior.INSTANCE; if (map == null || map.isEmpty()) return EmptyItemBehavior.INSTANCE;
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.behavior.missing_type"); String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.behavior.missing_type");
Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE);
@@ -31,25 +31,6 @@ public class ItemBehaviors {
if (factory == null) { if (factory == null) {
throw new LocalizedResourceConfigException("warning.config.item.behavior.invalid_type", type); throw new LocalizedResourceConfigException("warning.config.item.behavior.invalid_type", type);
} }
return factory.create(pack, path, id, map); return factory.create(pack, path, node, id, map);
}
public static List<ItemBehavior> fromList(Pack pack, Path path, Key id, List<Map<String, Object>> list) {
List<ItemBehavior> behaviors = new ArrayList<>(list.size());
for (Map<String, Object> map : list) {
behaviors.add(fromMap(pack, path, id, map));
}
return behaviors;
}
@SuppressWarnings("unchecked")
public static List<ItemBehavior> fromObj(Pack pack, Path path, Key id, Object behaviorObj) {
if (behaviorObj instanceof Map<?,?>) {
return List.of(fromMap(pack, path, id, MiscUtils.castToMap(behaviorObj, false)));
} else if (behaviorObj instanceof List<?>) {
return fromList(pack, path, id, (List<Map<String, Object>>) behaviorObj);
} else {
return List.of();
}
} }
} }

View File

@@ -135,10 +135,10 @@ public abstract class AbstractRecipeManager<T> implements RecipeManager<T> {
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (!Config.enableRecipeSystem()) return; if (!Config.enableRecipeSystem()) return;
if (AbstractRecipeManager.this.byId.containsKey(id)) { if (AbstractRecipeManager.this.byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.recipe.duplicate", path, id); throw new LocalizedResourceConfigException("warning.config.recipe.duplicate");
} }
Recipe<T> recipe = RecipeSerializers.fromMap(id, section); Recipe<T> recipe = RecipeSerializers.fromMap(id, section);
try { try {

View File

@@ -608,7 +608,6 @@ public abstract class AbstractPackManager implements PackManager {
return cachedConfigs; return cachedConfigs;
} }
// todo 本地化日志
private void loadResourceConfigs(Predicate<ConfigParser> predicate) { private void loadResourceConfigs(Predicate<ConfigParser> predicate) {
long o1 = System.nanoTime(); long o1 = System.nanoTime();
TreeMap<ConfigParser, List<CachedConfigSection>> cachedConfigs = this.updateCachedConfigFiles(); TreeMap<ConfigParser, List<CachedConfigSection>> cachedConfigs = this.updateCachedConfigFiles();
@@ -622,13 +621,12 @@ public abstract class AbstractPackManager implements PackManager {
switch (parser) { switch (parser) {
case SectionConfigParser configParser -> { case SectionConfigParser configParser -> {
for (CachedConfigSection cached : entry.getValue()) { for (CachedConfigSection cached : entry.getValue()) {
try { ResourceConfigUtils.runCatching(
configParser.parseSection(cached.pack(), cached.filePath(), cached.config()); cached.filePath(),
} catch (LocalizedException e) { cached.prefix(),
printWarningRecursively(e, cached.filePath(), cached.prefix()); () -> configParser.parseSection(cached.pack(), cached.filePath(), cached.config()),
} catch (Exception e) { () -> GsonHelper.get().toJson(cached.config())
this.plugin.logger().warn("Unexpected error loading file " + cached.filePath() + " - '" + parser.sectionId()[0] + "'. Please find the cause according to the stacktrace or seek developer help. Additional info: " + GsonHelper.get().toJson(cached.config()), e); );
}
} }
} }
case IdObjectConfigParser configParser -> { case IdObjectConfigParser configParser -> {
@@ -636,13 +634,13 @@ public abstract class AbstractPackManager implements PackManager {
for (Map.Entry<String, Object> configEntry : cached.config().entrySet()) { for (Map.Entry<String, Object> configEntry : cached.config().entrySet()) {
String key = configEntry.getKey(); String key = configEntry.getKey();
Key id = Key.withDefaultNamespace(key, cached.pack().namespace()); Key id = Key.withDefaultNamespace(key, cached.pack().namespace());
try { String node = cached.prefix() + "." + key;
configParser.parseObject(cached.pack(), cached.filePath(), id, configEntry.getValue()); ResourceConfigUtils.runCatching(
} catch (LocalizedException e) { cached.filePath(),
printWarningRecursively(e, cached.filePath(), cached.prefix() + "." + key); node,
} catch (Exception e) { () -> configParser.parseObject(cached.pack(), cached.filePath(), node, id, configEntry.getValue()),
this.plugin.logger().warn("Unexpected error loading file " + cached.filePath() + " - '" + parser.sectionId()[0] + "." + key + "'. Please find the cause according to the stacktrace or seek developer help. Additional info: " + GsonHelper.get().toJson(configEntry.getValue()), e); () -> GsonHelper.get().toJson(configEntry.getValue())
} );
} }
} }
} }
@@ -651,49 +649,37 @@ public abstract class AbstractPackManager implements PackManager {
for (Map.Entry<String, Object> configEntry : cached.config().entrySet()) { for (Map.Entry<String, Object> configEntry : cached.config().entrySet()) {
String key = configEntry.getKey(); String key = configEntry.getKey();
Key id = Key.withDefaultNamespace(key, cached.pack().namespace()); Key id = Key.withDefaultNamespace(key, cached.pack().namespace());
try { if (!(configEntry.getValue() instanceof Map<?, ?> section)) {
if (configEntry.getValue() instanceof Map<?, ?> configSection0) { TranslationManager.instance().log("warning.config.structure.not_section",
Map<String, Object> config = castToMap(configSection0, false); cached.filePath().toString(), cached.prefix() + "." + key, configEntry.getValue().getClass().getSimpleName());
continue;
}
Map<String, Object> config = castToMap(section, false);
if ((boolean) config.getOrDefault("debug", false)) { if ((boolean) config.getOrDefault("debug", false)) {
this.plugin.logger().info(GsonHelper.get().toJson(this.plugin.templateManager().applyTemplates(id, config))); this.plugin.logger().info(GsonHelper.get().toJson(this.plugin.templateManager().applyTemplates(id, config)));
} }
if ((boolean) config.getOrDefault("enable", true)) { if (!(boolean) config.getOrDefault("enable", true)) {
configParser.parseSection(cached.pack(), cached.filePath(), id, MiscUtils.castToMap(this.plugin.templateManager().applyTemplates(id, config), false)); continue;
}
} else {
TranslationManager.instance().log("warning.config.structure.not_section", cached.filePath().toString(), cached.prefix() + "." + key, configEntry.getValue().getClass().getSimpleName());
}
} catch (LocalizedException e) {
printWarningRecursively(e, cached.filePath(), cached.prefix() + "." + key);
} catch (Exception e) {
this.plugin.logger().warn("Unexpected error loading file " + cached.filePath() + " - '" + parser.sectionId()[0] + "." + key + "'. Please find the cause according to the stacktrace or seek developer help. Additional info: " + GsonHelper.get().toJson(configEntry.getValue()), e);
} }
String node = cached.prefix() + "." + key;
ResourceConfigUtils.runCatching(
cached.filePath(),
node,
() -> configParser.parseSection(cached.pack(), cached.filePath(), node, id, MiscUtils.castToMap(this.plugin.templateManager().applyTemplates(id, config), false)),
() -> GsonHelper.get().toJson(section)
);
} }
} }
} }
default -> { default -> {
} }
} }
parser.postProcess(); parser.postProcess();
long t2 = System.nanoTime(); long t2 = System.nanoTime();
this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms"); this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms");
} }
} }
private void printWarningRecursively(LocalizedException e, Path path, String prefix) {
for (Throwable t : e.getSuppressed()) {
if (t instanceof LocalizedException suppressed) {
printWarningRecursively(suppressed, path, prefix);
}
}
if (e instanceof LocalizedResourceConfigException exception) {
exception.setPath(path);
exception.setId(prefix);
}
TranslationManager.instance().log(e.node(), e.arguments());
}
private void processConfigEntry(Map.Entry<String, Object> entry, Path path, Pack pack, BiConsumer<ConfigParser, CachedConfigSection> callback) { private void processConfigEntry(Map.Entry<String, Object> entry, Path path, Pack pack, BiConsumer<ConfigParser, CachedConfigSection> callback) {
if (entry.getValue() instanceof Map<?,?> typeSections0) { if (entry.getValue() instanceof Map<?,?> typeSections0) {
String key = entry.getKey(); String key = entry.getKey();

View File

@@ -1,200 +0,0 @@
package net.momirealms.craftengine.core.pack.cache;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.momirealms.craftengine.core.util.FileUtils;
import net.momirealms.craftengine.core.util.GsonHelper;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
public class AutoId {
private final Path cachePath;
private final BiMap<String, Integer> forcedIds = HashBiMap.create(128);
private final Map<String, Integer> cachedIds = new HashMap<>();
private final BitSet occupiedIds = new BitSet();
private final Map<String, CompletableFuture<Integer>> autoIds = new HashMap<>();
private int currentAutoId;
private int minId;
private int maxId;
public AutoId(Path cachePath) {
this.cachePath = cachePath;
}
public void reset(int startIndex, int endIndex) {
this.minId = startIndex;
this.currentAutoId = startIndex;
this.maxId = endIndex;
this.occupiedIds.clear();
this.forcedIds.clear();
this.autoIds.clear();
this.cachedIds.clear();
}
public void arrangeForTheRest() {
// 然后处理自动分配的ID
for (Map.Entry<String, CompletableFuture<Integer>> entry : this.autoIds.entrySet()) {
String name = entry.getKey();
CompletableFuture<Integer> future = entry.getValue();
// 不应该触发
if (future.isDone()) {
continue;
}
// 尝试使用缓存的ID并且其有效
Integer cachedId = this.cachedIds.get(name);
if (cachedId != null && !this.occupiedIds.get(cachedId) && cachedId >= this.minId && cachedId <= this.maxId) {
this.occupiedIds.set(cachedId);
future.complete(cachedId);
continue;
}
// 寻找下一个可用的自动ID
int autoId = findNextAvailableAutoId();
if (autoId == -1) {
// 没有可用的ID
future.completeExceptionally(new AutoIdExhaustedException(name, this.minId, this.maxId));
continue;
}
// 分配找到的ID
this.occupiedIds.set(autoId);
future.complete(autoId);
this.cachedIds.put(name, autoId);
}
// 清空futureIds因为所有请求都已处理
this.autoIds.clear();
}
private int findNextAvailableAutoId() {
// 如果已经用尽
if (this.currentAutoId > this.maxId) {
return -1;
}
// 寻找下一个可用的id
this.currentAutoId = this.occupiedIds.nextClearBit(this.currentAutoId);
// 已经用完了
if (this.currentAutoId > maxId) {
return -1;
}
// 找到了
return this.currentAutoId;
}
// 强制使用某个id这时候直接标记到occupiedIds如果被占用则直接抛出异常
public CompletableFuture<Integer> forceId(final String name, int index) {
// 检查ID是否在有效范围内一般不会在这触发
if (index < this.minId || index > this.maxId) {
return CompletableFuture.failedFuture(new AutoIdOutOfRangeException(name, index, this.minId, this.maxId));
}
// 检查ID是否已被其他名称占用
String previous = this.forcedIds.inverse().get(index);
if (previous != null && !previous.equals(name)) {
return CompletableFuture.failedFuture(new AutoIdConflictException(previous, index));
}
this.forcedIds.put(name, index);
this.cachedIds.remove(name); // 如果曾经被缓存过,那么移除
return CompletableFuture.completedFuture(index);
}
// 自动分配id优先使用缓存的值
public CompletableFuture<Integer> autoId(final String name) {
CompletableFuture<Integer> future = new CompletableFuture<>();
this.autoIds.put(name, future);
return future;
}
// 大多数时候通过指令移除那些已经不再被使用的id使用完以后记得调用saveCache以保存更改
public int clearUnusedIds(Predicate<String> predicate) {
List<String> toRemove = new ArrayList<>();
for (String id : this.cachedIds.keySet()) {
if (predicate.test(id)) {
toRemove.add(id);
}
}
for (String id : toRemove) {
Integer removedId = this.cachedIds.remove(id);
if (removedId != null) {
// 只有当这个ID不是强制ID时才从occupiedIds中移除
if (!forcedIds.containsValue(removedId)) {
occupiedIds.clear(removedId);
}
}
}
return toRemove.size();
}
// 获取已分配的ID用于调试或查询
public Integer getId(String name) {
if (forcedIds.containsKey(name)) {
return forcedIds.get(name);
}
return cachedIds.get(name);
}
// 获取所有已分配的ID映射
public Map<String, Integer> getAllocatedIds() {
Map<String, Integer> result = new HashMap<>();
result.putAll(forcedIds);
result.putAll(cachedIds);
return Collections.unmodifiableMap(result);
}
// 检查某个ID是否已被占用
public boolean isIdOccupied(int id) {
return occupiedIds.get(id);
}
// 从缓存中加载文件
public void loadCache() throws IOException {
if (!Files.exists(this.cachePath)) {
return;
}
JsonElement element = GsonHelper.readJsonFile(this.cachePath);
if (element instanceof JsonObject jsonObject) {
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
if (entry.getValue() instanceof JsonPrimitive primitive) {
int id = primitive.getAsInt();
this.cachedIds.put(entry.getKey(), id);
}
}
}
}
// 保存缓存到文件
public void saveCache() throws IOException {
FileUtils.createDirectoriesSafe(this.cachePath.getParent());
GsonHelper.writeJsonFile(GsonHelper.get().toJsonTree(this.cachedIds), this.cachePath);
}
public static class AutoIdConflictException extends RuntimeException {
public AutoIdConflictException(String previousOwner, int id) {
super("ID " + id + " is already occupied by: " + previousOwner);
}
}
public static class AutoIdOutOfRangeException extends RuntimeException {
public AutoIdOutOfRangeException(String name, int id, int min, int max) {
super("ID " + id + " for '" + name + "' is out of range. Valid range: " + min + "-" + max);
}
}
public static class AutoIdExhaustedException extends RuntimeException {
public AutoIdExhaustedException(String name, int min, int max) {
super("No available auto ID for '" + name + "'. All IDs in range " + min + "-" + max + " are occupied.");
}
}
}

View File

@@ -0,0 +1,226 @@
package net.momirealms.craftengine.core.pack.cache;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.momirealms.craftengine.core.util.FileUtils;
import net.momirealms.craftengine.core.util.GsonHelper;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
public class IdAllocator {
private final Path cacheFilePath;
private final BiMap<String, Integer> forcedIdMap = HashBiMap.create(128);
private final Map<String, Integer> cachedIdMap = new HashMap<>();
private final BitSet occupiedIdSet = new BitSet();
private final Map<String, CompletableFuture<Integer>> pendingAllocations = new HashMap<>();
private int nextAutoId;
private int minId;
private int maxId;
public IdAllocator(Path cacheFilePath) {
this.cacheFilePath = cacheFilePath;
}
/**
* 重置分配器状态
* @param minId 最小ID包含
* @param maxId 最大ID包含
*/
public void reset(int minId, int maxId) {
this.minId = minId;
this.nextAutoId = minId;
this.maxId = maxId;
this.occupiedIdSet.clear();
this.forcedIdMap.clear();
this.pendingAllocations.clear();
this.cachedIdMap.clear();
}
/**
* 处理所有待分配的自动ID请求
*/
public void processPendingAllocations() {
for (Map.Entry<String, CompletableFuture<Integer>> entry : this.pendingAllocations.entrySet()) {
String name = entry.getKey();
CompletableFuture<Integer> future = entry.getValue();
if (future.isDone()) {
continue; // 不应该发生的情况
}
// 优先尝试使用缓存的ID
Integer cachedId = this.cachedIdMap.get(name);
if (isIdAvailable(cachedId)) {
allocateId(name, cachedId, future);
continue;
}
// 分配新的自动ID
int newId = findNextAvailableId();
if (newId == -1) {
future.completeExceptionally(new IdExhaustedException(name, this.minId, this.maxId));
continue;
}
allocateId(name, newId, future);
this.cachedIdMap.put(name, newId);
}
this.pendingAllocations.clear();
}
private boolean isIdAvailable(Integer id) {
return id != null && id >= this.minId && id <= this.maxId
&& !this.occupiedIdSet.get(id);
}
private void allocateId(String name, int id, CompletableFuture<Integer> future) {
this.occupiedIdSet.set(id);
future.complete(id);
}
private int findNextAvailableId() {
if (this.nextAutoId > this.maxId) {
return -1;
}
this.nextAutoId = this.occupiedIdSet.nextClearBit(this.nextAutoId);
return this.nextAutoId <= this.maxId ? this.nextAutoId : -1;
}
/**
* 强制分配指定ID无视限制
* @param name 名称
* @param id 要分配的ID
* @return 分配结果的Future
*/
public CompletableFuture<Integer> assignFixedId(String name, int id) {
// 检查ID是否被其他名称占用
String existingOwner = this.forcedIdMap.inverse().get(id);
if (existingOwner != null && !existingOwner.equals(name)) {
return CompletableFuture.failedFuture(new IdConflictException(existingOwner, id));
}
this.forcedIdMap.put(name, id);
this.cachedIdMap.remove(name); // 清除可能的缓存
this.occupiedIdSet.set(id);
return CompletableFuture.completedFuture(id);
}
/**
* 请求自动分配ID
* @param name 名称
* @return 分配结果的Future
*/
public CompletableFuture<Integer> requestAutoId(String name) {
CompletableFuture<Integer> future = new CompletableFuture<>();
this.pendingAllocations.put(name, future);
return future;
}
/**
* 清理不再使用的ID
* @param shouldRemove 判断是否应该移除的谓词
* @return 被移除的ID数量
*/
public int cleanupUnusedIds(Predicate<String> shouldRemove) {
List<String> idsToRemove = new ArrayList<>();
for (String id : this.cachedIdMap.keySet()) {
if (shouldRemove.test(id)) {
idsToRemove.add(id);
}
}
int removedCount = 0;
for (String id : idsToRemove) {
Integer removedId = this.cachedIdMap.remove(id);
if (removedId != null && !this.forcedIdMap.containsValue(removedId)) {
this.occupiedIdSet.clear(removedId);
removedCount++;
}
}
return removedCount;
}
/**
* 获取指定名称的ID
* @param name 名称
* @return ID如果不存在返回null
*/
public Integer getId(String name) {
Integer forcedId = this.forcedIdMap.get(name);
return forcedId != null ? forcedId : this.cachedIdMap.get(name);
}
/**
* 获取所有已分配的ID映射不可修改
*/
public Map<String, Integer> getAllAllocatedIds() {
Map<String, Integer> result = new HashMap<>();
result.putAll(this.forcedIdMap);
result.putAll(this.cachedIdMap);
return Collections.unmodifiableMap(result);
}
/**
* 检查ID是否已被占用
*/
public boolean isIdOccupied(int id) {
return this.occupiedIdSet.get(id);
}
/**
* 从文件加载缓存
*/
public void loadFromCache() throws IOException {
if (!Files.exists(this.cacheFilePath)) {
return;
}
JsonElement element = GsonHelper.readJsonFile(this.cacheFilePath);
if (element instanceof JsonObject jsonObject) {
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
if (entry.getValue() instanceof JsonPrimitive primitive) {
int id = primitive.getAsInt();
this.cachedIdMap.put(entry.getKey(), id);
}
}
}
}
/**
* 保存缓存到文件
*/
public void saveToCache() throws IOException {
FileUtils.createDirectoriesSafe(this.cacheFilePath.getParent());
GsonHelper.writeJsonFile(GsonHelper.get().toJsonTree(this.cachedIdMap), this.cacheFilePath);
}
// 异常类保持不变,但建议也重命名以保持一致性
public static class IdConflictException extends RuntimeException {
public IdConflictException(String previousOwner, int id) {
super("ID " + id + " is already occupied by: " + previousOwner);
}
}
public static class IdOutOfRangeException extends RuntimeException {
public IdOutOfRangeException(String name, int id, int min, int max) {
super("ID " + id + " for '" + name + "' is out of range. Valid range: " + min + "-" + max);
}
}
public static class IdExhaustedException extends RuntimeException {
public IdExhaustedException(String name, int min, int max) {
super("No available auto ID for '" + name + "'. All IDs in range " + min + "-" + max + " are occupied.");
}
}
}

View File

@@ -159,6 +159,8 @@ public class Config {
protected boolean item$update_triggers$click_in_inventory; protected boolean item$update_triggers$click_in_inventory;
protected boolean item$update_triggers$drop; protected boolean item$update_triggers$drop;
protected boolean item$update_triggers$pick_up; protected boolean item$update_triggers$pick_up;
protected int item$custom_model_data_starting_value$default;
protected Map<Key, Integer> item$custom_model_data_starting_value$overrides;
protected String equipment$sacrificed_vanilla_armor$type; protected String equipment$sacrificed_vanilla_armor$type;
protected Key equipment$sacrificed_vanilla_armor$asset_id; protected Key equipment$sacrificed_vanilla_armor$asset_id;
@@ -239,6 +241,7 @@ public class Config {
.addIgnoredRoute(PluginProperties.getValue("config"), "resource-pack.delivery.hosting", '.') .addIgnoredRoute(PluginProperties.getValue("config"), "resource-pack.delivery.hosting", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-blocks.convert", '.') .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-blocks.convert", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-furniture.convert", '.') .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-furniture.convert", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "item.custom-model-data-starting-value.overrides", '.')
.build()); .build());
} }
try { try {
@@ -398,6 +401,22 @@ public class Config {
item$update_triggers$click_in_inventory = config.getBoolean("item.update-triggers.click-in-inventory", false); item$update_triggers$click_in_inventory = config.getBoolean("item.update-triggers.click-in-inventory", false);
item$update_triggers$drop = config.getBoolean("item.update-triggers.drop", false); item$update_triggers$drop = config.getBoolean("item.update-triggers.drop", false);
item$update_triggers$pick_up = config.getBoolean("item.update-triggers.pick-up", false); item$update_triggers$pick_up = config.getBoolean("item.update-triggers.pick-up", false);
item$custom_model_data_starting_value$default = config.getInt("item.custom-model-data-starting-value.default", 10000);
Section customModelDataOverridesSection = config.getSection("item.custom-model-data-starting-value.overrides");
if (customModelDataOverridesSection != null) {
Map<Key, Integer> customModelDataOverrides = new HashMap<>();
for (Map.Entry<String, Object> entry : customModelDataOverridesSection.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof String s) {
customModelDataOverrides.put(Key.of(entry.getKey()), Integer.parseInt(s));
} else if (entry.getValue() instanceof Integer i) {
customModelDataOverrides.put(Key.of(entry.getKey()), i);
}
}
item$custom_model_data_starting_value$overrides = customModelDataOverrides;
} else {
item$custom_model_data_starting_value$overrides = Map.of();
}
// block // block
block$sound_system$enable = config.getBoolean("block.sound-system.enable", true); block$sound_system$enable = config.getBoolean("block.sound-system.enable", true);
@@ -752,6 +771,13 @@ public class Config {
return instance.furniture$hide_base_entity; return instance.furniture$hide_base_entity;
} }
public static int customModelDataStartingValue(Key material) {
if (instance.item$custom_model_data_starting_value$overrides.containsKey(material)) {
return instance.item$custom_model_data_starting_value$overrides.get(material);
}
return instance.item$custom_model_data_starting_value$default;
}
public static int compressionMethod() { public static int compressionMethod() {
int id = instance.chunk_system$compression_method; int id = instance.chunk_system$compression_method;
if (id <= 0 || id > CompressionMethod.METHOD_COUNT) { if (id <= 0 || id > CompressionMethod.METHOD_COUNT) {

View File

@@ -8,6 +8,6 @@ import java.nio.file.Path;
public interface IdObjectConfigParser extends ConfigParser { public interface IdObjectConfigParser extends ConfigParser {
default void parseObject(Pack pack, Path path, Key id, Object object) throws LocalizedException { default void parseObject(Pack pack, Path path, String node, Key id, Object object) throws LocalizedException {
} }
} }

View File

@@ -9,6 +9,6 @@ import java.util.Map;
public interface IdSectionConfigParser extends ConfigParser { public interface IdSectionConfigParser extends ConfigParser {
default void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) throws LocalizedException { default void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) throws LocalizedException {
} }
} }

View File

@@ -51,9 +51,9 @@ public class TemplateManagerImpl implements TemplateManager {
} }
@Override @Override
public void parseObject(Pack pack, Path path, Key id, Object obj) { public void parseObject(Pack pack, Path path, String node, Key id, Object obj) {
if (templates.containsKey(id)) { if (templates.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.template.duplicate", path.toString(), id.toString()); throw new LocalizedResourceConfigException("warning.config.template.duplicate");
} }
// 预处理会将 string类型的键或值解析为ArgumentString以加速模板应用。所以处理后不可能存在String类型。 // 预处理会将 string类型的键或值解析为ArgumentString以加速模板应用。所以处理后不可能存在String类型。
templates.put(id, preprocessUnknownValue(obj)); templates.put(id, preprocessUnknownValue(obj));

View File

@@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.plugin.Manageable;
import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.config.IdObjectConfigParser; import net.momirealms.craftengine.core.plugin.config.IdObjectConfigParser;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.nio.file.Path; import java.nio.file.Path;
@@ -49,7 +50,7 @@ public class GlobalVariableManager implements Manageable {
} }
@Override @Override
public void parseObject(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Object object) throws LocalizedException { public void parseObject(Pack pack, Path path, String node, Key id, Object object) throws LocalizedException {
if (object != null) { if (object != null) {
GlobalVariableManager.this.globalVariables.put(id.value(), object.toString()); GlobalVariableManager.this.globalVariables.put(id.value(), object.toString());
} }

View File

@@ -110,7 +110,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
String name = section.getOrDefault("name", id).toString(); String name = section.getOrDefault("name", id).toString();
List<String> members = MiscUtils.getAsStringList(section.getOrDefault("list", List.of())); List<String> members = MiscUtils.getAsStringList(section.getOrDefault("list", List.of()));
Key icon = Key.of(section.getOrDefault("icon", ItemKeys.STONE).toString()); Key icon = Key.of(section.getOrDefault("icon", ItemKeys.STONE).toString());

View File

@@ -37,7 +37,7 @@ public class LocalizedResourceConfigException extends LocalizedException {
super.setArgument(0, path.toString()); super.setArgument(0, path.toString());
} }
public void setId(String id) { public void setNode(String id) {
super.setArgument(1, id); super.setArgument(1, id);
} }
} }

View File

@@ -257,10 +257,10 @@ public class TranslationManagerImpl implements TranslationManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map<String, Object> section) {
Locale locale = TranslationManager.parseLocale(id.value()); Locale locale = TranslationManager.parseLocale(id.value());
if (locale == null) { if (locale == null) {
throw new LocalizedResourceConfigException("warning.config.i18n.unknown_locale", path, id); throw new LocalizedResourceConfigException("warning.config.i18n.unknown_locale");
} }
Map<String, String> bundle = new HashMap<>(); Map<String, String> bundle = new HashMap<>();
@@ -288,7 +288,7 @@ public class TranslationManagerImpl implements TranslationManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, net.momirealms.craftengine.core.util.Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map<String, Object> section) {
String langId = id.value().toLowerCase(Locale.ENGLISH); String langId = id.value().toLowerCase(Locale.ENGLISH);
Map<String, String> sectionData = section.entrySet().stream() Map<String, String> sectionData = section.entrySet().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(

View File

@@ -80,7 +80,7 @@ public abstract class AbstractSoundManager implements SoundManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (AbstractSoundManager.this.songs.containsKey(id)) { if (AbstractSoundManager.this.songs.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.jukebox_song.duplicate"); throw new LocalizedResourceConfigException("warning.config.jukebox_song.duplicate");
} }
@@ -107,7 +107,7 @@ public abstract class AbstractSoundManager implements SoundManager {
} }
@Override @Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) { public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (AbstractSoundManager.this.byId.containsKey(id)) { if (AbstractSoundManager.this.byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.sound.duplicate"); throw new LocalizedResourceConfigException("warning.config.sound.duplicate");
} }

View File

@@ -1,13 +1,16 @@
package net.momirealms.craftengine.core.util; package net.momirealms.craftengine.core.util;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.Vec3d;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Vector3f; import org.joml.Vector3f;
import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -280,4 +283,31 @@ public final class ResourceConfigUtils {
} }
} }
} }
public static void runCatching(Path configPath, String node, Runnable runnable, Supplier<String> config) {
try {
runnable.run();
} catch (LocalizedException e) {
printWarningRecursively(e, configPath, node);
} catch (Exception e) {
String message = "Unexpected error loading file " + configPath + " - '" + node + "'.";
if (config != null) {
message += " Configuration details: " + config.get();
}
CraftEngine.instance().logger().warn(message, e);
}
}
private static void printWarningRecursively(LocalizedException e, Path path, String node) {
for (Throwable t : e.getSuppressed()) {
if (t instanceof LocalizedException suppressed) {
printWarningRecursively(suppressed, path, node);
}
}
if (e instanceof LocalizedResourceConfigException exception) {
exception.setPath(path);
exception.setNode(node);
}
TranslationManager.instance().log(e.node(), e.arguments());
}
} }