9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 01:49:30 +00:00

盔甲重构part1

This commit is contained in:
XiaoMoMi
2025-07-03 05:55:57 +08:00
parent a27f1678fd
commit d9bf33ce11
30 changed files with 931 additions and 362 deletions

View File

@@ -15,10 +15,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.ThrowableProjectile;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
@@ -32,6 +29,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
public class BukkitProjectileManager implements Listener, ProjectileManager {
private static BukkitProjectileManager instance;
@@ -47,11 +45,22 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this, this.plugin.javaPlugin());
for (World world : Bukkit.getWorlds()) {
List<Entity> entities = world.getEntities();
for (Entity entity : entities) {
if (entity instanceof Projectile projectile) {
handleProjectileLoad(projectile);
if (VersionHelper.isFolia()) {
for (World world : Bukkit.getWorlds()) {
List<Entity> entities = world.getEntities();
for (Entity entity : entities) {
if (entity instanceof Projectile projectile) {
projectile.getScheduler().run(this.plugin.javaPlugin(), (t) -> handleProjectileLoad(projectile), () -> {});
}
}
}
} else {
for (World world : Bukkit.getWorlds()) {
List<Entity> entities = world.getEntities();
for (Entity entity : entities) {
if (entity instanceof Projectile projectile) {
handleProjectileLoad(projectile);
}
}
}
}

View File

@@ -149,6 +149,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
@Override
public CustomItem<ItemStack> build() {
this.modifiers.addAll(this.settings.modifiers());
this.clientBoundModifiers.addAll(this.settings.clientBoundModifiers());
return new BukkitCustomItem(this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors),
List.copyOf(this.modifiers), List.copyOf(this.clientBoundModifiers), this.settings, this.events);
}

View File

@@ -11,11 +11,14 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.equipment.TrimBasedEquipment;
import net.momirealms.craftengine.core.item.modifier.IdModifier;
import net.momirealms.craftengine.core.pack.AbstractPackManager;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
@@ -33,9 +36,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Set;
import java.util.*;
public class BukkitItemManager extends AbstractItemManager<ItemStack> {
static {
@@ -50,6 +51,8 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
private final DebugStickListener debugStickListener;
private final ArmorEventListener armorEventListener;
private final NetworkItemHandler<ItemStack> networkItemHandler;
private final Object bedrockItemHolder;
private boolean registeredTrimMaterial;
public BukkitItemManager(BukkitCraftEngine plugin) {
super(plugin);
@@ -61,6 +64,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
this.armorEventListener = new ArmorEventListener();
this.networkItemHandler = VersionHelper.isOrAbove1_20_5() ? new ModernNetworkItemHandler() : new LegacyNetworkItemHandler();
this.registerAllVanillaItems();
this.bedrockItemHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, KeyUtils.toResourceLocation(Key.of("minecraft:bedrock")))).get();;
}
@Override
@@ -134,6 +138,78 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
HandlerList.unregisterAll(this.armorEventListener);
}
@Override
protected void registerArmorTrimPattern(Collection<TrimBasedEquipment> equipments) {
if (equipments.isEmpty()) return;
this.registerCustomTrimMaterial();
Object registry = FastNMS.INSTANCE.method$RegistryAccess$lookupOrThrow(FastNMS.INSTANCE.registryAccess(), MRegistries.TRIM_PATTERN);
try {
CoreReflections.field$MappedRegistry$frozen.set(registry, false);
for (TrimBasedEquipment equipment : equipments) {
Object resourceLocation = KeyUtils.toResourceLocation(equipment.assetId());
Object previous = FastNMS.INSTANCE.method$Registry$getValue(registry, resourceLocation);
if (previous == null) {
Object trimPattern = createTrimPattern(equipment.assetId());
Object holder = CoreReflections.method$Registry$registerForHolder.invoke(null, registry, resourceLocation, trimPattern);
CoreReflections.method$Holder$Reference$bindValue.invoke(holder, trimPattern);
CoreReflections.field$Holder$Reference$tags.set(holder, Set.of());
}
}
} catch (Exception e) {
this.plugin.logger().warn("Failed to register armor trim pattern.", e);
} finally {
try {
CoreReflections.field$MappedRegistry$frozen.set(registry, true);
} catch (ReflectiveOperationException ignored) {
}
}
}
private void registerCustomTrimMaterial() {
if (this.registeredTrimMaterial) return;
Object registry = FastNMS.INSTANCE.method$RegistryAccess$lookupOrThrow(FastNMS.INSTANCE.registryAccess(), MRegistries.TRIM_MATERIAL);
Object resourceLocation = KeyUtils.toResourceLocation(Key.of("minecraft", AbstractPackManager.TRIM_MATERIAL));
Object previous = FastNMS.INSTANCE.method$Registry$getValue(registry, resourceLocation);
if (previous == null) {
try {
CoreReflections.field$MappedRegistry$frozen.set(registry, false);
Object trimMaterial = createTrimMaterial();
Object holder = CoreReflections.method$Registry$registerForHolder.invoke(null, registry, resourceLocation, trimMaterial);
CoreReflections.method$Holder$Reference$bindValue.invoke(holder, trimMaterial);
CoreReflections.field$Holder$Reference$tags.set(holder, Set.of());
} catch (Exception e) {
this.plugin.logger().warn("Failed to register trim material.", e);
} finally {
try {
CoreReflections.field$MappedRegistry$frozen.set(registry, true);
} catch (ReflectiveOperationException ignored) {
}
}
}
this.registeredTrimMaterial = true;
}
private Object createTrimPattern(Key key) throws ReflectiveOperationException {
if (VersionHelper.isOrAbove1_21_5()) {
return CoreReflections.constructor$TrimPattern.newInstance(KeyUtils.toResourceLocation(key), CoreReflections.instance$Component$empty, false);
} else if (VersionHelper.isOrAbove1_20_2()) {
return CoreReflections.constructor$TrimPattern.newInstance(KeyUtils.toResourceLocation(key), this.bedrockItemHolder, CoreReflections.instance$Component$empty, false);
} else {
return CoreReflections.constructor$TrimPattern.newInstance(KeyUtils.toResourceLocation(key), this.bedrockItemHolder, CoreReflections.instance$Component$empty);
}
}
private Object createTrimMaterial() throws ReflectiveOperationException {
if (VersionHelper.isOrAbove1_21_5()) {
Object assetGroup = CoreReflections.method$MaterialAssetGroup$create.invoke(null, "custom");
return CoreReflections.constructor$TrimMaterial.newInstance(assetGroup, CoreReflections.instance$Component$empty);
} else if (VersionHelper.isOrAbove1_21_4()) {
return CoreReflections.constructor$TrimMaterial.newInstance("custom", this.bedrockItemHolder, Map.of(), CoreReflections.instance$Component$empty);
} else {
return CoreReflections.constructor$TrimMaterial.newInstance("custom", this.bedrockItemHolder, 1_000_000.0f, Map.of(), CoreReflections.instance$Component$empty);
}
}
@SuppressWarnings("deprecation")
@Override
public Item<ItemStack> fromByteArray(byte[] bytes) {

View File

@@ -2,9 +2,17 @@ package net.momirealms.craftengine.bukkit.item.factory;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.core.entity.EquipmentSlot;
import net.momirealms.craftengine.core.item.equipment.Equipments;
import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public class ComponentItemFactory1_21_2 extends ComponentItemFactory1_21 {
@@ -52,6 +60,18 @@ public class ComponentItemFactory1_21_2 extends ComponentItemFactory1_21 {
@Override
protected Optional<EquipmentData> equippable(ComponentItemWrapper item) {
throw new UnsupportedOperationException("Not implemented yet.");
Optional<Object> optionalData = item.getJavaComponent(ComponentTypes.EQUIPPABLE);
if (optionalData.isEmpty()) return Optional.empty();
Map<String, Object> data = MiscUtils.castToMap(optionalData.get(), false);
String slot = data.get("slot").toString();
return Optional.of(new EquipmentData(
EquipmentSlot.valueOf(slot.toUpperCase(Locale.ENGLISH)),
data.containsKey("asset_id") ? Key.of((String) data.get("asset_id")) : null,
(boolean) data.getOrDefault("dispensable", true),
(boolean) data.getOrDefault("swappable", true),
(boolean) data.getOrDefault("damage_on_hurt", true),
(boolean) data.getOrDefault("equip_on_interact", false),
data.containsKey("camera_overlay") ? Key.of((String) data.get("camera_overlay")) : null
));
}
}

View File

@@ -3588,12 +3588,37 @@ public final class CoreReflections {
);
public static final Class<?> clazz$TrimPattern = requireNonNull(
VersionHelper.isOrAbove1_21_2() ?
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.equipment.trim.TrimPattern",
"world.item.equipment.trim.TrimPattern"
) :
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.armortrim.TrimPattern",
"world.item.armortrim.TrimPattern"
)
);
public static final Class<?> clazz$TrimMaterial = requireNonNull(
VersionHelper.isOrAbove1_21_2() ?
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.equipment.trim.TrimMaterial",
"world.item.equipment.trim.TrimMaterial"
) :
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.armortrim.TrimMaterial",
"world.item.armortrim.TrimMaterial"
)
);
public static final Class<?> clazz$MaterialAssetGroup = BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.equipment.trim.MaterialAssetGroup",
"world.item.equipment.trim.MaterialAssetGroup"
);
public static final Method method$MaterialAssetGroup$create = Optional.ofNullable(clazz$MaterialAssetGroup)
.map(it -> ReflectionUtils.getStaticMethod(it, it, String.class)).orElse(null);
public static final Constructor<?> constructor$TrimPattern = requireNonNull(
VersionHelper.isOrAbove1_21_5() ?
ReflectionUtils.getConstructor(clazz$TrimPattern, clazz$ResourceLocation, clazz$Component, boolean.class) :
@@ -3601,4 +3626,12 @@ public final class CoreReflections {
ReflectionUtils.getConstructor(clazz$TrimPattern, clazz$ResourceLocation, clazz$Holder, clazz$Component, boolean.class) :
ReflectionUtils.getConstructor(clazz$TrimPattern, clazz$ResourceLocation, clazz$Holder, clazz$Component)
);
public static final Constructor<?> constructor$TrimMaterial = requireNonNull(
VersionHelper.isOrAbove1_21_5() ?
ReflectionUtils.getConstructor(clazz$TrimMaterial, clazz$MaterialAssetGroup, clazz$Component) :
VersionHelper.isOrAbove1_21_4() ?
ReflectionUtils.getConstructor(clazz$TrimMaterial, String.class, clazz$Holder, Map.class, clazz$Component) :
ReflectionUtils.getConstructor(clazz$TrimMaterial, String.class, clazz$Holder, float.class, Map.class, clazz$Component)
);
}

View File

@@ -24,6 +24,7 @@ public final class MRegistries {
public static final Object CONFIGURED_FEATURE;
public static final Object PLACED_FEATURE;
public static final Object TRIM_PATTERN;
public static final Object TRIM_MATERIAL;
@Nullable // 1.21+
public static final Object JUKEBOX_SONG;
@Nullable // 1.21+
@@ -48,6 +49,7 @@ public final class MRegistries {
Object registries$JukeboxSong = null;
Object registries$Recipe = null;
Object registries$TrimPattern = null;
Object registries$TrimMaterial = null;
for (Field field : fields) {
Type fieldType = field.getGenericType();
if (fieldType instanceof ParameterizedType paramType) {
@@ -91,6 +93,8 @@ public final class MRegistries {
registries$PlacedFeature = field.get(null);
} else if (type == CoreReflections.clazz$TrimPattern) {
registries$TrimPattern = field.get(null);
} else if (type == CoreReflections.clazz$TrimMaterial) {
registries$TrimMaterial = field.get(null);
}
}
}
@@ -111,6 +115,7 @@ public final class MRegistries {
CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature);
PLACED_FEATURE = requireNonNull(registries$PlacedFeature);
TRIM_PATTERN = requireNonNull(registries$TrimPattern);
TRIM_MATERIAL = requireNonNull(registries$TrimMaterial);
JUKEBOX_SONG = registries$JukeboxSong;
RECIPE = registries$Recipe;
} catch (ReflectiveOperationException e) {

View File

@@ -30,10 +30,10 @@ public class BukkitSoundManager extends AbstractSoundManager {
@Override
protected void registerSongs(Map<Key, JukeboxSong> songs) {
if (songs.isEmpty()) return;
Object registry = FastNMS.INSTANCE.method$RegistryAccess$lookupOrThrow(FastNMS.INSTANCE.registryAccess(), MRegistries.JUKEBOX_SONG);
try {
// 获取 JUKEBOX_SONG 注册表
Object registry = FastNMS.INSTANCE.method$RegistryAccess$lookupOrThrow(FastNMS.INSTANCE.registryAccess(), MRegistries.JUKEBOX_SONG);
unfreezeRegistry(registry);
CoreReflections.field$MappedRegistry$frozen.set(registry, false);
for (Map.Entry<Key, JukeboxSong> entry : songs.entrySet()) {
Key id = entry.getKey();
JukeboxSong jukeboxSong = entry.getValue();
@@ -54,17 +54,12 @@ public class BukkitSoundManager extends AbstractSoundManager {
CoreReflections.field$Holder$Reference$tags.set(holder, Set.of());
}
}
freezeRegistry(registry);
} catch (Exception e) {
plugin.logger().warn("Failed to register jukebox songs.", e);
this.plugin.logger().warn("Failed to register jukebox songs.", e);
} finally {
try {
CoreReflections.field$MappedRegistry$frozen.set(registry, true);
} catch (ReflectiveOperationException ignored) {}
}
}
private void unfreezeRegistry(Object registry) throws IllegalAccessException {
CoreReflections.field$MappedRegistry$frozen.set(registry, false);
}
private void freezeRegistry(Object registry) throws IllegalAccessException {
CoreReflections.field$MappedRegistry$frozen.set(registry, true);
}
}

View File

@@ -138,6 +138,10 @@ resource-pack:
resolution:
type: merge_atlas
item:
# Make custom-model-data and item-model clientside by default
client-bound-model: false
equipment:
# The sacrificed-vanilla-armor argument determines which vanilla armor gets completely removed (loses all its trims)
# when you create new armor using trim types, freeing up a slot for your custom armor.

View File

@@ -153,27 +153,6 @@ items#topaz_gears:
template: default:model/simplified_handheld
arguments:
path: minecraft:item/custom/topaz_sword
$$>=1.21.2#armor:
default:topaz_helmet:
template: default:armor/topaz
arguments:
part: helmet
slot: head
default:topaz_chestplate:
template: default:armor/topaz
arguments:
part: chestplate
slot: chest
default:topaz_leggings:
template: default:armor/topaz
arguments:
part: leggings
slot: legs
default:topaz_boots:
template: default:armor/topaz
arguments:
part: boots
slot: feet
$$>=1.21.4#topaz_trident:
default:topaz_trident:
material: trident
@@ -288,6 +267,38 @@ items#topaz_gears:
tints:
- type: minecraft:dye
default: -6265536
default:topaz_helmet:
template:
- default:armor/topaz
- default:model/armor_trim
arguments:
part: helmet
slot: head
material: topaz
default:topaz_chestplate:
template:
- default:armor/topaz
- default:model/armor_trim
arguments:
part: chestplate
slot: chest
material: topaz
default:topaz_leggings:
template:
- default:armor/topaz
- default:model/armor_trim
arguments:
part: leggings
slot: legs
material: topaz
default:topaz_boots:
template:
- default:armor/topaz
- default:model/armor_trim
arguments:
part: boots
slot: feet
material: topaz
templates:
default:armor/topaz:
material: chainmail_${part}
@@ -298,122 +309,18 @@ templates:
settings:
tags:
- default:topaz_tools
equippable:
slot: ${slot}
asset-id: topaz
humanoid: minecraft:topaz
humanoid-leggings: minecraft:topaz
model:
type: minecraft:select
property: minecraft:trim_material
fallback:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
cases:
- when: minecraft:quartz
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_quartz_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_quartz
- when: minecraft:iron
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_iron_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_iron
- when: minecraft:netherite
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_netherite_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_netherite
- when: minecraft:redstone
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_redstone_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_redstone
- when: minecraft:copper
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_copper_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_copper
- when: minecraft:gold
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_gold_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_gold
- when: minecraft:emerald
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_emerald_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_emerald
- when: minecraft:diamond
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_diamond_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_diamond
- when: minecraft:lapis
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_lapis_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_lapis
- when: minecraft:amethyst
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_amethyst_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_amethyst
- when: minecraft:resin
model:
type: minecraft:model
path: minecraft:item/custom/topaz_${part}_resin_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/topaz_${part}
layer1: minecraft:trims/items/${part}_trim_resin
recipes#topaz_gears:
equipment:
asset-id: default:topaz
properties:
slot: ${slot}
equipments#topaz:
default:topaz:
type:
$$>=1.21.2: component
$$<1.21.2: trim
humanoid: minecraft:entity/equipment/humanoid/topaz
humanoid-leggings: minecraft:entity/equipment/humanoid_leggings/topaz
recipes#topaz:
default:topaz_shovel:
type: shaped
pattern:

View File

@@ -1,22 +1,5 @@
# This file contains some useful template data. If you have good ideas, you are welcome to contribute your template!
# These templates let you ditch the real custom_model_data on the server side.
# Instead, we use client side components sent via packets to control how items look.
default:item/client_bound_custom_model_data:
custom-model-data: ${custom_model_data}
data:
remove-components:
- minecraft:custom_model_data
client-bound-data:
custom-model-data: ${custom_model_data}
default:item/client_bound_item_model:
item-model: ${item_model}
data:
remove-components:
- minecraft:item_model
client-bound-data:
item-model: ${item_model}
# blocks
templates#models#block:
# template: default:model/cube_all
@@ -162,6 +145,120 @@ templates#models#2d:
texture: ${path}
broken_model: ${broken_path}
broken_texture: ${broken_path}
# template: default:model/armor_trim
# arguments:
# material: armor material type
# part: slot type
default:model/armor_trim:
type: minecraft:select
property: minecraft:trim_material
fallback:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
cases:
- when: minecraft:quartz
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_quartz_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_quartz
- when: minecraft:iron
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_iron_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_iron
- when: minecraft:netherite
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_netherite_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_netherite
- when: minecraft:redstone
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_redstone_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_redstone
- when: minecraft:copper
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_copper_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_copper
- when: minecraft:gold
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_gold_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_gold
- when: minecraft:emerald
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_emerald_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_emerald
- when: minecraft:diamond
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_diamond_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_diamond
- when: minecraft:lapis
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_lapis_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_lapis
- when: minecraft:amethyst
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_amethyst_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_amethyst
- when: minecraft:resin
model:
type: minecraft:model
path: minecraft:item/custom/${material}_${part}_resin_trim
generation:
parent: minecraft:item/generated
textures:
layer0: minecraft:item/custom/${material}_${part}
layer1: minecraft:trims/items/${part}_trim_resin
# shield
templates#models#shield:

View File

@@ -155,7 +155,9 @@ warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>Issue found in f
warning.config.item.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated item '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.item.settings.unknown: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an unknown setting type '<arg:2>'.</yellow>"
warning.config.item.settings.invulnerable.invalid_damage_source: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an unknown damage source '<arg:2>'. Allowed sources: [<arg:3>].</yellow>"
warning.config.item.settings.equippable.missing_slot: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'slot' argument for 'equippable' settings.</yellow>"
warning.config.item.settings.equipment.missing_asset_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'asset-id' argument for 'equipment' settings.</yellow>"
warning.config.item.settings.equipment.invalid_asset_id: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid 'asset-id' argument for 'equipment' settings. This might be because you haven't created this equipment configuration or misspelled the asset-id.</yellow>"
warning.config.item.settings.projectile.missing_item: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'item' argument for 'projectile' settings.</yellow>"
warning.config.item.missing_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'material' argument.</yellow>"
warning.config.item.invalid_material: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using an invalid material type '<arg:2>'.</yellow>"
warning.config.item.invalid_custom_model_data: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is using a negative custom model data '<arg:2>' which is invalid.</yellow>"
@@ -387,4 +389,7 @@ warning.config.resource_pack.generation.missing_item_model: "<yellow>Item '<arg:
warning.config.resource_pack.generation.missing_block_model: "<yellow>Block state '<arg:0>' is missing model file: '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_parent_model: "<yellow>Model '<arg:0>' cannot find parent model: '<arg:1>'</yellow>"
warning.config.resource_pack.generation.malformatted_json: "<yellow>Json file '<arg:0>' is malformatted.</yellow>"
warning.config.resource_pack.invalid_overlay_format: "<yellow>Issue found in config.yml at 'resource-pack.overlay-format' - Invalid overlay format '<arg:0>'. Overlay format must contain the placeholder '{version}'.</yellow>"
warning.config.resource_pack.invalid_overlay_format: "<yellow>Issue found in config.yml at 'resource-pack.overlay-format' - Invalid overlay format '<arg:0>'. Overlay format must contain the placeholder '{version}'.</yellow>"
warning.config.equipment.duplicate: ""
warning.config.equipment.missing_type: ""
warning.config.equipment.invalid_type: ""

View File

@@ -156,7 +156,9 @@ warning.config.furniture.hitbox.custom.invalid_entity: "<yellow>在文件 <arg:0
warning.config.item.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的物品 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.item.settings.unknown: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了未知的设置类型 '<arg:2>'</yellow>"
warning.config.item.settings.invulnerable.invalid_damage_source: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 物品使用了未知的伤害来源类型 '<arg:2>' 允许的来源: [<arg:3>]</yellow>"
warning.config.item.settings.equippable.missing_slot: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'equippable' 设置所需的 'slot' 参数.</yellow>"
warning.config.item.settings.equipment.missing_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'equipment' 设置所需的 'asset-id' 参数.</yellow>"
warning.config.item.settings.equipment.invalid_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 为 'equipment' 设置配置了无效的 'asset-id'. 这可能是因为你没有创建装备配置或是错误地拼写了 asset-id.</yellow>"
warning.config.item.settings.projectile.missing_item: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'projectile' 设置所需的 'item' 参数.</yellow>"
warning.config.item.missing_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少必需的 'material' 参数</yellow>"
warning.config.item.invalid_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的材料类型 '<arg:2>'</yellow>"
warning.config.item.invalid_custom_model_data: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的负数模型值 '<arg:2>'.</yellow>"

View File

@@ -4,13 +4,14 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviors;
import net.momirealms.craftengine.core.item.data.Enchantment;
import net.momirealms.craftengine.core.item.data.JukeboxPlayable;
import net.momirealms.craftengine.core.item.equipment.Equipment;
import net.momirealms.craftengine.core.item.equipment.Equipments;
import net.momirealms.craftengine.core.item.equipment.TrimBasedEquipment;
import net.momirealms.craftengine.core.item.modifier.*;
import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.item.setting.ItemEquipment;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.pack.ResourceLocation;
import net.momirealms.craftengine.core.pack.misc.Equipment;
import net.momirealms.craftengine.core.pack.model.*;
import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
@@ -22,7 +23,6 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.context.text.TextProvider;
import net.momirealms.craftengine.core.plugin.context.text.TextProviders;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
@@ -49,13 +49,13 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
protected final Map<String, ExternalItemProvider<I>> externalItemProviders = new HashMap<>();
protected final Map<String, Function<Object, ItemDataModifier<I>>> dataFunctions = new HashMap<>();
protected final Map<Key, CustomItem<I>> customItems = new HashMap<>();
protected final Map<Key, List<Holder<Key>>> customItemTags;
protected final Map<Key, Map<Integer, Key>> cmdConflictChecker;
protected final Map<Key, ModernItemModel> modernItemModels1_21_4;
protected final Map<Key, TreeSet<LegacyOverridesModel>> modernItemModels1_21_2;
protected final Map<Key, TreeSet<LegacyOverridesModel>> legacyOverrides;
protected final Map<Key, TreeMap<Integer, ModernItemModel>> modernOverrides;
protected final Map<Key, Equipment> equipmentsToGenerate;
protected final Map<Key, List<Holder<Key>>> customItemTags = new HashMap<>();
protected final Map<Key, Map<Integer, Key>> cmdConflictChecker = new HashMap<>();
protected final Map<Key, ModernItemModel> modernItemModels1_21_4 = new HashMap<>();
protected final Map<Key, TreeSet<LegacyOverridesModel>> modernItemModels1_21_2 = new HashMap<>();
protected final Map<Key, TreeSet<LegacyOverridesModel>> legacyOverrides = new HashMap<>();
protected final Map<Key, TreeMap<Integer, ModernItemModel>> modernOverrides = new HashMap<>();
protected final Map<Key, Equipment> equipments = new HashMap<>();
// Cached command suggestions
protected final List<Suggestion> cachedSuggestions = new ArrayList<>();
protected final List<Suggestion> cachedTotemSuggestions = new ArrayList<>();
@@ -65,13 +65,6 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
this.itemParser = new ItemParser();
this.equipmentParser = new EquipmentParser();
this.registerFunctions();
this.legacyOverrides = new HashMap<>();
this.modernOverrides = new HashMap<>();
this.customItemTags = new HashMap<>();
this.cmdConflictChecker = new HashMap<>();
this.modernItemModels1_21_4 = new HashMap<>();
this.modernItemModels1_21_2 = new HashMap<>();
this.equipmentsToGenerate = new HashMap<>();
}
@Override
@@ -127,12 +120,22 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
this.legacyOverrides.clear();
this.modernOverrides.clear();
this.customItemTags.clear();
this.equipmentsToGenerate.clear();
this.equipments.clear();
this.cmdConflictChecker.clear();
this.modernItemModels1_21_4.clear();
this.modernItemModels1_21_2.clear();
}
@Override
public Map<Key, Equipment> equipments() {
return Collections.unmodifiableMap(this.equipments);
}
@Override
public Optional<Equipment> getEquipment(Key key) {
return Optional.ofNullable(this.equipments.get(key));
}
@Override
public Optional<CustomItem<I>> getCustomItem(Key key) {
return Optional.ofNullable(this.customItems.get(key));
@@ -156,13 +159,6 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
for (Key tag : tags) {
this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.idHolder());
}
// equipment generation
ItemEquipment equipment = customItem.settings().equipment();
if (equipment != null) {
EquipmentData data = equipment.data();
Equipment equipmentJson = this.equipmentsToGenerate.computeIfAbsent(data.assetId(), k -> new Equipment());
equipmentJson.addAll(equipment);
}
return true;
}
@@ -252,11 +248,6 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
return Collections.unmodifiableMap(this.modernOverrides);
}
@Override
public Map<Key, Equipment> equipmentsToGenerate() {
return Collections.unmodifiableMap(this.equipmentsToGenerate);
}
@Override
public boolean isVanillaItem(Key item) {
return VANILLA_ITEMS.contains(item);
@@ -264,6 +255,8 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
protected abstract CustomItem.Builder<I> createPlatformItemBuilder(Holder<Key> id, Key material, Key clientBoundMaterial);
protected abstract void registerArmorTrimPattern(Collection<TrimBasedEquipment> equipments);
public class EquipmentParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"equipments", "equipment"};
@@ -279,8 +272,21 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (AbstractItemManager.this.equipments.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.equipment.duplicate");
}
Equipment equipment = Equipments.fromMap(id, section);
AbstractItemManager.this.equipments.put(id, equipment);
}
@Override
public void postProcess() {
registerArmorTrimPattern(
AbstractItemManager.this.equipments.values().stream()
.filter(TrimBasedEquipment.class::isInstance)
.map(TrimBasedEquipment.class::cast)
.toList()
);
}
}
@@ -320,6 +326,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
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;
int customModelData = ResourceConfigUtils.getAsInt(section.getOrDefault("custom-model-data", 0), "custom-model-data");
boolean clientBoundModel = section.containsKey("client-bound-model") ? ResourceConfigUtils.getAsBoolean(section.get("client-bound-data"), "client-bound-data") : Config.globalClientboundModel();
if (customModelData < 0) {
throw new LocalizedResourceConfigException("warning.config.item.invalid_custom_model_data", String.valueOf(customModelData));
}
@@ -335,7 +342,11 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
// To get at least one model provider
// Sets some basic model info
if (customModelData > 0) {
itemBuilder.clientBoundDataModifier(new CustomModelDataModifier<>(customModelData));
if (clientBoundModel) {
itemBuilder.clientBoundDataModifier(new CustomModelDataModifier<>(customModelData));
} else {
itemBuilder.dataModifier(new CustomModelDataModifier<>(customModelData));
}
}
// Requires the item to have model before apply item-model
else if (!hasItemModelSection && section.containsKey("model") && VersionHelper.isOrAbove1_21_2()) {
@@ -343,7 +354,11 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
// customize or use the id
itemModelKey = Key.from(section.getOrDefault("item-model", id.toString()).toString());
if (ResourceLocation.isValid(itemModelKey.toString())) {
itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey));
if (clientBoundModel) {
itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey));
} else {
itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey));
}
} else {
itemModelKey = null;
}
@@ -351,7 +366,11 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
if (hasItemModelSection && VersionHelper.isOrAbove1_21_2()) {
itemModelKey = Key.from(section.get("item-model").toString());
itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey));
if (clientBoundModel) {
itemBuilder.clientBoundDataModifier(new ItemModelModifier<>(itemModelKey));
} else {
itemBuilder.dataModifier(new ItemModelModifier<>(itemModelKey));
}
}
// Get item data

View File

@@ -2,8 +2,8 @@ package net.momirealms.craftengine.core.item;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.equipment.Equipment;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.pack.misc.Equipment;
import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel;
import net.momirealms.craftengine.core.pack.model.ModernItemModel;
import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator;
@@ -21,14 +21,14 @@ public interface ItemManager<T> extends Manageable, ModelGenerator {
void registerDataType(Function<Object, ItemDataModifier<T>> factory, String... alias);
Map<Key, Equipment> equipments();
ConfigParser[] parsers();
Map<Key, TreeSet<LegacyOverridesModel>> legacyItemOverrides();
Map<Key, TreeMap<Integer, ModernItemModel>> modernItemOverrides();
Map<Key, Equipment> equipmentsToGenerate();
Map<Key, ModernItemModel> modernItemModels1_21_4();
Map<Key, TreeSet<LegacyOverridesModel>> modernItemModels1_21_2();
@@ -57,6 +57,8 @@ public interface ItemManager<T> extends Manageable, ModelGenerator {
boolean registerExternalItemProvider(ExternalItemProvider<T> externalItemProvider);
Optional<Equipment> getEquipment(Key key);
Optional<CustomItem<T>> getCustomItem(Key key);
Optional<List<ItemBehavior>> getItemBehavior(Key key);

View File

@@ -5,11 +5,13 @@ import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.entity.projectile.ProjectileType;
import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment;
import net.momirealms.craftengine.core.item.equipment.Equipment;
import net.momirealms.craftengine.core.item.modifier.EquippableModifier;
import net.momirealms.craftengine.core.item.modifier.FoodModifier;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.item.setting.*;
import net.momirealms.craftengine.core.pack.misc.EquipmentLayerType;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.*;
@@ -23,8 +25,6 @@ import java.util.stream.Collectors;
public class ItemSettings {
int fuelTime;
Set<Key> tags = Set.of();
@Nullable
ItemEquipment equipment;
boolean canRepair = true;
List<AnvilRepairItem> anvilRepairItems = List.of();
boolean renameable = true;
@@ -38,13 +38,21 @@ public class ItemSettings {
List<DamageSource> invulnerable = List.of();
boolean canEnchant = true;
float compostProbability= 0.5f;
@Nullable
ItemEquipment equipment;
private ItemSettings() {}
public <I> List<ItemDataModifier<I>> modifiers() {
ArrayList<ItemDataModifier<I>> modifiers = new ArrayList<>();
if (VersionHelper.isOrAbove1_21_2() && this.equipment != null) {
modifiers.add(new EquippableModifier<>(this.equipment.data()));
if (this.equipment != null) {
EquipmentData data = this.equipment.equipmentData();
if (data != null) {
modifiers.add(new EquippableModifier<>(data));
}
if (!this.equipment.clientBoundModel().asBoolean(Config.globalClientboundModel())) {
modifiers.add(this.equipment.equipment().modifier());
}
}
if (VersionHelper.isOrAbove1_20_5() && this.foodData != null) {
modifiers.add(new FoodModifier<>(this.foodData.nutrition(), this.foodData.saturation(), false));
@@ -52,6 +60,16 @@ public class ItemSettings {
return modifiers;
}
public <I> List<ItemDataModifier<I>> clientBoundModifiers() {
ArrayList<ItemDataModifier<I>> modifiers = new ArrayList<>();
if (this.equipment != null) {
if (this.equipment.clientBoundModel().asBoolean(Config.globalClientboundModel())) {
modifiers.add(this.equipment.equipment().modifier());
}
}
return modifiers;
}
public static ItemSettings of() {
return new ItemSettings();
}
@@ -302,16 +320,31 @@ public class ItemSettings {
Map<String, Object> args = MiscUtils.castToMap(value, false);
EquipmentData data = EquipmentData.fromMap(args);
ComponentBasedEquipment componentBasedEquipment = ComponentBasedEquipment.FACTORY.create(data.assetId(), args);
ItemEquipment itemEquipment = new ItemEquipment(data, componentBasedEquipment);
ItemEquipment itemEquipment = new ItemEquipment(Tristate.FALSE, data, componentBasedEquipment);
return settings -> settings.equipment(itemEquipment);
}));
registerFactory("equipment", (value -> {
Map<String, Object> args = MiscUtils.castToMap(value, false);
Tristate clientBoundModel = Tristate.of((Boolean) args.get("client-bound-model"));
Key assetId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("asset-id"), "warning.config.item.settings.equipment.missing_asset_id"));
Optional<Equipment> optionalEquipment = CraftEngine.instance().itemManager().getEquipment(assetId);
if (optionalEquipment.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.item.settings.equipment.invalid_asset_id");
}
if (VersionHelper.isOrAbove1_21_2() && args.containsKey("slot")) {
EquipmentData data = EquipmentData.fromMap(args);
return settings -> settings.equipment(new ItemEquipment(clientBoundModel, data, optionalEquipment.get()));
} else {
return settings -> settings.equipment(new ItemEquipment(clientBoundModel, null, optionalEquipment.get()));
}
}));
registerFactory("can-place", (value -> {
boolean bool = ResourceConfigUtils.getAsBoolean(value, "can-place");
return settings -> settings.canPlaceRelatedVanillaBlock(bool);
}));
registerFactory("projectile", (value -> {
Map<String, Object> args = MiscUtils.castToMap(value, false);
Key customTridentItemId = Key.of(Objects.requireNonNull(args.get("item"), "'item should not be null'").toString());
Key customTridentItemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("item"), "warning.config.item.settings.projectile.missing_item"));
ItemDisplayContext displayType = ItemDisplayContext.valueOf(args.getOrDefault("display-transform", "NONE").toString().toUpperCase(Locale.ENGLISH));
Billboard billboard = Billboard.valueOf(args.getOrDefault("billboard", "FIXED").toString().toUpperCase(Locale.ENGLISH));
Vector3f translation = MiscUtils.getAsVector3f(args.getOrDefault("translation", "0"), "translation");

View File

@@ -1,7 +1,9 @@
package net.momirealms.craftengine.core.item.equipment;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.momirealms.craftengine.core.pack.misc.EquipmentLayerType;
import net.momirealms.craftengine.core.item.modifier.EquippableAssetIdModifier;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
@@ -14,7 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class ComponentBasedEquipment extends AbstractEquipment {
public class ComponentBasedEquipment extends AbstractEquipment implements Supplier<JsonObject> {
public static final Factory FACTORY = new Factory();
private final EnumMap<EquipmentLayerType, List<Layer>> layers;
@@ -24,10 +26,15 @@ public class ComponentBasedEquipment extends AbstractEquipment {
}
@Override
public Key renderingMethod() {
public Key type() {
return Equipments.COMPONENT;
}
@Override
public <I> ItemDataModifier<I> modifier() {
return new EquippableAssetIdModifier<>(this.assetId);
}
public EnumMap<EquipmentLayerType, List<Layer>> layers() {
return layers;
}
@@ -36,6 +43,28 @@ public class ComponentBasedEquipment extends AbstractEquipment {
this.layers.put(layerType, layer);
}
@Override
public JsonObject get() {
JsonObject jsonObject = new JsonObject();
JsonObject layersJson = new JsonObject();
jsonObject.add("layers", layersJson);
for (Map.Entry<EquipmentLayerType, List<ComponentBasedEquipment.Layer>> entry : layers.entrySet()) {
EquipmentLayerType type = entry.getKey();
List<ComponentBasedEquipment.Layer> layerList = entry.getValue();
setLayers(layersJson, layerList, type.id());
}
return jsonObject;
}
private void setLayers(JsonObject layersJson, List<ComponentBasedEquipment.Layer> layers, String key) {
if (layers == null || layers.isEmpty()) return;
JsonArray layersArray = new JsonArray();
for (ComponentBasedEquipment.Layer layer : layers) {
layersArray.add(layer.get());
}
layersJson.add(key, layersArray);
}
public static class Factory implements EquipmentFactory {
@Override

View File

@@ -1,10 +1,13 @@
package net.momirealms.craftengine.core.item.equipment;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.util.Key;
public interface Equipment {
Key assetId();
Key renderingMethod();
Key type();
<I> ItemDataModifier<I> modifier();
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.pack.misc;
package net.momirealms.craftengine.core.item.equipment;
import java.util.HashMap;
import java.util.Map;

View File

@@ -1,18 +1,23 @@
package net.momirealms.craftengine.core.item.equipment;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.Registries;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.ResourceKey;
public class Equipments {
import java.util.Map;
public final class Equipments {
public static final Key TRIM = Key.of("craftengine:trim");
public static final Key COMPONENT = Key.of("craftengine:component");
static {
register(TRIM, TrimBasedEquipment.FACTORY);
register(COMPONENT, ComponentBasedEquipment.FACTORY);
}
public static void register(Key key, EquipmentFactory factory) {
@@ -20,4 +25,14 @@ public class Equipments {
.registerForHolder(new ResourceKey<>(Registries.EQUIPMENT_FACTORY.location(), key));
holder.bindValue(factory);
}
public static Equipment fromMap(Key id, Map<String, Object> map) {
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.equipment.missing_type");
Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE);
EquipmentFactory factory = BuiltInRegistries.EQUIPMENT_FACTORY.getValue(key);
if (factory == null) {
throw new LocalizedResourceConfigException("warning.config.equipment.invalid_type", type);
}
return factory.create(id, map);
}
}

View File

@@ -1,42 +1,52 @@
package net.momirealms.craftengine.core.item.equipment;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.item.modifier.TrimModifier;
import net.momirealms.craftengine.core.pack.AbstractPackManager;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
public class TrimBasedEquipment extends AbstractEquipment {
public static final Factory FACTORY = new Factory();
private final Key humanoid;
private final Key humanoidLeggings;
public TrimBasedEquipment(Key assetId, Key humanoid, Key humanoidLeggings) {
public TrimBasedEquipment(Key assetId, @Nullable Key humanoid, @Nullable Key humanoidLeggings) {
super(assetId);
this.humanoid = humanoid;
this.humanoidLeggings = humanoidLeggings;
}
@Override
public Key renderingMethod() {
public Key type() {
return Equipments.TRIM;
}
@Nullable
public Key humanoid() {
return humanoid;
}
@Nullable
public Key humanoidLeggings() {
return humanoidLeggings;
}
@Override
public <I> ItemDataModifier<I> modifier() {
return new TrimModifier<>(AbstractPackManager.TRIM_MATERIAL, this.assetId.toString());
}
public static class Factory implements EquipmentFactory {
@Override
public Equipment create(Key id, Map<String, Object> args) {
// todo node
String humanoidId = ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("humanoid"), "");
String humanoidLeggingsId = ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("humanoid-leggings"), "");
// todo 验证resource location
return new TrimBasedEquipment(id, Key.of(humanoidId), Key.of(humanoidLeggingsId));
Key humanoidId = Optional.ofNullable((String) args.get("humanoid")).map(Key::of).orElse(null);
Key humanoidLeggingsId = Optional.ofNullable((String) args.get("humanoid-leggings")).map(Key::of).orElse(null);
return new TrimBasedEquipment(id, humanoidId, humanoidLeggingsId);
}
}
}

View File

@@ -0,0 +1,51 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.NetworkItemHandler;
import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.Tag;
import java.util.Optional;
public class EquippableAssetIdModifier<I> implements ItemDataModifier<I> {
private final Key assetId;
public EquippableAssetIdModifier(Key assetsId) {
this.assetId = assetsId;
}
@Override
public String name() {
return "equippable-asset-id";
}
@Override
public Item<I> apply(Item<I> item, ItemBuildContext context) {
Optional<EquipmentData> optionalData = item.equippable();
optionalData.ifPresent(data -> item.equippable(new EquipmentData(
data.slot(),
this.assetId,
data.dispensable(),
data.swappable(),
data.damageOnHurt(),
data.equipOnInteract(),
data.cameraOverlay()
)));
return item;
}
@Override
public Item<I> prepareNetworkItem(Item<I> item, ItemBuildContext context, CompoundTag networkData) {
Tag previous = item.getNBTComponent(ComponentKeys.EQUIPPABLE);
if (previous != null) {
networkData.put(ComponentKeys.EQUIPPABLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous));
} else {
networkData.put(ComponentKeys.EQUIPPABLE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE));
}
return item;
}
}

View File

@@ -9,6 +9,7 @@ import net.momirealms.sparrow.nbt.CompoundTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
@@ -44,8 +45,9 @@ public class EquipmentData {
public static EquipmentData fromMap(@NotNull final Map<String, Object> data) {
String slot = (String) data.get("slot");
if (slot == null) {
throw new LocalizedResourceConfigException("warning.config.item.settings.equippable.missing_slot");
throw new IllegalArgumentException("slot cannot be null");
}
// todo 重新写判断,不应该支持手部
EquipmentSlot slotEnum = EquipmentSlot.valueOf(slot.toUpperCase(Locale.ENGLISH));
EquipmentData.Builder builder = EquipmentData.builder().slot(slotEnum);
if (data.containsKey("asset-id")) {

View File

@@ -1,29 +1,33 @@
package net.momirealms.craftengine.core.item.setting;
import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment;
import net.momirealms.craftengine.core.pack.misc.EquipmentLayerType;
import java.util.EnumMap;
import java.util.List;
import net.momirealms.craftengine.core.item.equipment.Equipment;
import net.momirealms.craftengine.core.util.Tristate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ItemEquipment {
private final EquipmentData data;
private final ComponentBasedEquipment equipment;
private final Tristate clientBoundModel;
private final Equipment equipment;
private final EquipmentData equipmentData;
public ItemEquipment(EquipmentData data, ComponentBasedEquipment equipment) {
this.data = data;
public ItemEquipment(Tristate clientBoundModel, @Nullable EquipmentData equipmentData, Equipment equipment) {
this.clientBoundModel = clientBoundModel;
this.equipment = equipment;
this.equipmentData = equipmentData;
}
public void addLayer(EquipmentLayerType layerType, List<ComponentBasedEquipment.Layer> layer) {
this.equipment.addLayer(layerType, layer);
@NotNull
public Equipment equipment() {
return this.equipment;
}
public EnumMap<EquipmentLayerType, List<ComponentBasedEquipment.Layer>> layers() {
return this.equipment.layers();
@Nullable
public EquipmentData equipmentData() {
return this.equipmentData;
}
public EquipmentData data() {
return data;
@NotNull
public Tristate clientBoundModel() {
return clientBoundModel;
}
}

View File

@@ -9,12 +9,14 @@ import dev.dejvokep.boostedyaml.YamlDocument;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.momirealms.craftengine.core.font.BitmapImage;
import net.momirealms.craftengine.core.font.Font;
import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment;
import net.momirealms.craftengine.core.item.equipment.Equipment;
import net.momirealms.craftengine.core.item.equipment.TrimBasedEquipment;
import net.momirealms.craftengine.core.pack.conflict.PathContext;
import net.momirealms.craftengine.core.pack.conflict.resolution.ResolutionConditional;
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.pack.host.impl.NoneHost;
import net.momirealms.craftengine.core.pack.misc.Equipment;
import net.momirealms.craftengine.core.pack.model.ItemModel;
import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel;
import net.momirealms.craftengine.core.pack.model.ModernItemModel;
@@ -25,6 +27,7 @@ import net.momirealms.craftengine.core.pack.model.rangedisptach.CustomModelDataR
import net.momirealms.craftengine.core.pack.obfuscation.ObfA;
import net.momirealms.craftengine.core.pack.obfuscation.ResourcePackGenerationException;
import net.momirealms.craftengine.core.pack.revision.Revision;
import net.momirealms.craftengine.core.pack.revision.Revisions;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
@@ -65,9 +68,8 @@ public abstract class AbstractPackManager implements PackManager {
public static final Map<Key, ModernItemModel> PRESET_ITEMS = new HashMap<>();
public static final Set<Key> VANILLA_TEXTURES = new HashSet<>();
public static final Set<Key> VANILLA_MODELS = new HashSet<>();
public static final String TRIM_MATERIAL = "custom";
private static final byte[] EMPTY_IMAGE;
private static final String[] TRIM_ITEMS = {"boots_trim","chestplate_trim","helmet_trim","leggings_trim"};
private static final String[] TRIM_COLOR_PALETTES = {"amethyst","copper","diamond","diamond_darker","emerald","gold","gold_darker","iron","iron_darker","lapis","netherite","netherite_darker","quartz","redstone","resin","trim_palette"};
static {
var stream = new ByteArrayOutputStream();
try {
@@ -545,6 +547,7 @@ public abstract class AbstractPackManager implements PackManager {
ConfigParser parser = entry.getKey();
if (!predicate.test(parser)) continue;
long t1 = System.nanoTime();
parser.preProcess();
for (CachedConfigSection cached : entry.getValue()) {
for (Map.Entry<String, Object> configEntry : cached.config().entrySet()) {
String key = configEntry.getKey();
@@ -578,6 +581,7 @@ public abstract class AbstractPackManager implements PackManager {
}
}
}
parser.postProcess();
long t2 = System.nanoTime();
this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms");
}
@@ -632,7 +636,7 @@ public abstract class AbstractPackManager implements PackManager {
this.generateOverrideSounds(generatedPackPath);
this.generateCustomSounds(generatedPackPath);
this.generateClientLang(generatedPackPath);
this.generateEquipments(generatedPackPath);
this.generateEquipments(generatedPackPath, revisions::add);
this.generateParticle(generatedPackPath);
this.generatePackMetadata(generatedPackPath.resolve("pack.mcmeta"), revisions);
if (Config.excludeShaders()) {
@@ -1161,74 +1165,315 @@ public abstract class AbstractPackManager implements PackManager {
}
}
private void generateEquipments(Path generatedPackPath) {
for (Map.Entry<Key, Equipment> entry : this.plugin.itemManager().equipmentsToGenerate().entrySet()) {
Key assetId = entry.getKey();
if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) {
Path equipmentPath = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("equipment")
.resolve(assetId.value() + ".json");
private void generateEquipments(Path generatedPackPath, Consumer<Revision> callback) {
// asset id + 是否有上身 + 是否有腿
List<Tuple<Key, Boolean, Boolean>> collectedTrims = new ArrayList<>();
JsonObject equipmentJson = null;
if (Files.exists(equipmentPath)) {
try (BufferedReader reader = Files.newBufferedReader(equipmentPath)) {
equipmentJson = JsonParser.parseReader(reader).getAsJsonObject();
// 为trim类型提供的两个兼容性值
boolean needLegacyCompatibility = Config.packMinVersion().isBelow(MinecraftVersions.V1_21_2);
boolean needModernCompatibility = Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2);
for (Equipment equipment : this.plugin.itemManager().equipments().values()) {
if (equipment instanceof ComponentBasedEquipment componentBasedEquipment) {
// 现代的盔甲生成
Key assetId = componentBasedEquipment.assetId();
if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) {
Path equipmentPath = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("equipment")
.resolve(assetId.value() + ".json");
JsonObject equipmentJson = null;
if (Files.exists(equipmentPath)) {
try (BufferedReader reader = Files.newBufferedReader(equipmentPath)) {
equipmentJson = JsonParser.parseReader(reader).getAsJsonObject();
} catch (IOException e) {
plugin.logger().warn("Failed to load existing sounds.json", e);
return;
}
}
if (equipmentJson != null) {
equipmentJson = GsonHelper.deepMerge(equipmentJson, componentBasedEquipment.get());
} else {
equipmentJson = componentBasedEquipment.get();
}
try {
Files.createDirectories(equipmentPath.getParent());
} catch (IOException e) {
plugin.logger().warn("Failed to load existing sounds.json", e);
plugin.logger().severe("Error creating " + equipmentPath.toAbsolutePath());
return;
}
try {
GsonHelper.writeJsonFile(equipmentJson, equipmentPath);
} catch (IOException e) {
this.plugin.logger().severe("Error writing equipment file", e);
}
}
if (equipmentJson != null) {
equipmentJson = GsonHelper.deepMerge(equipmentJson, entry.getValue().get());
} else {
equipmentJson = entry.getValue().get();
if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4)) {
Path equipmentPath = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("models")
.resolve("equipment")
.resolve(assetId.value() + ".json");
JsonObject equipmentJson = null;
if (Files.exists(equipmentPath)) {
try (BufferedReader reader = Files.newBufferedReader(equipmentPath)) {
equipmentJson = JsonParser.parseReader(reader).getAsJsonObject();
} catch (IOException e) {
plugin.logger().warn("Failed to load existing sounds.json", e);
return;
}
}
if (equipmentJson != null) {
equipmentJson = GsonHelper.deepMerge(equipmentJson, componentBasedEquipment.get());
} else {
equipmentJson = componentBasedEquipment.get();
}
try {
Files.createDirectories(equipmentPath.getParent());
} catch (IOException e) {
plugin.logger().severe("Error creating " + equipmentPath.toAbsolutePath());
return;
}
try {
GsonHelper.writeJsonFile(equipmentJson, equipmentPath);
} catch (IOException e) {
this.plugin.logger().severe("Error writing equipment file", e);
}
}
} else if (equipment instanceof TrimBasedEquipment trimBasedEquipment) {
Key assetId = trimBasedEquipment.assetId();
Key humanoidResourceLocation = trimBasedEquipment.humanoid();
boolean hasLayer1 = humanoidResourceLocation != null;
Key humanoidLeggingsResourceLocation = trimBasedEquipment.humanoidLeggings();
boolean hasLayer2 = humanoidLeggingsResourceLocation != null;
if (hasLayer1) {
Path texture = generatedPackPath
.resolve("assets")
.resolve(humanoidResourceLocation.namespace())
.resolve("textures")
.resolve(humanoidResourceLocation.value() + ".png");
if (!Files.exists(texture) || !Files.isRegularFile(texture)) {
// todo 说话
continue;
}
boolean shouldPreserve = false;
if (needLegacyCompatibility) {
Path legacyTarget = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("textures")
.resolve("trims")
.resolve("models")
.resolve("armor")
.resolve(assetId.value() + "_" + TRIM_MATERIAL + ".png");
if (!legacyTarget.equals(texture)) {
try {
Files.createDirectories(legacyTarget.getParent());
Files.copy(texture, legacyTarget, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
plugin.logger().severe("Error writing armor texture file from " + texture + " to " + legacyTarget, e);
}
} else {
shouldPreserve = true;
}
}
if (needModernCompatibility) {
Path modernTarget = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("textures")
.resolve("trims")
.resolve("entity")
.resolve("humanoid")
.resolve(assetId.value() + "_" + TRIM_MATERIAL + ".png");
if (!modernTarget.equals(texture)) {
try {
Files.createDirectories(modernTarget.getParent());
Files.copy(texture, modernTarget, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
plugin.logger().severe("Error writing armor texture file from " + texture + " to " + modernTarget, e);
}
} else {
shouldPreserve = true;
}
}
if (!shouldPreserve) {
try {
Files.delete(texture);
} catch (IOException e) {
this.plugin.logger().severe("Error deleting armor texture file from " + texture, e);
}
}
}
if (hasLayer2) {
Path texture = generatedPackPath
.resolve("assets")
.resolve(humanoidLeggingsResourceLocation.namespace())
.resolve("textures")
.resolve(humanoidLeggingsResourceLocation.value() + ".png");
if (!Files.exists(texture) && !Files.isRegularFile(texture)) {
// todo 说话
continue;
}
boolean shouldPreserve = false;
if (needLegacyCompatibility) {
Path legacyTarget = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("textures")
.resolve("trims")
.resolve("models")
.resolve("armor")
.resolve(assetId.value() + "_leggings_" + TRIM_MATERIAL + ".png");
if (!legacyTarget.equals(texture)) {
try {
Files.createDirectories(legacyTarget.getParent());
Files.copy(texture, legacyTarget, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
this.plugin.logger().severe("Error writing armor texture file from " + texture + " to " + legacyTarget, e);
}
} else {
shouldPreserve = true;
}
}
if (needModernCompatibility) {
Path modernTarget = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("textures")
.resolve("trims")
.resolve("entity")
.resolve("humanoid_leggings")
.resolve(assetId.value() + "_" + TRIM_MATERIAL + ".png");
if (!modernTarget.equals(texture)) {
try {
Files.createDirectories(modernTarget.getParent());
Files.copy(texture, modernTarget, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
this.plugin.logger().severe("Error writing armor texture file from " + texture + " to " + modernTarget, e);
}
} else {
shouldPreserve = true;
}
}
if (!shouldPreserve) {
try {
Files.delete(texture);
} catch (IOException e) {
this.plugin.logger().severe("Error deleting armor texture file from " + texture, e);
}
}
}
collectedTrims.add(Tuple.of(assetId, hasLayer1, hasLayer2));
}
}
if (!collectedTrims.isEmpty()) {
// 获取基础atlas路径
Path atlasPath = generatedPackPath
.resolve("assets")
.resolve("minecraft")
.resolve("atlases")
.resolve("armor_trims.json");
// 读取先前sources内容
JsonArray previousAtlasSources = null;
if (Files.exists(atlasPath) && Files.isRegularFile(atlasPath)) {
try {
Files.createDirectories(equipmentPath.getParent());
} catch (IOException e) {
plugin.logger().severe("Error creating " + equipmentPath.toAbsolutePath());
return;
}
try {
GsonHelper.writeJsonFile(equipmentJson, equipmentPath);
} catch (IOException e) {
this.plugin.logger().severe("Error writing equipment file", e);
previousAtlasSources = GsonHelper.readJsonFile(atlasPath).getAsJsonObject().getAsJsonArray("sources");
} catch (Exception ignored) {
}
}
if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4)) {
Path equipmentPath = generatedPackPath
.resolve("assets")
.resolve(assetId.namespace())
.resolve("models")
.resolve("equipment")
.resolve(assetId.value() + ".json");
JsonObject equipmentJson = null;
if (Files.exists(equipmentPath)) {
try (BufferedReader reader = Files.newBufferedReader(equipmentPath)) {
equipmentJson = JsonParser.parseReader(reader).getAsJsonObject();
} catch (IOException e) {
plugin.logger().warn("Failed to load existing sounds.json", e);
return;
// 准备新版本atlas
JsonObject modernTrimAtlasJson = null;
if (needModernCompatibility) {
modernTrimAtlasJson = new JsonObject();
JsonArray sourcesArray = new JsonArray();
modernTrimAtlasJson.add("sources", sourcesArray);
for (Tuple<Key, Boolean, Boolean> tuple : collectedTrims) {
if (tuple.mid()) {
JsonObject single1 = new JsonObject();
single1.addProperty("type", "single");
single1.addProperty("resource", tuple.left().namespace() + ":trims/entity/humanoid/" + tuple.left().value() + "_" + TRIM_MATERIAL);
sourcesArray.add(single1);
}
if (tuple.right()) {
JsonObject single2 = new JsonObject();
single2.addProperty("type", "single");
single2.addProperty("resource", tuple.left().namespace() + ":trims/entity/humanoid_leggings/" + tuple.left().value() + "_" + TRIM_MATERIAL);
sourcesArray.add(single2);
}
}
if (equipmentJson != null) {
equipmentJson = GsonHelper.deepMerge(equipmentJson, entry.getValue().get());
} else {
equipmentJson = entry.getValue().get();
if (previousAtlasSources != null) {
sourcesArray.addAll(previousAtlasSources);
}
}
// 准备旧版本atlas
JsonObject legacyTrimAtlasJson = null;
if (needLegacyCompatibility) {
legacyTrimAtlasJson = new JsonObject();
JsonArray sourcesArray = new JsonArray();
legacyTrimAtlasJson.add("sources", sourcesArray);
for (Tuple<Key, Boolean, Boolean> tuple : collectedTrims) {
if (tuple.mid()) {
JsonObject single1 = new JsonObject();
single1.addProperty("type", "single");
single1.addProperty("resource", tuple.left().namespace() + ":trims/models/armor/" + tuple.left().value() + "_" + TRIM_MATERIAL);
sourcesArray.add(single1);
}
if (tuple.right()) {
JsonObject single2 = new JsonObject();
single2.addProperty("type", "single");
single2.addProperty("resource", tuple.left().namespace() + ":trims/models/armor/" + tuple.left().value() + "_leggings_" + TRIM_MATERIAL);
sourcesArray.add(single2);
}
}
if (previousAtlasSources != null) {
sourcesArray.addAll(previousAtlasSources);
}
}
// 创建atlas文件夹
try {
Files.createDirectories(atlasPath.getParent());
} catch (IOException e) {
this.plugin.logger().severe("Error creating " + atlasPath.toAbsolutePath(), e);
return;
}
// 写入atlas文件
try (BufferedWriter writer = Files.newBufferedWriter(atlasPath)) {
JsonObject selected = needLegacyCompatibility ? legacyTrimAtlasJson : modernTrimAtlasJson;
// 优先写入旧版
GsonHelper.get().toJson(selected, writer);
} catch (IOException e) {
this.plugin.logger().severe("Error writing " + atlasPath.toAbsolutePath(), e);
}
// 既要又要那么需要overlay
if (needLegacyCompatibility && needModernCompatibility) {
Revision revision = Revisions.SINCE_1_21_2;
callback.accept(revision);
Path overlayAtlasPath = generatedPackPath
.resolve(Config.createOverlayFolderName(revision.versionString()))
.resolve("assets")
.resolve("minecraft")
.resolve("atlases")
.resolve("armor_trims.json");
// 创建atlas文件夹
try {
Files.createDirectories(equipmentPath.getParent());
Files.createDirectories(overlayAtlasPath.getParent());
} catch (IOException e) {
plugin.logger().severe("Error creating " + equipmentPath.toAbsolutePath());
this.plugin.logger().severe("Error creating " + overlayAtlasPath.toAbsolutePath(), e);
return;
}
try {
GsonHelper.writeJsonFile(equipmentJson, equipmentPath);
// 写入atlas文件
try (BufferedWriter writer = Files.newBufferedWriter(overlayAtlasPath)) {
GsonHelper.get().toJson(modernTrimAtlasJson, writer);
callback.accept(revision);
} catch (IOException e) {
this.plugin.logger().severe("Error writing equipment file", e);
this.plugin.logger().severe("Error writing " + overlayAtlasPath.toAbsolutePath(), e);
}
}
}

View File

@@ -1,51 +0,0 @@
package net.momirealms.craftengine.core.pack.misc;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment;
import net.momirealms.craftengine.core.item.setting.ItemEquipment;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class Equipment implements Supplier<JsonObject> {
private final EnumMap<EquipmentLayerType, List<ComponentBasedEquipment.Layer>> layers;
public Equipment() {
this.layers = new EnumMap<>(EquipmentLayerType.class);
}
public void addAll(ItemEquipment equipment) {
for (Map.Entry<EquipmentLayerType, List<ComponentBasedEquipment.Layer>> entry : equipment.layers().entrySet()) {
List<ComponentBasedEquipment.Layer> layers = entry.getValue();
List<ComponentBasedEquipment.Layer> previous = this.layers.put(entry.getKey(), layers);
if (previous != null && !previous.equals(layers)) {
// todo 是否异常
}
}
}
@Override
public JsonObject get() {
JsonObject jsonObject = new JsonObject();
JsonObject layersJson = new JsonObject();
jsonObject.add("layers", layersJson);
for (Map.Entry<EquipmentLayerType, List<ComponentBasedEquipment.Layer>> entry : layers.entrySet()) {
EquipmentLayerType type = entry.getKey();
List<ComponentBasedEquipment.Layer> layerList = entry.getValue();
setLayers(layersJson, layerList, type.id());
}
return jsonObject;
}
private void setLayers(JsonObject layersJson, List<ComponentBasedEquipment.Layer> layers, String key) {
if (layers == null || layers.isEmpty()) return;
JsonArray layersArray = new JsonArray();
for (ComponentBasedEquipment.Layer layer : layers) {
layersArray.add(layer.get());
}
layersJson.add(key, layersArray);
}
}

View File

@@ -6,4 +6,5 @@ public final class Revisions {
private Revisions() {}
public static final Revision SINCE_1_21_6 = Revision.since(MinecraftVersions.V1_21_6);
public static final Revision SINCE_1_21_2 = Revision.since(MinecraftVersions.V1_21_2);
}

View File

@@ -151,6 +151,8 @@ public abstract class CraftEngine implements Plugin {
} catch (Exception e) {
this.logger().warn("Failed to load resources folder", e);
}
// register trims
this.itemManager.delayedLoad();
// init suggestions and packet mapper
this.blockManager.delayedLoad();
// handle some special client lang for instance block_name

View File

@@ -143,6 +143,13 @@ public class Config {
protected boolean image$intercept_packets$set_score;
protected boolean image$intercept_packets$item;
protected boolean item$client_bound_model;
protected String equipment$sacrificed_vanilla_armor$type;
protected Key equipment$sacrificed_vanilla_armor$asset_id;
protected Key equipment$sacrificed_vanilla_armor$humanoid;
protected Key equipment$sacrificed_vanilla_armor$humanoid_leggings;
protected boolean emoji$chat;
protected boolean emoji$book;
protected boolean emoji$anvil;
@@ -321,6 +328,15 @@ public class Config {
furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true);
furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH));
// equipment
equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.type", "chainmail");
equipment$sacrificed_vanilla_armor$asset_id = Key.of(config.getString("equipment.sacrificed-vanilla-armor.asset-id", "minecraft:chainmail"));
equipment$sacrificed_vanilla_armor$humanoid = Key.of(config.getString("equipment.sacrificed-vanilla-armor.humanoid"));
equipment$sacrificed_vanilla_armor$humanoid_leggings = Key.of(config.getString("equipment.sacrificed-vanilla-armor.humanoid-leggings"));
// item
item$client_bound_model = config.getBoolean("item.client-bound-model", false);
// block
block$sound_system$enable = config.getBoolean("block.sound-system.enable", true);
block$simplify_adventure_break_check = config.getBoolean("block.simplify-adventure-break-check", false);
@@ -739,6 +755,26 @@ public class Config {
return instance.resource_pack$overlay_format.replace("{version}", version);
}
public static Key sacrificedAssetId() {
return instance.equipment$sacrificed_vanilla_armor$asset_id;
}
public static Key sacrificedHumanoid() {
return instance.equipment$sacrificed_vanilla_armor$humanoid;
}
public static Key sacrificedHumanoidLeggings() {
return instance.equipment$sacrificed_vanilla_armor$humanoid_leggings;
}
public static String sacrificedVanillaArmorType() {
return instance.equipment$sacrificed_vanilla_armor$type;
}
public static boolean globalClientboundModel() {
return instance.item$client_bound_model;
}
public YamlDocument loadOrCreateYamlData(String fileName) {
Path path = this.plugin.dataFolderPath().resolve(fileName);
if (!Files.exists(path)) {

View File

@@ -29,4 +29,10 @@ public interface ConfigParser extends Comparable<ConfigParser> {
default int compareTo(@NotNull ConfigParser another) {
return Integer.compare(loadingSequence(), another.loadingSequence());
}
default void postProcess() {
}
default void preProcess() {
}
}

View File

@@ -24,4 +24,12 @@ public enum Tristate {
public boolean asBoolean() {
return this.booleanValue;
}
public boolean asBoolean(boolean defaultValue) {
if (this == UNDEFINED) {
return defaultValue;
} else {
return this.booleanValue;
}
}
}