9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-29 03:49:15 +00:00

外观状态自动分配part 1

This commit is contained in:
XiaoMoMi
2025-09-29 05:16:20 +08:00
parent f08131d771
commit 66cd1c0962
45 changed files with 359 additions and 127 deletions

View File

@@ -18,7 +18,9 @@ import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.pack.PendingConfigSection;
import net.momirealms.craftengine.core.pack.ResourceLocation;
import net.momirealms.craftengine.core.pack.cache.IdAllocator;
import net.momirealms.craftengine.core.pack.allocator.BlockStateAllocator;
import net.momirealms.craftengine.core.pack.allocator.IdAllocator;
import net.momirealms.craftengine.core.pack.allocator.BlockStateCandidate;
import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -68,8 +70,6 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
protected final Map<Integer, List<Integer>> appearanceToRealState = new Int2ObjectOpenHashMap<>();
// 用于note_block:0这样格式的自动分配
protected final Map<Key, List<BlockStateWrapper>> blockStateArranger = new HashMap<>();
// 根据registry id找note_block:x中的x值
protected final Map<Integer, Integer> reversedBlockStateArranger = new HashMap<>();
// 全方块状态映射文件,用于网络包映射
protected final int[] blockStateMappings;
// 原版方块状态数量
@@ -82,6 +82,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
protected final ImmutableBlockState[] immutableBlockStates;
// 原版方块的属性缓存
protected final BlockSettings[] vanillaBlockSettings;
// 倒推缓存
protected final BlockStateCandidate[] reversedBlockStateArranger;
// 临时存储哪些视觉方块被使用了
protected final Set<BlockStateWrapper> tempVisualBlockStatesInUse = new HashSet<>();
protected final Set<Key> tempVisualBlocksInUse = new HashSet<>();
@@ -99,6 +101,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
this.vanillaBlockSettings = new BlockSettings[vanillaBlockStateCount];
this.immutableBlockStates = new ImmutableBlockState[customBlockCount];
this.blockStateMappings = new int[customBlockCount + vanillaBlockStateCount];
this.reversedBlockStateArranger = new BlockStateCandidate[vanillaBlockStateCount];
Arrays.fill(this.blockStateMappings, -1);
}
@@ -127,10 +130,10 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
this.modBlockStateOverrides.clear();
this.byId.clear();
this.blockStateArranger.clear();
this.reversedBlockStateArranger.clear();
this.appearanceToRealState.clear();
Arrays.fill(this.blockStateMappings, -1);
Arrays.fill(this.immutableBlockStates, EmptyBlock.STATE);
Arrays.fill(this.reversedBlockStateArranger, null);
}
@Override
@@ -272,9 +275,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
continue;
}
AbstractBlockManager.this.blockStateMappings[beforeState.registryId()] = afterState.registryId();
List<BlockStateWrapper> blockStateWrappers = AbstractBlockManager.this.blockStateArranger.computeIfAbsent(getBlockOwnerId(beforeState), k -> new ArrayList<>());
Key blockOwnerId = getBlockOwnerId(beforeState);
List<BlockStateWrapper> blockStateWrappers = AbstractBlockManager.this.blockStateArranger.computeIfAbsent(blockOwnerId, k -> new ArrayList<>());
blockStateWrappers.add(beforeState);
AbstractBlockManager.this.reversedBlockStateArranger.put(beforeState.registryId(), blockStateWrappers.size() - 1);
AbstractBlockManager.this.reversedBlockStateArranger[beforeState.registryId()] = blockParser.createVisualBlockCandidate(beforeState);
}
exceptionCollector.throwIfPresent();
}
@@ -283,8 +288,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
public class BlockParser implements IdSectionConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"};
private final IdAllocator internalIdAllocator;
private final Map<Key, IdAllocator> appearanceIdAllocators = new HashMap<>();
private final List<PendingConfigSection> pendingConfigSections = new ArrayList<>();
private final BlockStateAllocator[] visualBlockStateAllocators = new BlockStateAllocator[AutoStateGroup.values().length];
public BlockParser() {
this.internalIdAllocator = new IdAllocator(AbstractBlockManager.this.plugin.dataFolderPath().resolve("cache").resolve("custom-block-states.json"));
@@ -294,6 +299,29 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
this.pendingConfigSections.add(section);
}
@Nullable
public BlockStateCandidate createVisualBlockCandidate(BlockStateWrapper blockState) {
List<AutoStateGroup> groups = AutoStateGroup.findGroups(blockState);
if (!groups.isEmpty()) {
BlockStateCandidate candidate = new BlockStateCandidate(blockState);
for (AutoStateGroup group : groups) {
getOrCreateBlockStateAllocator(group).addCandidate(candidate);
}
return candidate;
}
return null;
}
private BlockStateAllocator getOrCreateBlockStateAllocator(AutoStateGroup group) {
int index = group.ordinal();
BlockStateAllocator visualBlockStateAllocator = this.visualBlockStateAllocators[index];
if (visualBlockStateAllocator == null) {
visualBlockStateAllocator = new BlockStateAllocator();
this.visualBlockStateAllocators[index] = visualBlockStateAllocator;
}
return visualBlockStateAllocator;
}
@Override
public void postProcess() {
this.internalIdAllocator.processPendingAllocations();
@@ -323,23 +351,6 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
this.pendingConfigSections.clear();
}
@Nullable
public IdAllocator getOrCreateAppearanceIdAllocator(Key type) {
if (!AbstractBlockManager.this.blockStateArranger.containsKey(type)) {
return null;
}
return this.appearanceIdAllocators.computeIfAbsent(type, k -> {
IdAllocator newAllocator = new IdAllocator(plugin.dataFolderPath().resolve("cache").resolve("visual-block-states").resolve(k.value() + ".json"));
newAllocator.reset(0, AbstractBlockManager.this.blockStateArranger.get(type).size());
try {
newAllocator.loadFromCache();
} catch (IOException e) {
AbstractBlockManager.this.plugin.logger().warn("Error while loading visual block states cache for block " + k.asString(), e);
}
return newAllocator;
});
}
@Override
public String[] sectionId() {
return CONFIG_SECTION_NAME;

View File

@@ -0,0 +1,21 @@
package net.momirealms.craftengine.core.block;
public abstract class AbstractBlockStateWrapper implements BlockStateWrapper {
protected final Object blockState;
protected final int registryId;
protected AbstractBlockStateWrapper(Object blockState, int registryId) {
this.blockState = blockState;
this.registryId = registryId;
}
@Override
public Object literalObject() {
return this.blockState;
}
@Override
public int registryId() {
return this.registryId;
}
}

View File

@@ -0,0 +1,84 @@
package net.momirealms.craftengine.core.block;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Predicate;
public enum AutoStateGroup {
LEAVES("leaves",
Set.of(BlockKeys.OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES, BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES),
(w) -> !(boolean) w.getProperty("waterlogged"), 0
),
WATERLOGGED_LEAVES(
"waterlogged_leaves",
Set.of(BlockKeys.OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES, BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES),
(w) -> w.getProperty("waterlogged"), 0
),
TRIPWIRE("tripwire", Set.of(BlockKeys.TRIPWIRE), (w) -> true, 1),
LOWER_TRIPWIRE("lower_tripwire", Set.of(BlockKeys.TRIPWIRE), (w) -> w.getProperty("attached"), 0),
HIGHER_TRIPWIRE("higher_tripwire", Set.of(BlockKeys.TRIPWIRE), (w) -> !(boolean) w.getProperty("attached"), 0),
NOTE_BLOCK("note_block", Set.of(BlockKeys.NOTE_BLOCK), (w) -> true, 0),
BROWN_MUSHROOM("brown_mushroom", Set.of(BlockKeys.BROWN_MUSHROOM_BLOCK), (w) -> true, 0),
RED_MUSHROOM("red_mushroom", Set.of(BlockKeys.RED_MUSHROOM_BLOCK), (w) -> true, 0),
MUSHROOM_STEM("mushroom_stem", Set.of(BlockKeys.MUSHROOM_STEM), (w) -> true, 0),
MUSHROOM("mushroom", Set.of(BlockKeys.BROWN_MUSHROOM_BLOCK, BlockKeys.RED_MUSHROOM_BLOCK, BlockKeys.MUSHROOM_STEM), (w) -> true, 1),
SOLID("solid", Set.of(BlockKeys.BROWN_MUSHROOM_BLOCK, BlockKeys.RED_MUSHROOM_BLOCK, BlockKeys.MUSHROOM_STEM, BlockKeys.NOTE_BLOCK), (w) -> true, 2);
private final Set<Key> blocks;
private final String id;
private final Predicate<BlockStateWrapper> predicate;
private final int priority;
AutoStateGroup(String id, Set<Key> blocks, Predicate<BlockStateWrapper> predicate, int priority) {
this.id = id;
this.blocks = blocks;
this.predicate = predicate;
this.priority = priority;
}
public int priority() {
return priority;
}
public Set<Key> blocks() {
return blocks;
}
public String id() {
return id;
}
private static final Map<String, AutoStateGroup> BY_ID = new HashMap<>();
private static final Map<Key, List<AutoStateGroup>> BY_BLOCKS = new HashMap<>();
static {
for (AutoStateGroup group : AutoStateGroup.values()) {
BY_ID.put(group.id(), group);
BY_ID.put(group.id().toUpperCase(Locale.ROOT), group);
for (Key key : group.blocks) {
BY_BLOCKS.computeIfAbsent(key, k -> new ArrayList<>(4)).add(group);
}
}
}
@Nullable
public static AutoStateGroup byId(String id) {
return BY_ID.get(id);
}
public static List<AutoStateGroup> findGroups(BlockStateWrapper wrapper) {
return findGroups(wrapper.ownerId(), wrapper);
}
public static List<AutoStateGroup> findGroups(Key id, BlockStateWrapper wrapper) {
List<AutoStateGroup> groups = BY_BLOCKS.get(id);
if (groups == null) return Collections.emptyList();
List<AutoStateGroup> result = new ArrayList<>(groups.size());
for (AutoStateGroup group : groups) {
if (group.predicate.test(wrapper)) result.add(group);
}
return result;
}
}

View File

@@ -246,6 +246,22 @@ public final class BlockKeys {
public static final Key CACTUS = Key.of("minecraft:cactus");
public static final Key BROWN_MUSHROOM_BLOCK = Key.of("minecraft:brown_mushroom_block");
public static final Key RED_MUSHROOM_BLOCK = Key.of("minecraft:red_mushroom_block");
public static final Key MUSHROOM_STEM = Key.of("minecraft:mushroom_stem");
public static final Key OAK_LEAVES = Key.of("minecraft:oak_leaves");
public static final Key SPRUCE_LEAVES = Key.of("minecraft:spruce_leaves");
public static final Key BIRCH_LEAVES = Key.of("minecraft:birch_leaves");
public static final Key JUNGLE_LEAVES = Key.of("minecraft:jungle_leaves");
public static final Key ACACIA_LEAVES = Key.of("minecraft:acacia_leaves");
public static final Key DARK_OAK_LEAVES = Key.of("minecraft:dark_oak_leaves");
public static final Key MANGROVE_LEAVES = Key.of("minecraft:mangrove_leaves");
public static final Key CHERRY_LEAVES = Key.of("minecraft:cherry_leaves");
public static final Key PALE_OAK_LEAVES = Key.of("minecraft:pale_oak_leaves");
public static final Key AZALEA_LEAVES = Key.of("minecraft:azalea_leaves");
public static final Key FLOWERING_AZALEA_LEAVES = Key.of("minecraft:flowering_azalea_leaves");
public static final List<Key> WOODEN_TRAPDOORS = List.of(OAK_TRAPDOOR, SPRUCE_TRAPDOOR, BIRCH_TRAPDOOR,
ACACIA_TRAPDOOR, PALE_OAK_TRAPDOOR, DARK_OAK_TRAPDOOR, MANGROVE_TRAPDOOR, JUNGLE_TRAPDOOR);
public static final List<Key> CHERRY_TRAPDOORS = List.of(CHERRY_TRAPDOOR);

View File

@@ -18,7 +18,7 @@ public class BlockStateHolder {
this.propertyMap = new Reference2ObjectArrayMap<>(propertyMap);
}
public Holder<CustomBlock> owner() {
public Holder.Reference<CustomBlock> owner() {
return this.owner;
}

View File

@@ -9,4 +9,10 @@ public interface BlockStateWrapper {
int registryId();
Key ownerId();
<T> T getProperty(String propertyName);
boolean hasProperty(String propertyName);
String getAsString();
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.block.state;
package net.momirealms.craftengine.core.block;
import java.util.Collection;

View File

@@ -1,6 +1,6 @@
package net.momirealms.craftengine.core.block.properties;
import net.momirealms.craftengine.core.block.state.properties.*;
import net.momirealms.craftengine.core.block.properties.type.*;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Registries;
@@ -40,7 +40,6 @@ public final class Properties {
register(SLAB_TYPE, new EnumProperty.Factory<>(SlabType.class));
register(SOFA_SHAPE, new EnumProperty.Factory<>(SofaShape.class));
register(ANCHOR_TYPE, new EnumProperty.Factory<>(AnchorType.class));
}
public static void register(Key key, PropertyFactory factory) {

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.block.properties.type;
public enum AnchorType {
FLOOR,
WALL,
CEILING
}

View File

@@ -0,0 +1,5 @@
package net.momirealms.craftengine.core.block.properties.type;
public enum DoorHinge {
LEFT, RIGHT
}

View File

@@ -0,0 +1,5 @@
package net.momirealms.craftengine.core.block.properties.type;
public enum DoubleBlockHalf {
UPPER, LOWER
}

View File

@@ -0,0 +1,5 @@
package net.momirealms.craftengine.core.block.properties.type;
public enum SingleBlockHalf {
TOP, BOTTOM
}

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.block.properties.type;
public enum SlabType {
TOP,
BOTTOM,
DOUBLE
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.block.state.properties;
package net.momirealms.craftengine.core.block.properties.type;
public enum SofaShape {
STRAIGHT,

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.block.state.properties;
package net.momirealms.craftengine.core.block.properties.type;
public enum StairsShape {
STRAIGHT,

View File

@@ -1,7 +0,0 @@
package net.momirealms.craftengine.core.block.state.properties;
public enum AnchorType {
FLOOR,
WALL,
CEILING
}

View File

@@ -1,5 +0,0 @@
package net.momirealms.craftengine.core.block.state.properties;
public enum DoorHinge {
LEFT, RIGHT
}

View File

@@ -1,5 +0,0 @@
package net.momirealms.craftengine.core.block.state.properties;
public enum DoubleBlockHalf {
UPPER, LOWER
}

View File

@@ -1,5 +0,0 @@
package net.momirealms.craftengine.core.block.state.properties;
public enum SingleBlockHalf {
TOP, BOTTOM
}

View File

@@ -1,7 +0,0 @@
package net.momirealms.craftengine.core.block.state.properties;
public enum SlabType {
TOP,
BOTTOM,
DOUBLE
}

View File

@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.entity.player.Player;
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.cache.IdAllocator;
import net.momirealms.craftengine.core.pack.allocator.IdAllocator;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;

View File

@@ -14,7 +14,7 @@ import net.momirealms.craftengine.core.pack.AbstractPackManager;
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.cache.IdAllocator;
import net.momirealms.craftengine.core.pack.allocator.IdAllocator;
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;

View File

@@ -0,0 +1,33 @@
package net.momirealms.craftengine.core.pack.allocator;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class BlockStateAllocator {
private final List<BlockStateCandidate> blockStates = new ArrayList<>();
private int pointer = 0;
private int max = -1;
public void addCandidate(BlockStateCandidate state) {
this.blockStates.add(state);
this.max = this.blockStates.size() - 1;
}
@Nullable
public BlockStateCandidate findNext() {
while (this.pointer < this.max) {
final BlockStateCandidate state = this.blockStates.get(this.pointer);
if (!state.isUsed()) {
return state;
}
this.pointer++;
}
return null;
}
public void processPendingAllocations() {
}
}

View File

@@ -0,0 +1,24 @@
package net.momirealms.craftengine.core.pack.allocator;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
public class BlockStateCandidate {
private final BlockStateWrapper blockState;
private boolean used = false;
public BlockStateCandidate(BlockStateWrapper blockState) {
this.blockState = blockState;
}
public void setUsed() {
this.used = true;
}
public boolean isUsed() {
return used;
}
public BlockStateWrapper blockState() {
return blockState;
}
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.pack.cache;
package net.momirealms.craftengine.core.pack.allocator;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

View File

@@ -2,8 +2,8 @@ package net.momirealms.craftengine.core.plugin.context.condition;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.StatePropertyAccessor;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.StatePropertyAccessor;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;

View File

@@ -26,7 +26,7 @@ public class I18NData {
if (blockOptional.isPresent()) {
List<ImmutableBlockState> states = blockOptional.get().variantProvider().states();
if (states.size() == 1) {
return List.of("block." + stateToRealBlockId(states.get(0)));
return List.of("block." + stateToRealBlockId(states.getFirst()));
} else {
ArrayList<String> processed = new ArrayList<>();
for (ImmutableBlockState state : states) {

View File

@@ -2,16 +2,12 @@ package net.momirealms.craftengine.core.plugin.locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.block.AbstractBlockManager;
import net.momirealms.craftengine.core.font.FontManager;
import net.momirealms.craftengine.core.font.OffsetFont;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.Plugin;
import net.momirealms.craftengine.core.plugin.PluginProperties;
import net.momirealms.craftengine.core.plugin.config.*;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.plugin.text.minimessage.ImageTag;
import net.momirealms.craftengine.core.plugin.text.minimessage.IndexedArgumentTag;
import net.momirealms.craftengine.core.plugin.text.minimessage.ShiftTag;

View File

@@ -1,15 +1,11 @@
package net.momirealms.craftengine.core.util;
import com.google.gson.JsonElement;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentIteratorType;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.internal.parser.TokenParser;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.json.JSONOptions;

View File

@@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.util;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
public class CharacterUtils {

View File

@@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.world;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.state.StatePropertyAccessor;
import net.momirealms.craftengine.core.block.StatePropertyAccessor;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.NotNull;