Fixed 1.16 (tough someone needs to fix core/objects/opengl/RenderRegion to work with java8)

This commit is contained in:
coolGi2007
2022-03-03 17:39:14 +10:30
parent 7d2ee7126d
commit 4fc58adb0e
16 changed files with 357 additions and 602 deletions
@@ -1,7 +1,6 @@
package com.seibel.lod.common.wrappers;
import com.seibel.lod.common.LodCommonMain;
import com.seibel.lod.common.wrappers.block.BlockColorSingletonWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
import com.seibel.lod.core.handlers.IReflectionHandler;
@@ -9,7 +8,6 @@ import com.seibel.lod.core.handlers.ReflectionHandler;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
@@ -25,7 +23,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
*/
public class DependencySetup {
public static void createInitialBindings() {
SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE);
SingletonHandler.bind(IVersionConstants.class, VersionConstants.INSTANCE);
if (!LodCommonMain.serverSided) {
SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
@@ -46,9 +46,17 @@ public class McObjectConverter
return matrix;
}
static final Direction[] directions;
static {
LodDirection[] lodDirs = LodDirection.values();
directions = new Direction[lodDirs.length];
for (LodDirection lodDir : lodDirs) {
directions[lodDir.ordinal()] = Direction.byName(lodDir.name());
}
}
public static Direction Convert(LodDirection lodDirection)
{
return Direction.byName(lodDirection.name());
return directions[lodDirection.ordinal()];
}
}
@@ -52,4 +52,9 @@ public class VersionConstants implements IVersionConstants {
public boolean hasBatchGenerationImplementation() {
return true;
}
@Override
public boolean isVanillaRenderedChunkSquare() {
return true;
}
}
@@ -1,46 +0,0 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.common.wrappers.block;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import net.minecraft.world.level.block.Blocks;
/**
* Contains methods that would have been static in BlockColorWrapper.
* Since interfaces can't create/implement static methods we have
* to split the object up in two.
*
* @author James Seibel
* @version 11-17-2021
*/
public class BlockColorSingletonWrapper implements IBlockColorSingletonWrapper
{
public static final BlockColorSingletonWrapper INSTANCE = new BlockColorSingletonWrapper();
@Override
public IBlockColorWrapper getWaterColor()
{
return BlockColorWrapper.getBlockColorWrapper(Blocks.WATER);
}
}
@@ -1,322 +0,0 @@
package com.seibel.lod.common.wrappers.block;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
/**
* @author James Seibel
* @version 11-21-2021
*/
public class BlockColorWrapper implements IBlockColorWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
// public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static final AbstractBlockPosWrapper blockPos = new BlockPosWrapper(0, 0, 0);
public static final Random random = new Random(0);
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
private final Block block;
private int color;
private boolean isColored;
private boolean toTint;
private boolean foliageTint;
private boolean grassTint;
private boolean waterTint;
/**Constructor only require for the block instance we are wrapping**/
public BlockColorWrapper(Block block)
{
this.block = block;
this.color = 0;
this.isColored = true;
this.toTint = false;
this.foliageTint = false;
this.grassTint = false;
this.waterTint = false;
setupColorAndTint();
/*StringBuilder s = new StringBuilder();
s.append(block + "\n"
+ Integer.toHexString(
Minecraft.getInstance().getBlockColors().createDefault().getColor(
block.defaultBlockState(),
(World) MinecraftWrapper.INSTANCE.getWrappedServerLevel().getLevel(),
blockPosWrapper.getBlockPos())) + "\n"
);
for(Property x : Minecraft.getInstance().getBlockColors().getColoringProperties(block))
s.append(x.getName() + " " + x.getPossibleValues() + '\n');
System.out.println(s);*/
//System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint);
}
/**
* this return a wrapper of the block in input
* @param block object of the block to wrap
*/
public static IBlockColorWrapper getBlockColorWrapper(Block block)
{
//first we check if the block has already been wrapped
if (blockColorWrapperMap.containsKey(block) && blockColorWrapperMap.get(block) != null)
return blockColorWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockColorWrapper blockWrapper = new BlockColorWrapper(block);
blockColorWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint()
{
BlockState blockState = block.defaultBlockState();
//BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
//boolean isTinted = false;
//int listSize = 0;
// first step is to check if this block has a tinted face
//for (Direction direction : directions)
//{
// quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
// listSize = Math.max(listSize, quads.size());
// for (BakedQuad bakedQuad : quads)
// {
// isTinted |= bakedQuad.isTinted();
// }
//}
//if it contains a tinted face then we store this block in the toTint set
//if (isTinted)
// this.toTint = true;
//now we get the first non-empty face
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
if (!quads.isEmpty() && !(block instanceof RotatedPillarBlock && direction == Direction.UP))
break;
}
//the quads list is not empty we extract the first one
if (!quads.isEmpty())
{
isColored = true;
texture = quads.get(0).sprite;
}
else
{
isColored = true;
texture = mc.getModelManager().getBlockModelShaper().getParticleIcon(block.defaultBlockState());
texture = mc.getModelManager().getBlockModelShaper().getParticleIcon(block.defaultBlockState());
}
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int tempColor;
int colorMultiplier;
// generate the block's color
// for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
boolean lookForTint = false;
if (grassInstance() || leavesInstance())
lookForTint = true;
// generate the block's color
for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
{
// 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++)
{
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v);
if (texture.isTransparent(frameIndex, u, v))
continue;
if (lookForTint)
{
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
}
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(tempColor) > (ColorUtil.getBlue(tempColor) + 30)) || !(ColorUtil.getGreen(tempColor) > (ColorUtil.getRed(tempColor) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(tempColor) * ColorUtil.getAlpha(tempColor) * colorMultiplier;
red += ColorUtil.getBlue(tempColor) * ColorUtil.getBlue(tempColor) * colorMultiplier;
green += ColorUtil.getGreen(tempColor) * ColorUtil.getGreen(tempColor) * colorMultiplier;
blue += ColorUtil.getRed(tempColor) * ColorUtil.getRed(tempColor) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = 0;
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
(int) Math.sqrt(alpha / count),
(int) Math.sqrt(red / count),
(int) Math.sqrt(green / count),
(int) Math.sqrt(blue / count));
}
// determine if this block should use the biome color tint
if ((lookForTint && (float) numberOfGreyPixel / count > 0.75f) || waterInstance())
this.toTint = true;
// we check which kind of tint we need to apply
this.grassTint = grassInstance() && toTint;
this.foliageTint = leavesInstance() && toTint;
this.waterTint = waterInstance();
//hardcoded leaves
if (block == Blocks.SPRUCE_LEAVES)
color = ColorUtil.multiplyRGBcolors(tempColor, 0xFF619961);
else if (block == Blocks.BIRCH_LEAVES)
color = ColorUtil.multiplyRGBcolors(tempColor, 0xFF80A755);
else
color = tempColor;
}
/** determine if the given block should use the biome's grass color */
private boolean grassInstance()
{
return block instanceof GrassBlock
|| block instanceof BushBlock
// || block instanceof IGrowable
// || block instanceof AbstractPlantBlock
// || block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean leavesInstance()
{
return (block instanceof LeavesBlock && block != Blocks.SPRUCE_LEAVES && block != Blocks.BIRCH_LEAVES/* && block != Blocks.AZALEA_LEAVES && block != Blocks.FLOWERING_AZALEA_LEAVES*/)
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's foliage color */
private boolean waterInstance()
{
return block == Blocks.WATER;
}
@Override
public String getName(){
return block.getName().toString();
}
//--------------//
//Colors getters//
//--------------//
@Override
public boolean hasColor()
{
return isColored;
}
@Override
public int getColor()
{
return color;
}
//------------//
//Tint getters//
//------------//
@Override
public boolean hasTint()
{
return toTint;
}
@Override
public boolean hasGrassTint()
{
return grassTint;
}
@Override
public boolean hasFolliageTint()
{
return foliageTint;
}
@Override
public boolean hasWaterTint()
{
return waterTint;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockColorWrapper))
return false;
BlockColorWrapper that = (BlockColorWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,286 @@
package com.seibel.lod.common.wrappers.block;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.BlockDetail;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.block.BlockColors;
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.LevelReader;
import net.minecraft.world.level.biome.Biome;
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.LiquidBlock;
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.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
public class BlockDetailMap
{
public static final int FLOWER_COLOR_SCALE = 5;
public static final Random random = new Random(0);
//TODO: Perhaps make this not just use the first frame?
//FIXME: Stuff is wrong.
private static int calculateColorFromTexture(TextureAtlasSprite texture, boolean useFlowerScaling, boolean useFastLeaf) {
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 (useFastLeaf) {
r *= a;
g *= a;
b *= a;
a = 1.;
} else if (a==0.) {
continue;
} else if (useFlowerScaling && (g+0.1<b || g+0.1<r)) {
scale = FLOWER_COLOR_SCALE;
}
count += scale;
alpha += a*a*scale;
red += r*r*scale;
green += g*g*scale;
blue += b*b*scale;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = ColorUtil.rgbToInt(255,255,0,255);
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
(int) (Math.sqrt(alpha/count)*255.),
(int) (Math.sqrt(red / count)*255.),
(int) (Math.sqrt(green / count)*255.),
(int) (Math.sqrt(blue / count)*255.));
}
// TODO: Remove this when transparency is added!
double colorAlpha = ColorUtil.getAlpha(tempColor)/255.;
tempColor = ColorUtil.rgbToInt(
ColorUtil.getAlpha(tempColor),
(int)(ColorUtil.getRed(tempColor) * colorAlpha),
(int)(ColorUtil.getGreen(tempColor) * colorAlpha),
(int)(ColorUtil.getBlue(tempColor) * colorAlpha)
);
return tempColor;
}
static class BlockDetailCache {
static BlockDetailCache NULL_BLOCK_DETAIL = new BlockDetailCache();
private static final Block[] BLOCK_TO_AVOID = {Blocks.AIR, Blocks.CAVE_AIR, Blocks.BARRIER};
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
ConcurrentHashMap<Biome, BlockDetail> biomeDetailMap = null;
BlockDetail blockDetail;
BlockDetail defaultTintedDetail = null;
boolean requireResolving;
@SuppressWarnings("unused")
boolean requireShade; //TODO: Add back using this in renderer
@SuppressWarnings("unused")
boolean scaleFlowerColor; //FIXME: Do I need to scale the tint color???
int tintIndex;
static boolean isBlockToBeAvoid(Block b) {
for (Block bta : BLOCK_TO_AVOID)
if (bta==b) return true;
return false;
}
static boolean hasNoCollision(BlockState bs, BlockPos pos, LevelReader getter) {
if (!bs.getFluidState().isEmpty() || bs.getBlock() instanceof LiquidBlock) // Is blockState a fluid?
return false;
if (bs.getCollisionShape(getter, pos).isEmpty())
return true;
return false;
}
static boolean hasOnlyNonFullFace(BlockState bs, BlockPos pos, LevelReader getter) {
if (!bs.getFluidState().isEmpty() || bs.getBlock() instanceof LiquidBlock) // Is blockState a fluid?
return false;
VoxelShape voxelShape = bs.getShape(getter, pos);
if (voxelShape.isEmpty()) return true;
AABB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
return xWidth < 1 && zWidth < 1 && yWidth < 1;
}
static BlockDetailCache make(BlockState bs, BlockPos pos, LevelReader getter) {
boolean noCol, nonFull, canOcclude;
if(!bs.getFluidState().isEmpty()) {
bs = bs.getFluidState().createLegacyBlock();
FluidState fs = bs.getFluidState();
fs.getType();
noCol = false;
nonFull = false;
canOcclude = false;
BlockDetailCache result = new BlockDetailCache(fs);
//ApiShared.LOGGER.info(fs.toString()+" = ["+result+"]");
return result;
} else {
if (bs.getRenderShape() != RenderShape.MODEL) return NULL_BLOCK_DETAIL;
if (isBlockToBeAvoid(bs.getBlock())) return NULL_BLOCK_DETAIL;
//BlocksToAvoid toAvoid = CONFIG.client().worldGenerator().getBlocksToAvoid();
noCol = hasNoCollision(bs, pos, getter);
nonFull = hasOnlyNonFullFace(bs, pos, getter);
canOcclude = bs.canOcclude();
List<BakedQuad> quads = null;
for (Direction direction : DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(bs).getQuads(bs, direction, random);
if (!quads.isEmpty() && !(bs.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
break;
};
if (quads == null || quads.isEmpty()) return NULL_BLOCK_DETAIL;
BlockDetailCache result = new BlockDetailCache(canOcclude, noCol, nonFull, quads.get(0),
bs.getBlock() instanceof FlowerBlock, bs.getBlock() instanceof LeavesBlock);
// ApiShared.LOGGER.info(bs.toString()+" = ["+result+"]");
return result;
}
}
BlockDetailCache(boolean isFullBlock, boolean noCol, boolean nonFull, BakedQuad quad, boolean useFlowerScaling, boolean useFastLeaf) {
requireResolving = quad.isTinted();
requireShade = quad.isShade();
tintIndex = quad.getTintIndex();
scaleFlowerColor = useFlowerScaling;
blockDetail = new BlockDetail(calculateColorFromTexture(quad.sprite, useFlowerScaling, useFastLeaf), isFullBlock, noCol, nonFull);
if (quad.isTinted()) biomeDetailMap = new ConcurrentHashMap<Biome, BlockDetail>();
}
BlockDetailCache(FluidState fluid) {
requireResolving = true; // TODO: Maybe in the future recheck that there really is no way to see if a fluid needs tinting
requireShade = false;
tintIndex = 0; // Vanilla doesn't use this index currently. (Checked at 1.18.X, See BlockColors.class)
scaleFlowerColor = false;
TextureAtlasSprite text = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(fluid.createLegacyBlock()).getParticleIcon();
blockDetail = new BlockDetail(calculateColorFromTexture(text, false, false), true, false, false);
biomeDetailMap = new ConcurrentHashMap<Biome, BlockDetail>();
}
private BlockDetailCache()
{
//DUMMY CREATOR
}
BlockDetail getResolvedBlockDetail(BlockState bs, int x, int y, int z, LevelReader getter) {
if (!requireResolving) return blockDetail;
BlockPos pos = new BlockPos(x,y,z);
Biome biome = getter.getBiome(pos);
BlockDetail tintDetail = biomeDetailMap.get(biome);
if (tintDetail == null) {
if (!bs.getFluidState().isEmpty()) bs = bs.getFluidState().createLegacyBlock();
BlockColors bc = Minecraft.getInstance().getBlockColors();
int tintColor = bc.getColor(bs, getter, pos, tintIndex);
tintColor = ColorUtil.multiplyARGBwithRGB(blockDetail.color, tintColor);
tintDetail = new BlockDetail(tintColor, blockDetail.isFullBlock,
blockDetail.hasNoCollision, blockDetail.hasOnlyNonFullFace);
BlockDetail tintDetailCAS = biomeDetailMap.putIfAbsent(biome, tintDetail);
if (tintDetailCAS != null) tintDetail = tintDetailCAS;
}
return tintDetail;
}
// Note: this one won't resolve biome based or pos based colors. (Kinda like GUI block icons)
BlockDetail getResolvedBlockDetail(BlockState bs) {
if (!requireResolving) return blockDetail;
if (defaultTintedDetail != null) return defaultTintedDetail;
BlockColors bc = Minecraft.getInstance().getBlockColors();
if (!bs.getFluidState().isEmpty()) bs = bs.getFluidState().createLegacyBlock();
int tintColor = bc.getColor(bs, null, null, tintIndex);
if (tintColor == -1) {
defaultTintedDetail = blockDetail;
}
else {
defaultTintedDetail = new BlockDetail(ColorUtil.multiplyARGBwithRGB(blockDetail.color, tintColor),
blockDetail.isFullBlock, blockDetail.hasNoCollision, blockDetail.hasOnlyNonFullFace);
}
return defaultTintedDetail;
}
@Override
public String toString() {
return "[BlockDetail: "+blockDetail+", RequireResolving: "+requireResolving+", requireShade: "+requireShade+", scaleFlowerColor: "+scaleFlowerColor+"]";
}
}
private static ConcurrentHashMap<BlockState, BlockDetailCache> map = new ConcurrentHashMap<BlockState, BlockDetailCache>();
private BlockDetailMap() {}
private static BlockDetailCache getOrMakeBlockDetailCache(BlockState bs, BlockPos pos, LevelReader getter) {
BlockDetailCache cache = map.get(bs);
if (cache != null) return cache;
if (bs.getFluidState().isEmpty()) {
cache = BlockDetailCache.make(bs, pos, getter);
} else {
cache = BlockDetailCache.make(bs.getFluidState().createLegacyBlock(), pos, getter);
}
BlockDetailCache cacheCAS = map.putIfAbsent(bs, cache);
return cacheCAS==null ? cache : cacheCAS;
}
// Return null means skip the block
public static BlockDetail getBlockDetail(BlockState bs) {
BlockDetailCache cache = getOrMakeBlockDetailCache(bs, new BlockPos(0, 0, 0), null);
if (cache == BlockDetailCache.NULL_BLOCK_DETAIL) return null;
return cache.getResolvedBlockDetail(bs);
}
// Return null means skip the block
public static BlockDetail getBlockDetailWithCompleteTint(BlockState bs, int x, int y, int z, LevelReader tintGetter) {
BlockDetailCache cache = getOrMakeBlockDetailCache(bs, new BlockPos(x,y,z), tintGetter);
if (cache == BlockDetailCache.NULL_BLOCK_DETAIL) return null;
return cache.getResolvedBlockDetail(bs, x, y, z, tintGetter);
}
}
@@ -1,159 +0,0 @@
package com.seibel.lod.common.wrappers.block;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
/**
* @author James Seibel
* @version 11-21-2021
*/
public class BlockShapeWrapper implements IBlockShapeWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockShapeWrapper> blockShapeWrapperMap = new ConcurrentHashMap<>();
public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper();
private final Block block;
private final boolean toAvoid;
private boolean nonFull;
private boolean noCollision;
/**Constructor only require for the block instance we are wrapping**/
public BlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, int x, int y, int z)
{
this.block = block;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = ofBlockToAvoid();
setupShapes(chunkWrapper, x, y, z);
//System.out.println(block + " non full " + nonFull + " no collision " + noCollision + " to avoid " + toAvoid);
}
private BlockShapeWrapper()
{
this.block = Blocks.WATER;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = false;
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
*/
static public BlockShapeWrapper getBlockShapeWrapper(Block block, ChunkWrapper chunkWrapper, int x, int y, int z)
{
//first we check if the block has already been wrapped
if (blockShapeWrapperMap.containsKey(block) && blockShapeWrapperMap.get(block) != null)
return blockShapeWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockShapeWrapper blockWrapper = new BlockShapeWrapper(block, chunkWrapper, x, y, z);
blockShapeWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
private void setupShapes(IChunkWrapper chunkWrapper, int x, int y, int z)
{
ChunkAccess chunk = ((ChunkWrapper) chunkWrapper).getChunk();
BlockPos blockPos = new BlockPos(x, y, z);
boolean noCollisionSetted = false;
boolean nonFullSetted = false;
if (!block.defaultBlockState().getFluidState().isEmpty())// || block instanceof SixWayBlock)
{
noCollisionSetted = true;
nonFullSetted = true;
noCollision = false;
nonFull = false;
}
if (!nonFullSetted)
{
VoxelShape voxelShape = block.defaultBlockState().getShape(chunk, blockPos);
if (!voxelShape.isEmpty())
{
AABB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
nonFull = xWidth < 1 && zWidth < 1 && yWidth < 1;
}
else
{
nonFull = false;
}
}
if (!noCollisionSetted)
{
VoxelShape collisionShape = block.defaultBlockState().getCollisionShape(chunk, blockPos);
noCollision = collisionShape.isEmpty();
}
}
@Override
public boolean ofBlockToAvoid()
{
return block.equals(Blocks.AIR)
|| block.equals(Blocks.CAVE_AIR)
|| block.equals(Blocks.BARRIER);
}
//-----------------//
//Avoidance getters//
//-----------------//
@Override
public boolean isNonFull()
{
return nonFull;
}
@Override
public boolean hasNoCollision()
{
return noCollision;
}
@Override
public boolean isToAvoid()
{
return toAvoid;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockShapeWrapper))
return false;
BlockShapeWrapper that = (BlockShapeWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -1,18 +1,18 @@
package com.seibel.lod.common.wrappers.chunk;
import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightedWorldGenRegion;
import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.BlockDetail;
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.BlockColorWrapper;
import com.seibel.lod.common.wrappers.block.BlockShapeWrapper;
import com.seibel.lod.common.wrappers.block.BlockDetailMap;
import com.seibel.lod.common.wrappers.world.BiomeWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.LiquidBlockContainer;
@@ -20,6 +20,7 @@ import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.Heightmap;
/**
@@ -30,7 +31,7 @@ import net.minecraft.world.level.levelgen.Heightmap;
public class ChunkWrapper implements IChunkWrapper
{
private ChunkAccess chunk;
private BlockAndTintGetter lightSource;
private LevelReader lightSource;
@Override
public int getHeight(){
@@ -62,19 +63,9 @@ public class ChunkWrapper implements IChunkWrapper
}
@Override
public IBlockColorWrapper getBlockColorWrapper(int x, int y, int z)
{
BlockState blockState = chunk.getBlockState(new BlockPos(x,y,z));
Block block = blockState.getBlock();
return BlockColorWrapper.getBlockColorWrapper(block);
}
@Override
public IBlockShapeWrapper getBlockShapeWrapper(int x, int y, int z)
{
BlockState blockState = chunk.getBlockState(new BlockPos(x,y,z));
Block block = blockState.getBlock();
return BlockShapeWrapper.getBlockShapeWrapper(block, this, x, y, z);
public BlockDetail getBlockDetail(int x, int y, int z) {
BlockState blockState = chunk.getBlockState(new BlockPos(x, y, z));
return BlockDetailMap.getBlockDetailWithCompleteTint(blockState, x, y, z, lightSource);
}
@Deprecated
@@ -83,7 +74,7 @@ public class ChunkWrapper implements IChunkWrapper
this.chunk = chunk;
this.lightSource = null;
}
public ChunkWrapper(ChunkAccess chunk, BlockAndTintGetter lightSource) {
public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource) {
this.chunk = chunk;
this.lightSource = lightSource;
}
@@ -170,4 +161,17 @@ public class ChunkWrapper implements IChunkWrapper
public long getLongChunkPos() {
return chunk.getPos().toLong();
}
@Override
public boolean doesNearbyChunksExist() {
if (lightSource instanceof LightedWorldGenRegion) return true;
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx == 0 && dz == 0) continue;
if (lightSource.getChunk(dx + getChunkPosX(), dz + getChunkPosZ(), ChunkStatus.BIOMES, false) == null)
return false;
}
}
return true;
}
}
@@ -7,6 +7,7 @@ import java.util.stream.Collectors;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.lod.common.wrappers.WrapperFactory;
import com.seibel.lod.common.wrappers.misc.LightMapWrapper;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
@@ -182,40 +183,13 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
MinecraftWrapper.INSTANCE.sendChatMessage(
"\u00A7eOverdraw prevention will be worse than normal.");
} catch (Exception e2) {}
ClientApi.LOGGER.error("getVanillaRenderedChunks Error: {}", e);
ApiShared.LOGGER.error("getVanillaRenderedChunks Error: {}", e);
usingBackupGetVanillaRenderedChunks = true;
}
}
return getMaximumRenderedChunks();
}
@Override
public HashSet<AbstractChunkPosWrapper> getMaximumRenderedChunks()
{
IMinecraftWrapper mcWrapper = SingletonHandler.get(IMinecraftWrapper.class);
IWrapperFactory factory = SingletonHandler.get(IWrapperFactory.class);
int chunkRenderDist = this.getRenderDistance();
AbstractChunkPosWrapper centerChunkPos = mcWrapper.getPlayerChunkPos();
int startChunkX = centerChunkPos.getX() - chunkRenderDist;
int startChunkZ = centerChunkPos.getZ() - chunkRenderDist;
// add every position within render distance
HashSet<AbstractChunkPosWrapper> renderedPos = new HashSet<AbstractChunkPosWrapper>();
for (int chunkX = 0; chunkX < (chunkRenderDist * 2+1); chunkX++)
{
for(int chunkZ = 0; chunkZ < (chunkRenderDist * 2+1); chunkZ++)
{
renderedPos.add(factory.createChunkPos(startChunkX + chunkX, startChunkZ + chunkZ));
}
}
return renderedPos;
}
@Override
public int[] getLightmapPixels()
{
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.platform.Window;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.util.LodUtil;
@@ -417,7 +418,7 @@ public class MinecraftWrapper implements IMinecraftWrapper
@Override
public void crashMinecraft(String errorMessage, Throwable exception)
{
ClientApi.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
ApiShared.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report);
}
@@ -19,6 +19,7 @@
package com.seibel.lod.common.wrappers.worldGeneration;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig;
@@ -220,7 +221,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (!unsafeThreadingRecorded && !f.isDone()) {
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons: Unsafe Threading in Chunk Generator Detected!");
MC.sendChatMessage("\u00A7eTo increase stability, it is recommended to set world generation threads count to 1.");
ClientApi.LOGGER.error("Unsafe Threading in Chunk Generator: ", new RuntimeException("Concurrent future"));
ApiShared.LOGGER.error("Unsafe Threading in Chunk Generator: ", new RuntimeException("Concurrent future"));
unsafeThreadingRecorded = true;
}
return f.join();
@@ -262,19 +263,19 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
try {
event.join();
} catch (Throwable e) {
ClientApi.LOGGER.error("Batching World Generator: Event {} gotten an exception", event);
ClientApi.LOGGER.error("Exception: ", e);
ApiShared.LOGGER.error("Batching World Generator: Event {} gotten an exception", event);
ApiShared.LOGGER.error("Exception: ", e);
unknownExceptionCount++;
lastExceptionTriggerTime = System.nanoTime();
} finally {
iter.remove();
}
} else if (event.hasTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
ClientApi.LOGGER.error("Batching World Generator: " + event + " timed out and terminated!");
ClientApi.LOGGER.info("Dump PrefEvent: " + event.pEvent);
ApiShared.LOGGER.error("Batching World Generator: " + event + " timed out and terminated!");
ApiShared.LOGGER.info("Dump PrefEvent: " + event.pEvent);
try {
if (!event.terminate())
ClientApi.LOGGER.error("Failed to terminate the stuck generation event!");
ApiShared.LOGGER.error("Failed to terminate the stuck generation event!");
} finally {
iter.remove();
}
@@ -284,7 +285,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
try {
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons: Too many exceptions in Batching World Generator! Disabling the generator.");
} catch (Exception e) {}
ClientApi.LOGGER.error("Too many exceptions in Batching World Generator! Now disabling.");
ApiShared.LOGGER.error("Too many exceptions in Batching World Generator! Now disabling.");
unknownExceptionCount = 0;
CONFIG.client().worldGenerator().setEnableDistantGeneration(false);
}
@@ -292,14 +293,14 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
public BatchGenerationEnvironment(IWorldWrapper serverlevel, LodBuilder lodBuilder, LodDimension lodDim) {
super(serverlevel, lodBuilder, lodDim);
ClientApi.LOGGER.info("================WORLD_GEN_STEP_INITING=============");
ApiShared.LOGGER.info("================WORLD_GEN_STEP_INITING=============");
ChunkGenerator generator = ((WorldWrapper) serverlevel).getServerWorld().getChunkSource().getGenerator();
if (!(generator instanceof NoiseBasedChunkGenerator ||
generator instanceof DebugLevelSource ||
generator instanceof FlatLevelSource)) {
MC.sendChatMessage("\u00A74\u00A7l\u00A7uWARNING: Distant Horizons: Unknown Chunk Generator Detected! Distant Generation May Fail!");
MC.sendChatMessage("\u00A7eIf it does crash, set Distant Generation to OFF or Generation Mode to None.");
ClientApi.LOGGER.warn("Unknown Chunk Generator detected: {}", generator.getClass());
ApiShared.LOGGER.warn("Unknown Chunk Generator detected: {}", generator.getClass());
}
params = new GlobalParameters((ServerLevel) ((WorldWrapper) serverlevel).getWorld(), lodBuilder, lodDim);
}
@@ -313,7 +314,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
try {
chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos);
} catch (Exception e) {
ClientApi.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
ApiShared.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
}
if (chunkData == null) {
return new ProtoChunk(chunkPos, UpgradeData.EMPTY);
@@ -321,7 +322,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
try {
return ChunkLoader.read(level, lightEngine, chunkPos, chunkData);
} catch (Exception e) {
ClientApi.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
ApiShared.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
return new ProtoChunk(chunkPos, UpgradeData.EMPTY);
}
}
@@ -330,7 +331,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
void generateLodFromList(GenerationEvent e) {
if (ENABLE_EVENT_LOGGING)
ClientApi.LOGGER.info("Lod Generate Event: " + e.pos);
ApiShared.LOGGER.info("Lod Generate Event: " + e.pos);
e.pEvent.beginNano = System.nanoTime();
GridList<ChunkAccess> referencedChunks;
DistanceGenerationMode generationMode;
@@ -415,7 +416,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk;
if (isFull) {
if (ENABLE_LOAD_EVENT_LOGGING)
ClientApi.LOGGER.info("Detected full existing chunk at {}", target.getPos());
ApiShared.LOGGER.info("Detected full existing chunk at {}", target.getPos());
params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region),
new LodBuilderConfig(DistanceGenerationMode.FULL), true, e.genAllDetails);
} else if (target.getStatus() == ChunkStatus.EMPTY && generationMode == DistanceGenerationMode.NONE) {
@@ -432,7 +433,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
e.refreshTimeout();
if (ENABLE_PERF_LOGGING) {
e.tParam.perf.recordEvent(e.pEvent);
ClientApi.LOGGER.info(e.tParam.perf);
ApiShared.LOGGER.info(e.tParam.perf);
}
}
@@ -506,15 +507,15 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
@Override
public void stop(boolean blocking) {
ClientApi.LOGGER.info("Batch Chunk Generator shutting down...");
ApiShared.LOGGER.info("Batch Chunk Generator shutting down...");
executors.shutdownNow();
if (blocking) {
try {
if (!executors.awaitTermination(10, TimeUnit.SECONDS)) {
ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...");
ApiShared.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...");
}
} catch (InterruptedException e) {
ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...", e);
ApiShared.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...", e);
}
}
}
@@ -5,6 +5,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.PrefEvent;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.config.LightGenerationMode;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
@@ -58,7 +59,7 @@ public final class GenerationEvent {
public boolean terminate() {
future.cancel(true);
ClientApi.LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
ApiShared.LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
BatchGenerationEnvironment.threadFactory.dumpAllThreadStacks();
return future.isCancelled();
}
@@ -1,6 +1,7 @@
package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import java.util.Objects;
@@ -31,7 +32,7 @@ import net.minecraft.world.level.material.Fluids;
import org.apache.logging.log4j.Logger;
public class ChunkLoader {
private static final Logger LOGGER = ClientApi.LOGGER;
private static final Logger LOGGER = ApiShared.LOGGER;
private static LevelChunkSection[] readSections(WorldGenLevel level, LevelLightEngine lightEngine,
ChunkPos chunkPos, CompoundTag tagLevel) {
@@ -3,6 +3,7 @@ package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
import java.util.List;
import java.util.stream.Stream;
import com.seibel.lod.core.api.ApiShared;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.EmptyChunkGenerator;
@@ -173,7 +174,7 @@ public class LightedWorldGenRegion extends WorldGenRegion {
}
}
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus) {
ClientApi.LOGGER.info("WorldGen requiring " + chunkStatus
ApiShared.LOGGER.info("WorldGen requiring " + chunkStatus
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works.");
debugTriggeredForStatus = chunkStatus;
}
@@ -21,6 +21,7 @@ package com.seibel.lod.fabric;
import com.seibel.lod.common.LodCommonMain;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
@@ -64,7 +65,7 @@ public class Main implements ClientModInitializer
LodCommonMain.startup(null, false);
DependencySetup.createInitialBindings();
SingletonHandler.bind(IModChecker.class, ModChecker.INSTANCE);
ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
ApiShared.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
// Check if this works
client_proxy = new ClientProxy();
@@ -81,6 +82,6 @@ public class Main implements ClientModInitializer
LodCommonMain.initConfig();
LodCommonMain.startup(null, true);
DependencySetup.createInitialBindings();
ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
ApiShared.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
}
}
@@ -24,6 +24,7 @@ import com.seibel.lod.common.forge.LodForgeMethodCaller;
import com.seibel.lod.common.wrappers.config.ConfigGui;
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
import com.seibel.lod.core.handlers.ReflectionHandler;
@@ -70,7 +71,7 @@ public class ForgeMain implements LodForgeMethodCaller
LodCommonMain.initConfig();
LodCommonMain.startup(this, !FMLLoader.getDist().isClient());
ForgeDependencySetup.createInitialBindings();
ClientApi.LOGGER.info("Distant Horizons initializing...");
ApiShared.LOGGER.info("Distant Horizons initializing...");
SingletonHandler.bind(IModChecker.class, ModChecker.INSTANCE);
if (ReflectionHandler.instance.optifinePresent()) {