LeavesFix.java

net.minecraft.util.datafix.fixes.LeavesFix

信息

  • 全限定名:net.minecraft.util.datafix.fixes.LeavesFix
  • 类型:public class
  • 包:net.minecraft.util.datafix.fixes
  • 源码路径:src/main/java/net/minecraft/util/datafix/fixes/LeavesFix.java
  • 起始行号:L35
  • 继承:DataFix
  • 职责:

    TODO

字段/常量

  • NORTH_WEST_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L36
    • 说明:

      TODO

  • WEST_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L37
    • 说明:

      TODO

  • SOUTH_WEST_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L38
    • 说明:

      TODO

  • SOUTH_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L39
    • 说明:

      TODO

  • SOUTH_EAST_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L40
    • 说明:

      TODO

  • EAST_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L41
    • 说明:

      TODO

  • NORTH_EAST_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L42
    • 说明:

      TODO

  • NORTH_MASK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L43
    • 说明:

      TODO

  • DIRECTIONS

    • 类型: int[][]
    • 修饰符: private static final
    • 源码定位: L44
    • 说明:

      TODO

  • DECAY_DISTANCE

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L45
    • 说明:

      TODO

  • SIZE_BITS

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L46
    • 说明:

      TODO

  • SIZE

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L47
    • 说明:

      TODO

  • LEAVES

    • 类型: Object2IntMap<String>
    • 修饰符: private static final
    • 源码定位: L48
    • 说明:

      TODO

  • LOGS

    • 类型: Set<String>
    • 修饰符: private static final
    • 源码定位: L56
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.util.datafix.fixes.LeavesFix.LeavesSection

    • 类型: class
    • 修饰符: public static final
    • 源码定位: L230
    • 说明:

      TODO

  • net.minecraft.util.datafix.fixes.LeavesFix.Section

    • 类型: class
    • 修饰符: public abstract static
    • 源码定位: L314
    • 说明:

      TODO

构造器

public LeavesFix(Schema outputSchema, boolean changesType) @ L77

  • 构造器名:LeavesFix
  • 源码定位:L77
  • 修饰符:public

参数:

  • outputSchema: Schema
  • changesType: boolean

说明:

TODO

方法

下面的方法块按源码顺序生成。

protected TypeRewriteRule makeRule() @ L81

  • 方法名:makeRule
  • 源码定位:L81
  • 返回类型:TypeRewriteRule
  • 修饰符:protected

参数:

说明:

TODO

public static int getIndex(int x, int y, int z) @ L187

  • 方法名:getIndex
  • 源码定位:L187
  • 返回类型:int
  • 修饰符:public static

参数:

  • x: int
  • y: int
  • z: int

说明:

TODO

private int getX(int index) @ L191

  • 方法名:getX
  • 源码定位:L191
  • 返回类型:int
  • 修饰符:private

参数:

  • index: int

说明:

TODO

private int getY(int index) @ L195

  • 方法名:getY
  • 源码定位:L195
  • 返回类型:int
  • 修饰符:private

参数:

  • index: int

说明:

TODO

private int getZ(int index) @ L199

  • 方法名:getZ
  • 源码定位:L199
  • 返回类型:int
  • 修饰符:private

参数:

  • index: int

说明:

TODO

public static int getSideMask(boolean west, boolean east, boolean north, boolean south) @ L203

  • 方法名:getSideMask
  • 源码定位:L203
  • 返回类型:int
  • 修饰符:public static

参数:

  • west: boolean
  • east: boolean
  • north: boolean
  • south: boolean

说明:

TODO

代码

public class LeavesFix extends DataFix {
    private static final int NORTH_WEST_MASK = 128;
    private static final int WEST_MASK = 64;
    private static final int SOUTH_WEST_MASK = 32;
    private static final int SOUTH_MASK = 16;
    private static final int SOUTH_EAST_MASK = 8;
    private static final int EAST_MASK = 4;
    private static final int NORTH_EAST_MASK = 2;
    private static final int NORTH_MASK = 1;
    private static final int[][] DIRECTIONS = new int[][]{{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}};
    private static final int DECAY_DISTANCE = 7;
    private static final int SIZE_BITS = 12;
    private static final int SIZE = 4096;
    private static final Object2IntMap<String> LEAVES = DataFixUtils.make(new Object2IntOpenHashMap<>(), map -> {
        map.put("minecraft:acacia_leaves", 0);
        map.put("minecraft:birch_leaves", 1);
        map.put("minecraft:dark_oak_leaves", 2);
        map.put("minecraft:jungle_leaves", 3);
        map.put("minecraft:oak_leaves", 4);
        map.put("minecraft:spruce_leaves", 5);
    });
    private static final Set<String> LOGS = ImmutableSet.of(
        "minecraft:acacia_bark",
        "minecraft:birch_bark",
        "minecraft:dark_oak_bark",
        "minecraft:jungle_bark",
        "minecraft:oak_bark",
        "minecraft:spruce_bark",
        "minecraft:acacia_log",
        "minecraft:birch_log",
        "minecraft:dark_oak_log",
        "minecraft:jungle_log",
        "minecraft:oak_log",
        "minecraft:spruce_log",
        "minecraft:stripped_acacia_log",
        "minecraft:stripped_birch_log",
        "minecraft:stripped_dark_oak_log",
        "minecraft:stripped_jungle_log",
        "minecraft:stripped_oak_log",
        "minecraft:stripped_spruce_log"
    );
 
    public LeavesFix(Schema outputSchema, boolean changesType) {
        super(outputSchema, changesType);
    }
 
    @Override
    protected TypeRewriteRule makeRule() {
        Type<?> chunkType = this.getInputSchema().getType(References.CHUNK);
        OpticFinder<?> levelFinder = chunkType.findField("Level");
        OpticFinder<?> sectionsFinder = levelFinder.type().findField("Sections");
        Type<?> sectionsType = sectionsFinder.type();
        if (!(sectionsType instanceof ListType)) {
            throw new IllegalStateException("Expecting sections to be a list.");
        } else {
            Type<?> sectionType = ((ListType)sectionsType).getElement();
            OpticFinder<?> sectionFinder = DSL.typeFinder(sectionType);
            return this.fixTypeEverywhereTyped(
                "Leaves fix",
                chunkType,
                chunk -> chunk.updateTyped(
                    levelFinder,
                    level -> {
                        int[] sides = new int[]{0};
                        Typed<?> newLevel = level.updateTyped(
                            sectionsFinder,
                            sections -> {
                                Int2ObjectMap<LeavesFix.LeavesSection> sectionMap = new Int2ObjectOpenHashMap<>(
                                    sections.getAllTyped(sectionFinder)
                                        .stream()
                                        .map(sectionx -> new LeavesFix.LeavesSection(sectionx, this.getInputSchema()))
                                        .collect(Collectors.toMap(LeavesFix.Section::getIndex, s -> (LeavesFix.LeavesSection)s))
                                );
                                if (sectionMap.values().stream().allMatch(LeavesFix.Section::isSkippable)) {
                                    return sections;
                                } else {
                                    List<IntSet> queue = Lists.newArrayList();
 
                                    for (int i = 0; i < 7; i++) {
                                        queue.add(new IntOpenHashSet());
                                    }
 
                                    for (LeavesFix.LeavesSection section : sectionMap.values()) {
                                        if (!section.isSkippable()) {
                                            for (int i = 0; i < 4096; i++) {
                                                int block = section.getBlock(i);
                                                if (section.isLog(block)) {
                                                    queue.get(0).add(section.getIndex() << 12 | i);
                                                } else if (section.isLeaf(block)) {
                                                    int x = this.getX(i);
                                                    int z = this.getZ(i);
                                                    sides[0] |= getSideMask(x == 0, x == 15, z == 0, z == 15);
                                                }
                                            }
                                        }
                                    }
 
                                    for (int ix = 1; ix < 7; ix++) {
                                        IntSet set = queue.get(ix - 1);
                                        IntSet newSet = queue.get(ix);
                                        IntIterator iterator = set.iterator();
 
                                        while (iterator.hasNext()) {
                                            int posChunk = iterator.nextInt();
                                            int x = this.getX(posChunk);
                                            int y = this.getY(posChunk);
                                            int z = this.getZ(posChunk);
 
                                            for (int[] direction : DIRECTIONS) {
                                                int nx = x + direction[0];
                                                int nyChunk = y + direction[1];
                                                int nz = z + direction[2];
                                                if (nx >= 0 && nx <= 15 && nz >= 0 && nz <= 15 && nyChunk >= 0 && nyChunk <= 255) {
                                                    LeavesFix.LeavesSection sectionx = sectionMap.get(nyChunk >> 4);
                                                    if (sectionx != null && !sectionx.isSkippable()) {
                                                        int posSection = getIndex(nx, nyChunk & 15, nz);
                                                        int block = sectionx.getBlock(posSection);
                                                        if (sectionx.isLeaf(block)) {
                                                            int oldDistance = sectionx.getDistance(block);
                                                            if (oldDistance > ix) {
                                                                sectionx.setDistance(posSection, block, ix);
                                                                newSet.add(getIndex(nx, nyChunk, nz));
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
 
                                    return sections.updateTyped(
                                        sectionFinder, sectionx -> sectionMap.get(sectionx.get(DSL.remainderFinder()).get("Y").asInt(0)).write(sectionx)
                                    );
                                }
                            }
                        );
                        if (sides[0] != 0) {
                            newLevel = newLevel.update(DSL.remainderFinder(), tag -> {
                                Dynamic<?> upgradeData = DataFixUtils.orElse(tag.get("UpgradeData").result(), tag.emptyMap());
                                return tag.set(
                                    "UpgradeData", upgradeData.set("Sides", tag.createByte((byte)(upgradeData.get("Sides").asByte((byte)0) | sides[0])))
                                );
                            });
                        }
 
                        return newLevel;
                    }
                )
            );
        }
    }
 
    public static int getIndex(int x, int y, int z) {
        return y << 8 | z << 4 | x;
    }
 
    private int getX(int index) {
        return index & 15;
    }
 
    private int getY(int index) {
        return index >> 8 & 0xFF;
    }
 
    private int getZ(int index) {
        return index >> 4 & 15;
    }
 
    public static int getSideMask(boolean west, boolean east, boolean north, boolean south) {
        int s = 0;
        if (north) {
            if (east) {
                s |= 2;
            } else if (west) {
                s |= 128;
            } else {
                s |= 1;
            }
        } else if (south) {
            if (west) {
                s |= 32;
            } else if (east) {
                s |= 8;
            } else {
                s |= 16;
            }
        } else if (east) {
            s |= 4;
        } else if (west) {
            s |= 64;
        }
 
        return s;
    }
 
    public static final class LeavesSection extends LeavesFix.Section {
        private static final String PERSISTENT = "persistent";
        private static final String DECAYABLE = "decayable";
        private static final String DISTANCE = "distance";
        private @Nullable IntSet leaveIds;
        private @Nullable IntSet logIds;
        private @Nullable Int2IntMap stateToIdMap;
 
        public LeavesSection(Typed<?> section, Schema inputSchema) {
            super(section, inputSchema);
        }
 
        @Override
        protected boolean skippable() {
            this.leaveIds = new IntOpenHashSet();
            this.logIds = new IntOpenHashSet();
            this.stateToIdMap = new Int2IntOpenHashMap();
 
            for (int i = 0; i < this.palette.size(); i++) {
                Dynamic<?> paletteTag = this.palette.get(i);
                String blockName = paletteTag.get("Name").asString("");
                if (LeavesFix.LEAVES.containsKey(blockName)) {
                    boolean persistent = Objects.equals(paletteTag.get("Properties").get("decayable").asString(""), "false");
                    this.leaveIds.add(i);
                    this.stateToIdMap.put(this.getStateId(blockName, persistent, 7), i);
                    this.palette.set(i, this.makeLeafTag(paletteTag, blockName, persistent, 7));
                }
 
                if (LeavesFix.LOGS.contains(blockName)) {
                    this.logIds.add(i);
                }
            }
 
            return this.leaveIds.isEmpty() && this.logIds.isEmpty();
        }
 
        private Dynamic<?> makeLeafTag(Dynamic<?> input, String blockName, boolean persistent, int distance) {
            Dynamic<?> properties = input.emptyMap();
            properties = properties.set("persistent", properties.createString(persistent ? "true" : "false"));
            properties = properties.set("distance", properties.createString(Integer.toString(distance)));
            Dynamic<?> tag = input.emptyMap();
            tag = tag.set("Properties", properties);
            return tag.set("Name", tag.createString(blockName));
        }
 
        public boolean isLog(int block) {
            return this.logIds.contains(block);
        }
 
        public boolean isLeaf(int block) {
            return this.leaveIds.contains(block);
        }
 
        private int getDistance(int block) {
            return this.isLog(block) ? 0 : Integer.parseInt(this.palette.get(block).get("Properties").get("distance").asString(""));
        }
 
        private void setDistance(int pos, int block, int distance) {
            Dynamic<?> baseTag = this.palette.get(block);
            String blockName = baseTag.get("Name").asString("");
            boolean persistent = Objects.equals(baseTag.get("Properties").get("persistent").asString(""), "true");
            int stateId = this.getStateId(blockName, persistent, distance);
            if (!this.stateToIdMap.containsKey(stateId)) {
                int id = this.palette.size();
                this.leaveIds.add(id);
                this.stateToIdMap.put(stateId, id);
                this.palette.add(this.makeLeafTag(baseTag, blockName, persistent, distance));
            }
 
            int id = this.stateToIdMap.get(stateId);
            if (1 << this.storage.getBits() <= id) {
                PackedBitStorage newStorage = new PackedBitStorage(this.storage.getBits() + 1, 4096);
 
                for (int i = 0; i < 4096; i++) {
                    newStorage.set(i, this.storage.get(i));
                }
 
                this.storage = newStorage;
            }
 
            this.storage.set(pos, id);
        }
    }
 
    public abstract static class Section {
        protected static final String BLOCK_STATES_TAG = "BlockStates";
        protected static final String NAME_TAG = "Name";
        protected static final String PROPERTIES_TAG = "Properties";
        private final Type<Pair<String, Dynamic<?>>> blockStateType = DSL.named(References.BLOCK_STATE.typeName(), DSL.remainderType());
        protected final OpticFinder<List<Pair<String, Dynamic<?>>>> paletteFinder = DSL.fieldFinder("Palette", DSL.list(this.blockStateType));
        protected final List<Dynamic<?>> palette;
        protected final int index;
        protected @Nullable PackedBitStorage storage;
 
        public Section(Typed<?> section, Schema inputSchema) {
            if (!Objects.equals(inputSchema.getType(References.BLOCK_STATE), this.blockStateType)) {
                throw new IllegalStateException("Block state type is not what was expected.");
            } else {
                Optional<List<Pair<String, Dynamic<?>>>> typedPalette = section.getOptional(this.paletteFinder);
                this.palette = typedPalette.<List>map(p -> p.stream().map(Pair::getSecond).collect(Collectors.toList())).orElse(ImmutableList.of());
                Dynamic<?> tag = section.get(DSL.remainderFinder());
                this.index = tag.get("Y").asInt(0);
                this.readStorage(tag);
            }
        }
 
        protected void readStorage(Dynamic<?> tag) {
            if (this.skippable()) {
                this.storage = null;
            } else {
                long[] states = tag.get("BlockStates").asLongStream().toArray();
                int size = Math.max(4, DataFixUtils.ceillog2(this.palette.size()));
                this.storage = new PackedBitStorage(size, 4096, states);
            }
        }
 
        public Typed<?> write(Typed<?> section) {
            return this.isSkippable()
                ? section
                : section.update(DSL.remainderFinder(), tag -> tag.set("BlockStates", tag.createLongList(Arrays.stream(this.storage.getRaw()))))
                    .set(this.paletteFinder, this.palette.stream().<Pair<String,Dynamic<?>>>map(b -> Pair.of(References.BLOCK_STATE.typeName(), b)).collect(Collectors.toList()));
        }
 
        public boolean isSkippable() {
            return this.storage == null;
        }
 
        public int getBlock(int pos) {
            return this.storage.get(pos);
        }
 
        protected int getStateId(String blockName, boolean persistent, int distance) {
            return LeavesFix.LEAVES.get(blockName) << 5 | (persistent ? 16 : 0) | distance;
        }
 
        int getIndex() {
            return this.index;
        }
 
        protected abstract boolean skippable();
    }
}

引用的其他类