From d414a274981630e78fd31377fb526adb15968ebd Mon Sep 17 00:00:00 2001 From: TomTheFurry Date: Sat, 30 Jul 2022 16:06:55 +0800 Subject: [PATCH] Something renders! And introduce mem leaks, missing texture color, and inverted lights! What a great set of features! --- .../wrappers/block/BlockDetailWrapper.java | 327 ------------------ .../wrappers/block/BlockStateWrapper.java | 2 +- .../block/cache/ClientBlockDetailMap.java | 25 ++ .../block/cache/ClientBlockStateCache.java | 197 +++++++++++ .../ServerBlockDetailMap.java} | 33 +- .../block/cache/ServerBlockStateCache.java | 67 ++++ .../common/wrappers/chunk/ChunkWrapper.java | 43 +-- .../wrappers/world/ClientLevelWrapper.java | 14 +- .../wrappers/world/ServerLevelWrapper.java | 15 +- .../BatchGenerationEnvironment.java | 2 +- core | 2 +- .../com/seibel/lod/FabricClientProxy.java | 24 +- .../com/seibel/lod/FabricServerProxy.java | 2 +- .../lod/mixins/client/MixinClientLevel.java | 2 +- .../lod/mixins/server/MixinChunkMap.java | 2 +- 15 files changed, 358 insertions(+), 399 deletions(-) delete mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/block/BlockDetailWrapper.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockDetailMap.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockStateCache.java rename common/src/main/java/com/seibel/lod/common/wrappers/block/{BlockDetailMap.java => cache/ServerBlockDetailMap.java} (52%) create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ServerBlockStateCache.java diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockDetailWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockDetailWrapper.java deleted file mode 100644 index a04184730..000000000 --- a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockDetailWrapper.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * This file is part of the Distant Horizons mod (formerly the LOD Mod), - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2022 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.common.wrappers.block; - -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -import com.seibel.lod.common.wrappers.McObjectConverter; -import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; -import com.seibel.lod.core.config.Config; -import com.seibel.lod.core.enums.ELodDirection; -import com.seibel.lod.core.objects.DHBlockPos; -import com.seibel.lod.core.util.ColorUtil; -import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper; -import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.*; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.FlowerBlock; -import net.minecraft.world.level.block.LeavesBlock; -import net.minecraft.world.level.block.RenderShape; -import net.minecraft.world.level.block.RotatedPillarBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.shapes.VoxelShape; -#if POST_MC_1_19 -import net.minecraft.util.RandomSource; -#endif - -/*-- WARN: This class should NEVER hold reference to anything large, - as this is never dealloc until the end of runtime!! --*/ -public class BlockDetailWrapper extends IBlockDetailWrapper -{ - public static final int FLOWER_COLOR_SCALE = 5; - - #if PRE_MC_1_19 - public static final Random random = new Random(0); - #else - public static final RandomSource random = RandomSource.create(); - #endif - - enum ColorMode { - Default, - Flower, - Leaves; - static ColorMode getColorMode(Block b) { - if (b instanceof LeavesBlock) return Leaves; - if (b instanceof FlowerBlock) return Flower; - return Default; - } - } - //TODO: Perhaps make this not just use the first frame? - private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) { - - int count = 0; - double alpha = 0; - double red = 0; - double green = 0; - double blue = 0; - int tempColor; - - { - // textures normally use u and v instead of x and y - for (int u = 0; u < texture.getWidth(); u++) - { - for (int v = 0; v < texture.getHeight(); v++) - { - //note: Minecraft color format is: 0xAA BB GG RR - //________ DH mod color format is: 0xAA RR GG BB - //OpenGL RGBA format native order: 0xRR GG BB AA - //_ OpenGL RGBA format Java Order: 0xAA BB GG RR - tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v); - - double r = ((tempColor & 0x000000FF) )/255.; - double g = ((tempColor & 0x0000FF00) >>> 8)/255.; - double b = ((tempColor & 0x00FF0000) >>> 16)/255.; - double a = ((tempColor & 0xFF000000) >>> 24)/255.; - int scale = 1; - - if (colorMode == ColorMode.Leaves) { - r *= a; - g *= a; - b *= a; - a = 1.; - } else if (a==0.) { - continue; - } else if (colorMode == ColorMode.Flower && (g+0.1 quads = null; - for (Direction direction : DIRECTION_ORDER) - { - quads = Minecraft.getInstance().getModelManager().getBlockModelShaper(). - getBlockModel(state).getQuads(state, direction, random); - if (!quads.isEmpty() && - !(state.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP)) - break; - }; - if (quads == null || quads.isEmpty()) { - quads = Minecraft.getInstance().getModelManager().getBlockModelShaper(). - getBlockModel(state).getQuads(state, null, random); - } - if (quads != null && !quads.isEmpty()) { - needPostTinting = quads.get(0).isTinted(); - needShade = quads.get(0).isShade(); - tintIndex = quads.get(0).getTintIndex(); - baseColor = calculateColorFromTexture( - #if PRE_MC_1_17_1 quads.get(0).sprite, - #else quads.get(0).getSprite(), #endif - ColorMode.getColorMode(state.getBlock())); - } else { // Backup method. - needPostTinting = false; - needShade = false; - tintIndex = 0; - baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state), - ColorMode.getColorMode(state.getBlock())); - } - } else { // Liquid Block - needPostTinting = true; - needShade = false; - tintIndex = 0; - baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state), - ColorMode.getColorMode(state.getBlock())); - } - isColorResolved = true; - } - - private BlockAndTintGetter wrapColorResolver(LevelReader level) { - int blendDistance = Config.Client.Graphics.Quality.lodBiomeBlending.get(); - if (blendDistance == 0) { - return new TintGetterOverrideFast(level); - } else { - return new TintGetterOverrideSmooth(level, blendDistance); - } - } - - @Override - public int getAndResolveFaceColor(ELodDirection dir, IChunkWrapper chunk, DHBlockPos blockPos) - { - // FIXME: impl per-face colors - resolveColors(); - if (!needPostTinting) return baseColor; - int tintColor = Minecraft.getInstance().getBlockColors() - .getColor(state, wrapColorResolver(((ChunkWrapper)chunk).getColorResolver()), - McObjectConverter.Convert(blockPos), tintIndex); - if (tintColor == -1) return baseColor; - return ColorUtil.multiplyARGBwithRGB(baseColor, tintColor); - } - - @Override - public boolean hasFaceCullingFor(ELodDirection dir) - { - //resolveShapes(); - return !dontOccludeFaces[dir.ordinal()]; - } - - @Override - public boolean hasNoCollision() - { - //resolveShapes(); - return noCollision; - } - - @Override - public boolean noFaceIsFullFace() - { - //resolveShapes(); - return noFullFace; - } - - @Override - public String serialize() - { - // FIXME: Impl this for the blockState Storage stuff - return null; - } - - @Override - protected boolean isSame(IBlockDetailWrapper iBlockDetail) - { - return ((BlockDetailWrapper)iBlockDetail).state.getBlock().equals(state.getBlock()); - } - - public String toString() { - return "BlockDetail{" + state + "}"; - } -} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java index 0ce0da9e9..11cd03528 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java @@ -22,7 +22,7 @@ public class BlockStateWrapper implements IBlockStateWrapper { } public final BlockState blockState; - private BlockStateWrapper(BlockState blockState) { + BlockStateWrapper(BlockState blockState) { this.blockState = blockState; } diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockDetailMap.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockDetailMap.java new file mode 100644 index 000000000..5e6f0fa83 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockDetailMap.java @@ -0,0 +1,25 @@ +package com.seibel.lod.common.wrappers.block.cache; + +import com.seibel.lod.common.wrappers.block.BiomeWrapper; +import com.seibel.lod.common.wrappers.world.ClientLevelWrapper; +import com.seibel.lod.core.objects.DHBlockPos; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.concurrent.ConcurrentHashMap; + +public class ClientBlockDetailMap { + private final ConcurrentHashMap blockCache = new ConcurrentHashMap<>(); + //private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder #endif, Biome> biomeMap = new ConcurrentHashMap<>(); + private final ClientLevelWrapper level; + public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; } + + public ClientBlockStateCache getBlockStateData(BlockState state, DHBlockPos pos) { //TODO: Allow a per pos unique setting + return blockCache.computeIfAbsent(state, (s) -> new ClientBlockStateCache(s, level, new DHBlockPos(0,0,0))); + } + + public void clear() { blockCache.clear(); } + + public int getColor(BlockState state, BiomeWrapper biome, DHBlockPos pos) { + return getBlockStateData(state, pos).getAndResolveFaceColor(biome); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockStateCache.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockStateCache.java new file mode 100644 index 000000000..df398faff --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ClientBlockStateCache.java @@ -0,0 +1,197 @@ +package com.seibel.lod.common.wrappers.block.cache; + +import com.seibel.lod.common.wrappers.McObjectConverter; +import com.seibel.lod.common.wrappers.block.*; +import com.seibel.lod.core.config.Config; +import com.seibel.lod.core.objects.DHBlockPos; +import com.seibel.lod.core.util.ColorUtil; +import com.seibel.lod.core.wrapperInterfaces.world.IClientLevelWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FlowerBlock; +import net.minecraft.world.level.block.LeavesBlock; +import net.minecraft.world.level.block.RotatedPillarBlock; +#if POST_MC_1_19 +import net.minecraft.util.RandomSource; +#endif +import net.minecraft.world.level.block.state.BlockState; + +import java.util.List; +import java.util.Random; + +public class ClientBlockStateCache { + + #if PRE_MC_1_19 + public static final Random random = new Random(0); + #else + public static final RandomSource random = RandomSource.create(); + #endif + + public final BlockState state; + public final LevelReader level; + public final BlockPos pos; + public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DHBlockPos samplingPos) { + state = blockState; + level = (LevelReader) samplingLevel.unwrapLevel(); + pos = McObjectConverter.Convert(samplingPos); + resolveColors(); + } + + boolean isColorResolved = false; + int baseColor = 0; //TODO: Impl per-face color + boolean needShade = true; + boolean needPostTinting = false; + int tintIndex = 0; + + + public static final int FLOWER_COLOR_SCALE = 5; + + enum ColorMode { + Default, + Flower, + Leaves; + static ColorMode getColorMode(Block b) { + if (b instanceof LeavesBlock) return Leaves; + if (b instanceof FlowerBlock) return Flower; + return Default; + } + } + //TODO: Perhaps make this not just use the first frame? + private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) { + int count = 0; + double alpha = 0; + double red = 0; + double green = 0; + double blue = 0; + int tempColor; + { + // textures normally use u and v instead of x and y + for (int u = 0; u < texture.getWidth(); u++) + { + for (int v = 0; v < texture.getHeight(); v++) + { + //note: Minecraft color format is: 0xAA BB GG RR + //________ DH mod color format is: 0xAA RR GG BB + //OpenGL RGBA format native order: 0xRR GG BB AA + //_ OpenGL RGBA format Java Order: 0xAA BB GG RR + tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v); + + double r = ((tempColor & 0x000000FF) )/255.; + double g = ((tempColor & 0x0000FF00) >>> 8)/255.; + double b = ((tempColor & 0x00FF0000) >>> 16)/255.; + double a = ((tempColor & 0xFF000000) >>> 24)/255.; + int scale = 1; + + if (colorMode == ColorMode.Leaves) { + r *= a; + g *= a; + b *= a; + a = 1.; + } else if (a==0.) { + continue; + } else if (colorMode == ColorMode.Flower && (g+0.1 quads = null; + for (Direction direction : DIRECTION_ORDER) + { + quads = Minecraft.getInstance().getModelManager().getBlockModelShaper(). + getBlockModel(state).getQuads(state, direction, random); + if (!quads.isEmpty() && + !(state.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP)) + break; + }; + if (quads == null || quads.isEmpty()) { + quads = Minecraft.getInstance().getModelManager().getBlockModelShaper(). + getBlockModel(state).getQuads(state, null, random); + } + if (quads != null && !quads.isEmpty()) { + needPostTinting = quads.get(0).isTinted(); + needShade = quads.get(0).isShade(); + tintIndex = quads.get(0).getTintIndex(); + baseColor = calculateColorFromTexture( + #if PRE_MC_1_17_1 quads.get(0).sprite, + #else quads.get(0).getSprite(), #endif + ColorMode.getColorMode(state.getBlock())); + } else { // Backup method. + needPostTinting = false; + needShade = false; + tintIndex = 0; + baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state), + ColorMode.getColorMode(state.getBlock())); + } + } else { // Liquid Block + needPostTinting = true; + needShade = false; + tintIndex = 0; + baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state), + ColorMode.getColorMode(state.getBlock())); + } + isColorResolved = true; + } + + + + private BlockAndTintGetter wrapColorResolver(LevelReader level) { + int blendDistance = Config.Client.Graphics.Quality.lodBiomeBlending.get(); + if (blendDistance == 0) { + return new TintGetterOverrideFast(level); + } else { + return new TintGetterOverrideSmooth(level, blendDistance); + } + } + + public int getAndResolveFaceColor(BiomeWrapper biome) + { + // FIXME: impl per-face colors + if (!needPostTinting) return baseColor; + int tintColor = Minecraft.getInstance().getBlockColors() + .getColor(state, wrapColorResolver(level), pos, tintIndex); //FIXME: Use biome? Hack the ColorResolver? + if (tintColor == -1) return baseColor; + return ColorUtil.multiplyARGBwithRGB(baseColor, tintColor); + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockDetailMap.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ServerBlockDetailMap.java similarity index 52% rename from common/src/main/java/com/seibel/lod/common/wrappers/block/BlockDetailMap.java rename to common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ServerBlockDetailMap.java index 326ae281b..2848f2f8c 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockDetailMap.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ServerBlockDetailMap.java @@ -17,30 +17,25 @@ * along with this program. If not, see . */ -package com.seibel.lod.common.wrappers.block; +package com.seibel.lod.common.wrappers.block.cache; import java.util.concurrent.ConcurrentHashMap; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.LevelReader; +import com.seibel.lod.common.wrappers.world.ServerLevelWrapper; +import com.seibel.lod.core.objects.DHBlockPos; import net.minecraft.world.level.block.state.BlockState; -public class BlockDetailMap + +public class ServerBlockDetailMap { - - private static ConcurrentHashMap map = new ConcurrentHashMap(); - - private BlockDetailMap() {} - - public static BlockDetailWrapper getOrMakeBlockDetailCache(BlockState bs, BlockPos pos, LevelReader getter) { - if (!bs.getFluidState().isEmpty()) { - bs = bs.getFluidState().createLegacyBlock(); - } - BlockDetailWrapper cache = map.get(bs); - if (cache != null) return cache; - cache = BlockDetailWrapper.make(bs, pos, getter); - //ApiShared.LOGGER.info("New blockDetail cache for {} to {} ", bs, cache); - BlockDetailWrapper cacheCAS = map.putIfAbsent(bs, cache); - return cacheCAS==null ? cache : cacheCAS; + private final ConcurrentHashMap blockCache = new ConcurrentHashMap<>(); + //private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder #endif, Biome> biomeMap = new ConcurrentHashMap<>(); + private final ServerLevelWrapper level; + public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; } + + public ServerBlockStateCache getBlockStateData(BlockState state, DHBlockPos pos) { //TODO: Allow a per pos unique setting + return blockCache.computeIfAbsent(state, (s) -> new ServerBlockStateCache(s, level, new DHBlockPos(0,0,0))); } + + public void clear() { blockCache.clear(); } } diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ServerBlockStateCache.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ServerBlockStateCache.java new file mode 100644 index 000000000..cff1905f7 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/cache/ServerBlockStateCache.java @@ -0,0 +1,67 @@ +package com.seibel.lod.common.wrappers.block.cache; + +import com.seibel.lod.common.wrappers.McObjectConverter; +import com.seibel.lod.core.objects.DHBlockPos; +import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.shapes.VoxelShape; + +import java.util.Arrays; + +public class ServerBlockStateCache { + public final BlockState state; + public final LevelReader level; + public final BlockPos pos; + + public ServerBlockStateCache(BlockState blockState, ILevelWrapper samplingLevel, DHBlockPos samplingPos) { + state = blockState; + level = (LevelReader) samplingLevel.unwrapLevel(); + pos = McObjectConverter.Convert(samplingPos); + resolveShapes(); + } + + boolean noCollision = false; + boolean[] occludeFaces = null; + boolean[] fullFaces = null; + boolean isShapeResolved = false; + public void resolveShapes() { + if (isShapeResolved) return; + if (state.getFluidState().isEmpty()) { + noCollision = state.getCollisionShape(level, pos).isEmpty(); + occludeFaces = new boolean[6]; + if (state.canOcclude()) { + for (Direction dir : Direction.values()) { + // Note: isEmpty() isn't quite correct... best would be a isFull() or something... + occludeFaces[McObjectConverter.Convert(dir).ordinal()] + = !state.getFaceOcclusionShape(level, pos, dir).isEmpty(); + } + } + + VoxelShape voxelShape = state.getShape(level, pos); + fullFaces = new boolean[6]; + if (!voxelShape.isEmpty()) { + for (Direction dir : Direction.values()) { + VoxelShape faceShape = voxelShape.getFaceShape(dir); + AABB aabb = faceShape.bounds(); + boolean xFull = aabb.minX <= 0.01 && aabb.maxX >= 0.99; + boolean yFull = aabb.minY <= 0.01 && aabb.maxY >= 0.99; + boolean zFull = aabb.minZ <= 0.01 && aabb.maxZ >= 0.99; + fullFaces[McObjectConverter.Convert(dir).ordinal()] = + (xFull || dir.getAxis().equals(Direction.Axis.X)) + && (yFull || dir.getAxis().equals(Direction.Axis.Y)) + && (zFull || dir.getAxis().equals(Direction.Axis.Z)); + } + } + } else { // Liquid Block. Treat as full block + occludeFaces = new boolean[6]; + Arrays.fill(occludeFaces, true); + fullFaces = new boolean[6]; + Arrays.fill(fullFaces, true); + } + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java index ce16b0976..0614f6be1 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java @@ -19,20 +19,22 @@ package com.seibel.lod.common.wrappers.chunk; -import com.seibel.lod.common.wrappers.block.BlockDetailWrapper; import com.seibel.lod.common.wrappers.block.BlockStateWrapper; import com.seibel.lod.core.enums.ELodDirection; +import com.seibel.lod.core.objects.DHBlockPos; import com.seibel.lod.core.util.LevelPosUtil; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.lod.common.wrappers.WrapperUtil; -import com.seibel.lod.common.wrappers.block.BlockDetailMap; +import com.seibel.lod.common.wrappers.block.cache.ServerBlockDetailMap; import com.seibel.lod.common.wrappers.block.BiomeWrapper; import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightedWorldGenRegion; +import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper; import net.minecraft.core.BlockPos; #if POST_MC_1_17_1 import net.minecraft.core.QuartPos; @@ -48,6 +50,8 @@ import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.levelgen.Heightmap; +import javax.annotation.Nullable; + /** * * @author James Seibel @@ -57,12 +61,14 @@ public class ChunkWrapper implements IChunkWrapper { private final ChunkAccess chunk; private final LevelReader lightSource; + private final ILevelWrapper wrappedLevel; - public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource) + public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, @Nullable ILevelWrapper wrappedLevel) { this.chunk = chunk; this.lightSource = lightSource; + this.wrappedLevel = wrappedLevel; } @Override @@ -98,6 +104,8 @@ public class ChunkWrapper implements IChunkWrapper @Override public IBiomeWrapper getBiome(int x, int y, int z) { + if (wrappedLevel != null) return wrappedLevel.getBiome(new DHBlockPos(x + getMinX(), y, z + getMinZ())); + #if PRE_MC_1_17_1 return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome( x >> 2, y >> 2, z >> 2)); @@ -113,32 +121,6 @@ public class ChunkWrapper implements IChunkWrapper #endif } - @Override - @Deprecated - public IBlockDetailWrapper getBlockDetail(int x, int y, int z) { - BlockPos pos = new BlockPos(x,y,z); - BlockState blockState = chunk.getBlockState(pos); - IBlockDetailWrapper blockDetail = BlockDetailMap.getOrMakeBlockDetailCache(blockState, pos, lightSource); - return blockDetail == BlockDetailWrapper.NULL_BLOCK_DETAIL ? null : blockDetail; - } - - @Override - @Deprecated - public IBlockDetailWrapper getBlockDetailAtFace(int x, int y, int z, ELodDirection dir) { - int fy = y+dir.getNormal().y; - if (fy < getMinBuildHeight() || fy > getMaxBuildHeight()) return null; - BlockPos pos = new BlockPos(x+dir.getNormal().x,fy,z+dir.getNormal().z); - BlockState blockState; - if (blockPosInsideChunk(x,y,z)) - blockState = chunk.getBlockState(pos); - else { - blockState = lightSource.getBlockState(pos); - } - if (blockState == null || blockState.isAir()) return null; - IBlockDetailWrapper blockDetail = BlockDetailMap.getOrMakeBlockDetailCache(blockState, pos, lightSource); - return blockDetail == BlockDetailWrapper.NULL_BLOCK_DETAIL ? null : blockDetail; - } - public ChunkAccess getChunk() { return chunk; } @@ -252,7 +234,8 @@ public class ChunkWrapper implements IChunkWrapper } @Override - public BlockStateWrapper getBlockState(int x, int y, int z) { + public IBlockStateWrapper getBlockState(int x, int y, int z) { + if (wrappedLevel != null) return wrappedLevel.getBlockState(new DHBlockPos(x + getMinX(), y, z + getMinZ())); return BlockStateWrapper.fromBlockState(chunk.getBlockState(new BlockPos(x,y,z))); } } diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/world/ClientLevelWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/ClientLevelWrapper.java index fc4af4466..12fb43164 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/world/ClientLevelWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/world/ClientLevelWrapper.java @@ -1,5 +1,9 @@ package com.seibel.lod.common.wrappers.world; +import com.seibel.lod.common.wrappers.McObjectConverter; +import com.seibel.lod.common.wrappers.block.BiomeWrapper; +import com.seibel.lod.common.wrappers.block.BlockStateWrapper; +import com.seibel.lod.common.wrappers.block.cache.ClientBlockDetailMap; import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper; import com.seibel.lod.core.api.internal.a7.ClientApi; @@ -40,6 +44,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper this.level = level; } final ClientLevel level; + ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this); @Nullable @Override public IServerLevelWrapper tryGetServerSideWrapper() { @@ -60,7 +65,8 @@ public class ClientLevelWrapper implements IClientLevelWrapper @Override public int computeBaseColor(DHBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState) { - return 0; + return blockMap.getColor(((BlockStateWrapper)blockState).blockState, + (BiomeWrapper)biome, pos); } @Override @@ -115,7 +121,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper public IChunkWrapper tryGetChunk(DHChunkPos pos) { ChunkAccess chunk = level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false); if (chunk == null) return null; - return new ChunkWrapper(chunk, level); + return new ChunkWrapper(chunk, level, this); } @Override @@ -126,12 +132,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper @Override public IBlockStateWrapper getBlockState(DHBlockPos pos) { - return null; + return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos))); } @Override public IBiomeWrapper getBiome(DHBlockPos pos) { - return null; + return BiomeWrapper.getBiomeWrapper(level.getBiome(McObjectConverter.Convert(pos))); } @Override diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/world/ServerLevelWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/ServerLevelWrapper.java index 01fe2a8a7..627449e80 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/world/ServerLevelWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/world/ServerLevelWrapper.java @@ -22,7 +22,12 @@ package com.seibel.lod.common.wrappers.world; import java.io.File; import java.util.concurrent.ConcurrentHashMap; +import com.seibel.lod.common.wrappers.McObjectConverter; +import com.seibel.lod.common.wrappers.block.BiomeWrapper; +import com.seibel.lod.common.wrappers.block.BlockStateWrapper; +import com.seibel.lod.common.wrappers.block.cache.ServerBlockDetailMap; import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper; +import com.seibel.lod.core.a7.world.WorldEnvironment; import com.seibel.lod.core.api.internal.a7.ServerApi; import com.seibel.lod.core.objects.DHBlockPos; import com.seibel.lod.core.objects.DHChunkPos; @@ -67,11 +72,13 @@ public class ServerLevelWrapper implements IServerLevelWrapper } } + final ServerLevel level; + ServerBlockDetailMap blockMap = new ServerBlockDetailMap(this); + public ServerLevelWrapper(ServerLevel level) { this.level = level; } - final ServerLevel level; @Nullable @Override public IClientLevelWrapper tryGetClientSideWrapper() { @@ -144,7 +151,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper public IChunkWrapper tryGetChunk(DHChunkPos pos) { ChunkAccess chunk = level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false); if (chunk == null) return null; - return new ChunkWrapper(chunk, level); + return new ChunkWrapper(chunk, level, this); } @Override @@ -156,12 +163,12 @@ public class ServerLevelWrapper implements IServerLevelWrapper @Override public IBlockStateWrapper getBlockState(DHBlockPos pos) { - return null; + return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos))); } @Override public IBiomeWrapper getBiome(DHBlockPos pos) { - return null; + return BiomeWrapper.getBiomeWrapper(level.getBiome(McObjectConverter.Convert(pos))); } @Override diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java index bf5b96655..72895e454 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java @@ -454,7 +454,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv { for (int ox = 0; ox < e.size; ox++) { - result.set(ox, oy, new ChunkWrapper(genChunks.get(ox, oy), region)); + result.set(ox, oy, new ChunkWrapper(genChunks.get(ox, oy), region, null)); } } e.timer.complete(); diff --git a/core b/core index 823da7689..6fe2b6f33 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 823da76896f7fc9d8cd24ef009ab3ca37e936b18 +Subproject commit 6fe2b6f331d7a7d9d445826ecfdbd5716907c3b2 diff --git a/fabric/src/main/java/com/seibel/lod/FabricClientProxy.java b/fabric/src/main/java/com/seibel/lod/FabricClientProxy.java index 7d4c5a1e5..367e525d4 100644 --- a/fabric/src/main/java/com/seibel/lod/FabricClientProxy.java +++ b/fabric/src/main/java/com/seibel/lod/FabricClientProxy.java @@ -77,17 +77,23 @@ public class FabricClientProxy // TODO: Is using setClientLightReady one still better? //#if PRE_MC_1_18_1 // in 1.18+, we use mixin hook in setClientLightReady(true) ClientChunkEvents.CHUNK_LOAD.register((level, chunk) -> - ClientApi.INSTANCE.clientChunkLoadEvent( - new ChunkWrapper(chunk, level), - ClientLevelWrapper.getWrapper(level) - )); + { + ClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); + ClientApi.INSTANCE.clientChunkLoadEvent( + new ChunkWrapper(chunk, level, wrappedLevel), + wrappedLevel + ); + }); //#endif // ClientChunkSaveEvent - ClientChunkEvents.CHUNK_UNLOAD.register((level, chunk)-> - ClientApi.INSTANCE.clientChunkSaveEvent( - new ChunkWrapper(chunk, level), - ClientLevelWrapper.getWrapper(level) - )); + ClientChunkEvents.CHUNK_UNLOAD.register((level, chunk) -> + { + ClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); + ClientApi.INSTANCE.clientChunkSaveEvent( + new ChunkWrapper(chunk, level, wrappedLevel), + wrappedLevel + ); + }); // RendererStartupEvent - Done in MixinGameRenderer // RendererShutdownEvent - Done in MixinGameRenderer diff --git a/fabric/src/main/java/com/seibel/lod/FabricServerProxy.java b/fabric/src/main/java/com/seibel/lod/FabricServerProxy.java index aa3fa361a..22ebeef37 100644 --- a/fabric/src/main/java/com/seibel/lod/FabricServerProxy.java +++ b/fabric/src/main/java/com/seibel/lod/FabricServerProxy.java @@ -98,7 +98,7 @@ public class FabricServerProxy { -> { ILevelWrapper level = getLevelWrapper((ServerLevel) chunk.getLevel()); if (isValidTime()) ServerApi.INSTANCE.serverChunkLoadEvent( - new ChunkWrapper(chunk, chunk.getLevel()), + new ChunkWrapper(chunk, chunk.getLevel(), level), level); } ); diff --git a/fabric/src/main/java/com/seibel/lod/mixins/client/MixinClientLevel.java b/fabric/src/main/java/com/seibel/lod/mixins/client/MixinClientLevel.java index 3a36905c2..0a27d2d00 100644 --- a/fabric/src/main/java/com/seibel/lod/mixins/client/MixinClientLevel.java +++ b/fabric/src/main/java/com/seibel/lod/mixins/client/MixinClientLevel.java @@ -55,7 +55,7 @@ public class MixinClientLevel { ClientLevel l = (ClientLevel) (Object) this; LevelChunk chunk = l.getChunkSource().getChunk(x, z, false); if (chunk!=null&& !chunk.isClientLightReady()) - ClientApi.INSTANCE.clientChunkLoadEvent(new ChunkWrapper(chunk, l), ClientLevelWrapper.getWrapper(l)); + ClientApi.INSTANCE.clientChunkLoadEvent(new ChunkWrapper(chunk, l, ClientLevelWrapper.getWrapper(l)), ClientLevelWrapper.getWrapper(l)); } #endif } diff --git a/fabric/src/main/java/com/seibel/lod/mixins/server/MixinChunkMap.java b/fabric/src/main/java/com/seibel/lod/mixins/server/MixinChunkMap.java index 3171aa3b7..56b43d854 100644 --- a/fabric/src/main/java/com/seibel/lod/mixins/server/MixinChunkMap.java +++ b/fabric/src/main/java/com/seibel/lod/mixins/server/MixinChunkMap.java @@ -30,7 +30,7 @@ public class MixinChunkMap { @Inject(method = "save", at = @At(value = "INVOKE", target = CHUNK_SERIALIZER_WRITE)) private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable ci) { ServerApi.INSTANCE.serverChunkSaveEvent( - new ChunkWrapper(chunk, level), + new ChunkWrapper(chunk, level, ServerLevelWrapper.getWrapper(level)), ServerLevelWrapper.getWrapper(level) ); }