diff --git a/common/src/main/java/com/seibel/lod/common/LodCommonMain.java b/common/src/main/java/com/seibel/lod/common/LodCommonMain.java new file mode 100644 index 000000000..8bfedf620 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/LodCommonMain.java @@ -0,0 +1,23 @@ +package com.seibel.lod.common; + +import com.seibel.lod.common.forge.LodForgeMethodCaller; +import com.seibel.lod.common.wrappers.DependencySetup; + +/** + * This is the common main class + * @author Ran + */ +public class LodCommonMain { + public static boolean forge = false; + public static LodForgeMethodCaller forgeMethodCaller; + + public static void startup(LodForgeMethodCaller caller) { + if (caller != null) { + LodCommonMain.forge = true; + forgeMethodCaller = caller; + } + + DependencySetup.createInitialBindings(); + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java b/common/src/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java new file mode 100644 index 000000000..8e3d8afab --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java @@ -0,0 +1,15 @@ +package com.seibel.lod.common.forge; + +import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.data.ModelDataMap; + +import java.util.List; +import java.util.Random; + +public interface LodForgeMethodCaller { + List getQuads(MinecraftWrapper mc, Block block, BlockState blockState, Direction direction, Random random, ModelDataMap dataMap); +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/DependencySetup.java b/common/src/main/java/com/seibel/lod/common/wrappers/DependencySetup.java new file mode 100644 index 000000000..c6c15057c --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/DependencySetup.java @@ -0,0 +1,33 @@ +package com.seibel.lod.common.wrappers; + +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; +import com.seibel.lod.core.handlers.ReflectionHandler; +import com.seibel.lod.core.util.SingletonHandler; +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; + +/** + * Binds all necessary dependencies, so we + * can access them in Core.
+ * This needs to be called before any Core classes + * are loaded. + * + * @author James Seibel + * @author Ran + * @version 12-1-2021 + */ +public class DependencySetup { + public static void createInitialBindings() { + SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE); + SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE); + SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); + SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); + + SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions())); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java b/common/src/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java new file mode 100644 index 000000000..31614b123 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java @@ -0,0 +1,54 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers; + +import java.nio.FloatBuffer; + +import com.mojang.math.Matrix4f; +import com.seibel.lod.core.enums.LodDirection; +import com.seibel.lod.core.objects.math.Mat4f; + +import net.minecraft.core.Direction; + +/** + * This class converts to and from Minecraft objects (Ex: Matrix4f) + * and objects we created (Ex: Mat4f). + * + * @author James Seibel + * @version 11-20-2021 + */ +public class McObjectConverter +{ + /** 4x4 float matrix converter */ + public static Mat4f Convert(Matrix4f mcMatrix) + { + FloatBuffer buffer = FloatBuffer.allocate(16); + mcMatrix.store(buffer); + Mat4f matrix = new Mat4f(buffer); + matrix.transpose(); + return matrix; + } + + + public static Direction Convert(LodDirection lodDirection) + { + return Direction.byName(lodDirection.name()); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java new file mode 100644 index 000000000..4383458a8 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java @@ -0,0 +1,91 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers; + +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.objects.lod.LodDimension; +import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper; +import com.seibel.lod.common.wrappers.block.BlockPosWrapper; +import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper; +import com.seibel.lod.common.wrappers.worldGeneration.WorldGeneratorWrapper; + +/** + * This handles creating abstract wrapper objects. + * + * @author James Seibel + * @version 11-20-2021 + */ +public class WrapperFactory implements IWrapperFactory +{ + public static final WrapperFactory INSTANCE = new WrapperFactory(); + + + @Override + public AbstractBlockPosWrapper createBlockPos() + { + return new BlockPosWrapper(); + } + + @Override + public AbstractBlockPosWrapper createBlockPos(int x, int y, int z) + { + return new BlockPosWrapper(x,y,z); + } + + + + + @Override + public AbstractChunkPosWrapper createChunkPos() + { + return new ChunkPosWrapper(); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(int x, int z) + { + return new ChunkPosWrapper(x, z); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos) + { + return new ChunkPosWrapper(newChunkPos); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos) + { + return new ChunkPosWrapper(blockPos); + } + + + + @Override + public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) + { + return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper); + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java new file mode 100644 index 000000000..7a57e241e --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers; + +import net.minecraft.world.level.levelgen.Heightmap; + +/** + * Stores any variables or code that + * may be shared between wrapper objects. + * + * @author James Seibel + * @version 11-20-2021 + */ +public class WrapperUtil { + /** If we ever need to use a heightmap for any reason, use this one. */ + public static final Heightmap.Types DEFAULT_HEIGHTMAP = Heightmap.Types.WORLD_SURFACE_WG; +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java new file mode 100644 index 000000000..732625970 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java @@ -0,0 +1,46 @@ +/* + * 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 . + */ + +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.defaultBlockState(), new BlockPosWrapper(0,0, 0)); + } +} + diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java new file mode 100644 index 000000000..9173dbe9f --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java @@ -0,0 +1,303 @@ +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.LodCommonMain; +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.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BushBlock; +import net.minecraft.world.level.block.FlowerBlock; +import net.minecraft.world.level.block.GrassBlock; +import net.minecraft.world.level.block.LeavesBlock; +import net.minecraft.world.level.block.TallGrassBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.data.ModelDataMap; + + +/** + * @author James Seibel + * @version 11-21-2021 + */ +public class BlockColorWrapper implements IBlockColorWrapper +{ + //set of block which require tint + public static final ConcurrentMap blockColorWrapperMap = new ConcurrentHashMap<>(); + public static final ModelDataMap dataMap = new ModelDataMap.Builder().build(); + public static final AbstractBlockPosWrapper blockPos = new BlockPosWrapper(0,0,0); + public static 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 Block block; + private int color; + private boolean isColored; + private boolean toTint; + private boolean folliageTint; + private boolean grassTint; + private boolean waterTint; + + + /**Constructor only require for the block instance we are wrapping**/ + public BlockColorWrapper(BlockState blockState, AbstractBlockPosWrapper blockPosWrapper) + { + this.block = blockState.getBlock(); + this.color = 0; + this.isColored = true; + this.toTint = false; + this.folliageTint = false; + this.grassTint = false; + this.waterTint = false; + setupColorAndTint(blockState,blockPosWrapper); + 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 blockState of the block to wrap + */ + static public IBlockColorWrapper getBlockColorWrapper(BlockState blockState, AbstractBlockPosWrapper blockPosWrapper) + { + //first we check if the block has already been wrapped + if (blockColorWrapperMap.containsKey(blockState.getBlock()) && blockColorWrapperMap.get(blockState.getBlock()) != null) + return blockColorWrapperMap.get(blockState.getBlock()); + + + //if it hasn't been created yet, we create it and save it in the map + IBlockColorWrapper blockWrapper = new BlockColorWrapper(blockState, blockPosWrapper); + blockColorWrapperMap.put(blockState.getBlock(), 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, AbstractBlockPosWrapper blockPosWrapper) + { + MinecraftWrapper mc = MinecraftWrapper.INSTANCE; + TextureAtlasSprite texture; + List quads = null; + + boolean isTinted = false; + int listSize = 0; + + // first step is to check if this block has a tinted face + for (Direction direction : directions) + { + if (LodCommonMain.forge) { + quads = LodCommonMain.forgeMethodCaller.getQuads(mc, block, blockState, direction, random, dataMap); + } else { + 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) + { + if (LodCommonMain.forge) { + quads = LodCommonMain.forgeMethodCaller.getQuads(mc, block, blockState, direction, random, dataMap); + } else { + quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random); + } + if (!quads.isEmpty()) + break; + } + + //the quads list is not empty we extract the first one + if (!quads.isEmpty()) + { + isColored = true; + texture = quads.get(0).getSprite(); + } + else + { + isColored = true; + 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++) + int frameIndex = 0; // TODO + { + // textures normally use u and v instead of x and y + for (int u = 0; u < texture.getHeight(); u++) + { + for (int v = 0; v < texture.getWidth(); v++) + { + + tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v); + + if (ColorUtil.getAlpha(TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v)) == 0) + continue; + + // 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) * colorMultiplier; + red += ColorUtil.getBlue(tempColor) * colorMultiplier; + green += ColorUtil.getGreen(tempColor) * colorMultiplier; + blue += ColorUtil.getRed(tempColor) * colorMultiplier; + } + } + } + + + if (count == 0) + // this block is entirely transparent + tempColor = 0; + else + { + // determine the average color + alpha /= count; + red /= count; + green /= count; + blue /= count; + tempColor = ColorUtil.rgbToInt(alpha, red, green, blue); + } + + // determine if this block should use the biome color tint + if ((grassInstance() || leavesInstance() || waterIstance()) && (float) numberOfGreyPixel / count > 0.75f) + this.toTint = true; + + // we check which kind of tint we need to apply + this.grassTint = grassInstance() && toTint; + + this.folliageTint = leavesInstance() && toTint; + + this.waterTint = waterIstance() && toTint; + + 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 TallGrassBlock; + } + + /** determine if the given block should use the biome's foliage color */ + private boolean leavesInstance() + { + return block instanceof LeavesBlock + || block == Blocks.VINE + || block == Blocks.SUGAR_CANE; + } + + /** determine if the given block should use the biome's foliage color */ + private boolean waterIstance() + { + return block == Blocks.WATER; + } + +//--------------// +//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 folliageTint; + } + + @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); + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java new file mode 100644 index 000000000..b5cd321b1 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java @@ -0,0 +1,84 @@ +package com.seibel.lod.common.wrappers.block; + +import java.util.Objects; + +import com.seibel.lod.core.enums.LodDirection; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; + +import net.minecraft.core.BlockPos; + + +/** + * @author James Seibel + * @version 11-21-2021 + */ +public class BlockPosWrapper extends AbstractBlockPosWrapper +{ + private BlockPos.MutableBlockPos blockPos; + + + public BlockPosWrapper() + { + this.blockPos = new BlockPos.MutableBlockPos(0,0,0); + } + + public BlockPosWrapper(int x, int y, int z) + { + this.blockPos = new BlockPos.MutableBlockPos(x, y, z); + } + + + + @Override + public void set(int x, int y, int z) + { + blockPos.set(x, y, z); + } + + @Override + public int getX() + { + return blockPos.getX(); + } + + @Override + public int getY() + { + return blockPos.getY(); + } + + @Override + public int getZ() + { + return blockPos.getZ(); + } + + @Override + public int get(LodDirection.Axis axis) + { + return axis.choose(getX(), getY(), getZ()); + } + + public BlockPos.MutableBlockPos getBlockPos() + { + return blockPos; + } + + @Override public boolean equals(Object o) + { + return blockPos.equals(o); + } + + @Override public int hashCode() + { + return Objects.hash(blockPos); + } + + @Override + public BlockPosWrapper offset(int x, int y, int z) + { + blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z); + return this; + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java new file mode 100644 index 000000000..e8313aba4 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java @@ -0,0 +1,159 @@ + +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 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, AbstractBlockPosWrapper blockPosWrapper) + { + this.block = block; + this.nonFull = false; + this.noCollision = false; + this.toAvoid = ofBlockToAvoid(); + setupShapes(chunkWrapper, blockPosWrapper); + 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, AbstractBlockPosWrapper blockPosWrapper) + { + //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, blockPosWrapper); + blockShapeWrapperMap.put(block, blockWrapper); + + //we return the newly created wrapper + return blockWrapper; + } + + private void setupShapes(IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper) + { + ChunkAccess chunk = ((ChunkWrapper) chunkWrapper).getChunk(); + BlockPos blockPos = ((BlockPosWrapper) blockPosWrapper).getBlockPos(); + 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); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java new file mode 100644 index 000000000..41bcba5f5 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java @@ -0,0 +1,28 @@ +package com.seibel.lod.common.wrappers.block; + + +import net.minecraft.client.renderer.texture.TextureAtlasSprite; + +/** + * For wrapping/utilizing around TextureAtlasSprite + * @author Ran + */ +public class TextureAtlasSpriteWrapper { + + /** + * This code is from Minecraft Forge + * Which is licensed under the terms of GNU Lesser General Public License + * as published by the Free Software Foundation version 2.1 + * of the License. + * + * The code has been modified to use TextureAtlasSprite + */ + public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y) { + if (sprite.animatedTexture != null) { + x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width; + y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height; + } + + return sprite.mainImage[0].getPixelRGBA(x, y); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java new file mode 100644 index 000000000..d4fcd8d80 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java @@ -0,0 +1,116 @@ +package com.seibel.lod.common.wrappers.chunk; + +import java.util.Objects; + +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.common.wrappers.block.BlockPosWrapper; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.ChunkPos; + + +/** + * @author James Seibel + * @version 11-21-2021 + */ +public class ChunkPosWrapper extends AbstractChunkPosWrapper +{ + private net.minecraft.world.level.ChunkPos chunkPos; + + public ChunkPosWrapper() + { + this.chunkPos = new ChunkPos(0, 0); + } + + public ChunkPosWrapper(BlockPos blockPos) + { + this.chunkPos = new ChunkPos(blockPos); + } + + public ChunkPosWrapper(AbstractChunkPosWrapper newChunkPos) + { + this.chunkPos = ((ChunkPosWrapper) newChunkPos).chunkPos; + } + + public ChunkPosWrapper(AbstractBlockPosWrapper blockPos) + { + this.chunkPos = new ChunkPos(((BlockPosWrapper) blockPos).getBlockPos()); + } + + public ChunkPosWrapper(int chunkX, int chunkZ) + { + this.chunkPos = new ChunkPos(chunkX, chunkZ); + } + + + public ChunkPosWrapper(ChunkPos pos) + { + this.chunkPos = pos; + } + + + + + @Override + public int getX() + { + return chunkPos.x; + } + + @Override + public int getZ() + { + return chunkPos.z; + } + + @Override + public int getMinBlockX() + { + return chunkPos.getMinBlockX(); + } + + @Override + public int getMinBlockZ() + { + return chunkPos.getMinBlockZ(); + } + + @Override + public int getRegionX() + { + return chunkPos.getRegionX(); + } + + @Override + public int getRegionZ() + { + return chunkPos.getRegionZ(); + } + + public ChunkPos getChunkPos() + { + return chunkPos; + } + + @Override + public boolean equals(Object o) + { + return chunkPos.equals(o); + } + + @Override + public int hashCode() + { + return Objects.hash(chunkPos); + } + + @Override + public AbstractBlockPosWrapper getWorldPosition() + { + // the parameter here is the y position + BlockPos blockPos = chunkPos.getMiddleBlockPosition(0); + return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + } + +} 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 new file mode 100644 index 000000000..311c44070 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java @@ -0,0 +1,130 @@ +package com.seibel.lod.common.wrappers.chunk; + +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +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.BlockPosWrapper; +import com.seibel.lod.common.wrappers.block.BlockShapeWrapper; +import com.seibel.lod.common.wrappers.world.BiomeWrapper; + +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.chunk.ChunkAccess; + +/** + * + * @author James Seibel + * @version 11-21-2021 + */ +public class ChunkWrapper implements IChunkWrapper +{ + + private ChunkAccess chunk; + private AbstractChunkPosWrapper chunkPos; + + @Override + public int getHeight(){ + return chunk.getMaxBuildHeight(); + } + + @Override + public boolean isPositionInWater(AbstractBlockPosWrapper blockPos) + { + BlockState blockState = chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()); + + //This type of block is always in water + if((blockState.getBlock() instanceof LiquidBlock))// && !(blockState.getBlock() instanceof IWaterLoggable)) + return true; + + //This type of block could be in water + if(blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get()) + return true; + + return false; + } + + @Override + public int getHeightMapValue(int xRel, int zRel) + { + return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel); + } + + @Override + public IBiomeWrapper getBiome(int xRel, int yAbs, int zRel) + { + return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2)); + } + + @Override + public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos) + { + return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()), blockPos); + } + + @Override + public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos) + { + return BlockShapeWrapper.getBlockShapeWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock(), this, blockPos); + } + + public ChunkWrapper(ChunkAccess chunk) + { + this.chunk = chunk; + this.chunkPos = new ChunkPosWrapper(chunk.getPos()); + } + + public ChunkAccess getChunk(){ + return chunk; + } + @Override + public AbstractChunkPosWrapper getPos() + { + return chunkPos; + } + + @Override + public boolean isLightCorrect(){ + return chunk.isLightCorrect(); + } + + public boolean + isWaterLogged(BlockPosWrapper blockPos) + { + BlockState blockState = chunk.getBlockState(blockPos.getBlockPos()); + +// //This type of block is always in water +// if((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable)) +// return true; + + //This type of block could be in water + if(blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get()) + return true; + + return false; + } + + public int getEmittedBrightness(BlockPosWrapper blockPos) + { + return chunk.getLightEmission(blockPos.getBlockPos()); + } + + @Override + public boolean isWaterLogged(AbstractBlockPosWrapper blockPos) + { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getEmittedBrightness(AbstractBlockPosWrapper blockPos) + { + // TODO Auto-generated method stub + return 0; + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java new file mode 100644 index 000000000..3fe97ea0f --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java @@ -0,0 +1,161 @@ +package com.seibel.lod.common.wrappers.minecraft; + +import java.awt.*; +import java.util.HashSet; + +import org.lwjgl.opengl.GL20; + +import com.mojang.math.Vector3f; +import com.seibel.lod.core.objects.math.Mat4f; +import com.seibel.lod.core.objects.math.Vec3d; +import com.seibel.lod.core.objects.math.Vec3f; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.lod.common.wrappers.McObjectConverter; +import com.seibel.lod.common.wrappers.block.BlockPosWrapper; +import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper; + +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LevelRenderer.RenderChunkInfo; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk; +import net.minecraft.core.BlockPos; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.phys.Vec3; + + +/** + * A singleton that contains everything + * related to rendering in Minecraft. + * + * @author James Seibel + * @version 11-26-2021 + */ +public class MinecraftRenderWrapper implements IMinecraftRenderWrapper +{ + public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper(); + + private final GameRenderer gameRenderer = Minecraft.getInstance().gameRenderer; + private final static Minecraft mc = Minecraft.getInstance(); + + + + @Override + public Vec3f getLookAtVector() + { + Camera camera = gameRenderer.getMainCamera(); + Vector3f cameraDir = camera.getLookVector(); + return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z()); + } + + @Override + public AbstractBlockPosWrapper getCameraBlockPosition() + { + Camera camera = gameRenderer.getMainCamera(); + BlockPos blockPos = camera.getBlockPosition(); + return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + } + + @Override + public boolean playerHasBlindnessEffect() + { + return mc.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null; + } + + @Override + public Vec3d getCameraExactPosition() + { + Camera camera = gameRenderer.getMainCamera(); + Vec3 projectedView = camera.getPosition(); + + return new Vec3d(projectedView.x, projectedView.y, projectedView.z); + } + + @Override + public Mat4f getDefaultProjectionMatrix(float partialTicks) + { + return McObjectConverter.Convert(gameRenderer.getProjectionMatrix(gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true))); + } + + @Override + public double getGamma() + { + return mc.options.gamma; + } + + @Override + public Color getFogColor() { + float[] colorValues = new float[4]; + GL20.glGetFloatv(GL20.GL_FOG_COLOR, colorValues); + return new Color(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); + } + + @Override + public Color getSkyColor() { + if (mc.level.dimensionType().hasSkyLight()) { + Vec3 colorValues = mc.level.getSkyColor(mc.gameRenderer.getMainCamera().getPosition(), mc.getFrameTime()); + return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z); + } else + return new Color(0, 0, 0); + } + + @Override + public double getFov(float partialTicks) + { + return gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true); + } + + /** Measured in chunks */ + @Override + public int getRenderDistance() + { + return mc.options.renderDistance; + } + + @Override + public int getScreenWidth() + { + return mc.getWindow().getWidth(); + } + @Override + public int getScreenHeight() + { + return mc.getWindow().getHeight(); + } + + /** + * This method returns the ChunkPos of all chunks that Minecraft + * is going to render this frame.

+ *

+ * Note: This isn't perfect. It will return some chunks that are outside + * the clipping plane. (For example, if you are high above the ground some chunks + * will be incorrectly added, even though they are outside render range). + */ + @Override + public HashSet getRenderedChunks() + { + HashSet loadedPos = new HashSet<>(); + + // Wow, those are some long names! + + // go through every RenderInfo to get the compiled chunks + LevelRenderer renderer = mc.levelRenderer; + for (RenderChunkInfo worldRenderer$LocalRenderInformationContainer : renderer.renderChunks) + { + CompiledChunk compiledChunk = worldRenderer$LocalRenderInformationContainer.chunk.getCompiledChunk(); + if (!compiledChunk.hasNoRenderableLayers()) + { + // add the ChunkPos for every rendered chunk + BlockPos bpos = worldRenderer$LocalRenderInformationContainer.chunk.getOrigin(); + + loadedPos.add(new ChunkPosWrapper(bpos)); + } + } + + return loadedPos; + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java new file mode 100644 index 000000000..f7591e002 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java @@ -0,0 +1,423 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers.minecraft; + +import java.awt.Color; +import java.io.File; +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.ClientApi; +import com.seibel.lod.core.enums.LodDirection; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.common.wrappers.McObjectConverter; +import com.seibel.lod.common.wrappers.block.BlockPosWrapper; +import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper; +import com.seibel.lod.common.wrappers.misc.LightMapWrapper; +import com.seibel.lod.common.wrappers.world.DimensionTypeWrapper; +import com.seibel.lod.common.wrappers.world.WorldWrapper; + +import net.minecraft.CrashReport; +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.server.IntegratedServer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.dimension.DimensionType; +import org.jetbrains.annotations.Nullable; + +/** + * A singleton that wraps the Minecraft class + * to allow for easier movement between Minecraft versions. + * + * @author James Seibel + * @version 9-16-2021 + */ +public class MinecraftWrapper implements IMinecraftWrapper +{ + public static final MinecraftWrapper INSTANCE = new MinecraftWrapper(); + + private final Minecraft mc = Minecraft.getInstance(); + + /** + * The lightmap for the current: + * Time, dimension, brightness setting, etc. + */ + private NativeImage lightMap = null; + + private ProfilerWrapper profilerWrapper; + + + private MinecraftWrapper() + { + + } + + + + //================// + // helper methods // + //================// + + /** + * This should be called at the beginning of every frame to + * clear any Minecraft data that becomes out of date after a frame.

+ *

+ * LightMaps and other time sensitive objects fall in this category.

+ *

+ * This doesn't affect OpenGL objects in any way. + */ + @Override + public void clearFrameObjectCache() + { + lightMap = null; + } + + + + //=================// + // method wrappers // + //=================// + + @Override + public float getShade(LodDirection lodDirection) { + if (mc.level != null) + { + Direction mcDir = McObjectConverter.Convert(lodDirection); + return mc.level.getShade(mcDir, true); + } + else return 0.0f; + } + + @Override + public boolean hasSinglePlayerServer() + { + return mc.hasSingleplayerServer(); + } + + @Override + public String getCurrentServerName() + { + return mc.getCurrentServer().name; + } + + @Override + public String getCurrentServerIp() + { + return mc.getCurrentServer().ip; + } + + @Override + public String getCurrentServerVersion() + { + return mc.getCurrentServer().version.getString(); + } + + /** Returns the dimension the player is currently in */ + @Override + public IDimensionTypeWrapper getCurrentDimension() + { + return DimensionTypeWrapper.getDimensionTypeWrapper(mc.player.level.dimensionType()); + } + + @Override + public String getCurrentDimensionId() + { + return LodUtil.getDimensionIDFromWorld(WorldWrapper.getWorldWrapper(mc.level)); + } + + /** This texture changes every frame */ + @Override + public ILightMapWrapper getCurrentLightMap() + { + // get the current lightMap if the cache is empty + if (lightMap == null) + { + LightTexture tex = mc.gameRenderer.lightTexture(); + lightMap = tex.lightPixels; + } + return new LightMapWrapper(lightMap); + } + + /** + * Returns the color int at the given pixel coordinates + * from the current lightmap. + * @param u x location in texture space + * @param v z location in texture space + */ + @Override + public int getColorIntFromLightMap(int u, int v) + { + if (lightMap == null) + { + // make sure the lightMap is up-to-date + getCurrentLightMap(); + } + + return lightMap.getPixelRGBA(u, v); + } + + /** + * Returns the Color at the given pixel coordinates + * from the current lightmap. + * @param u x location in texture space + * @param v z location in texture space + */ + @Override + public Color getColorFromLightMap(int u, int v) + { + return LodUtil.intToColor(lightMap.getPixelRGBA(u, v)); + } + + + + + //=============// + // Simple gets // + //=============// + + public LocalPlayer getPlayer() + { + return mc.player; + } + + @Override + public boolean playerExists() + { + return mc.player != null; + } + + @Override + public BlockPosWrapper getPlayerBlockPos() + { + BlockPos playerPos = getPlayer().blockPosition(); + return new BlockPosWrapper(playerPos.getX(), playerPos.getY(), playerPos.getZ()); + } + + @Override + public ChunkPosWrapper getPlayerChunkPos() + { + ChunkPos playerPos = getPlayer().chunkPosition(); + return new ChunkPosWrapper(playerPos.x, playerPos.z); + } + + public Options getOptions() + { + return mc.options; + } + + public ModelManager getModelManager() + { + return mc.getModelManager(); + } + + public ClientLevel getClientLevel() + { + return mc.level; + } + + @Override + public IWorldWrapper getWrappedServerWorld() + { + if (mc.level == null) + return null; + + DimensionType dimension = mc.level.dimensionType(); + IntegratedServer server = mc.getSingleplayerServer(); + + if (server == null) + return null; + + ServerLevel serverWorld = null; + Iterable worlds = server.getAllLevels(); + for (ServerLevel world : worlds) + { + if (world.dimensionType() == dimension) + { + serverWorld = world; + break; + } + } + return WorldWrapper.getWorldWrapper(serverWorld); + } + + public WorldWrapper getWrappedClientLevel() + { + return WorldWrapper.getWorldWrapper(mc.level); + } + + public WorldWrapper getWrappedServerLevel() + { + + if (mc.level == null) + return null; + DimensionType dimension = mc.level.dimensionType(); + IntegratedServer server = mc.getSingleplayerServer(); + if (server == null) + return null; + + Iterable worlds = server.getAllLevels(); + ServerLevel returnWorld = null; + + for (ServerLevel world : worlds) + { + if (world.dimensionType() == dimension) + { + returnWorld = world; + break; + } + } + + return WorldWrapper.getWorldWrapper(returnWorld); + } + + @Nullable + @Override + public IWorldWrapper getWrappedClientWorld() + { + return WorldWrapper.getWorldWrapper(mc.level); + } + + @Override + public File getGameDirectory() + { + return mc.gameDirectory; + } + + @Override + public IProfilerWrapper getProfiler() + { + if (profilerWrapper == null) + profilerWrapper = new ProfilerWrapper(mc.getProfiler()); + else if (mc.getProfiler() != profilerWrapper.profiler) + profilerWrapper.profiler = mc.getProfiler(); + + return profilerWrapper; } + + public ClientPacketListener getConnection() + { + return mc.getConnection(); + } + + public GameRenderer getGameRenderer() + { + return mc.gameRenderer; + } + + public Entity getCameraEntity() + { + return mc.cameraEntity; + } + + public Window getWindow() + { + return mc.getWindow(); + } + + @Override + public float getSkyDarken(float partialTicks) + { + return mc.level.getSkyDarken(partialTicks); + } + + public IntegratedServer getSinglePlayerServer() + { + return mc.getSingleplayerServer(); + } + + @Override + public boolean connectedToServer() + { + return mc.getCurrentServer() != null; + } + + public ServerData getCurrentServer() + { + return mc.getCurrentServer(); + } + + public LevelRenderer getLevelRenderer() + { + return mc.levelRenderer; + } + + /** Returns all worlds available to the server */ + @Override + public ArrayList getAllServerWorlds() + { + ArrayList worlds = new ArrayList(); + + Iterable serverWorlds = mc.getSingleplayerServer().getAllLevels(); + for (ServerLevel world : serverWorlds) + { + worlds.add(WorldWrapper.getWorldWrapper(world)); + } + + return worlds; + } + + + + @Override + public void sendChatMessage(String string) + { + getPlayer().sendMessage(new TextComponent(string), getPlayer().getUUID()); + } + + /** + * Crashes Minecraft, displaying the given errorMessage

+ * In the following format:
+ * + * The game crashed whilst errorMessage
+ * Error: ExceptionClass: exceptionErrorMessage
+ * Exit Code: -1
+ */ + @Override + public void crashMinecraft(String errorMessage, Throwable exception) + { + ClientApi.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft..."); + CrashReport report = new CrashReport(errorMessage, exception); + Minecraft.crash(report); + } + + + + + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java new file mode 100644 index 000000000..5c02d2c24 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java @@ -0,0 +1,41 @@ +package com.seibel.lod.common.wrappers.minecraft; + +import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper; + +import net.minecraft.util.profiling.ProfilerFiller; + +/** + * @author James Seibel + * @version 11-20-2021 + */ +public class ProfilerWrapper implements IProfilerWrapper +{ + public ProfilerFiller profiler; + + public ProfilerWrapper(ProfilerFiller newProfiler) + { + profiler = newProfiler; + } + + + /** starts a new section inside the currently running section */ + @Override + public void push(String newSection) + { + profiler.push(newSection); + } + + /** ends the currently running section and starts a new one */ + @Override + public void popPush(String newSection) + { + profiler.popPush(newSection); + } + + /** ends the currently running section */ + @Override + public void pop() + { + profiler.pop(); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java new file mode 100644 index 000000000..c05c61a29 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java @@ -0,0 +1,29 @@ +package com.seibel.lod.common.wrappers.misc; + +import com.mojang.blaze3d.platform.NativeImage; +import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper; + +/** + * @author James Seibel + * @version 11-21-2021 + */ +public class LightMapWrapper implements ILightMapWrapper +{ + static NativeImage lightMap = null; + + public LightMapWrapper(NativeImage newLightMap) + { + lightMap = newLightMap; + } + + public static void setLightMap(NativeImage newLightMap) + { + lightMap = newLightMap; + } + + @Override + public int getLightValue(int skyLight, int blockLight) + { + return lightMap.getPixelRGBA(skyLight, blockLight); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java new file mode 100644 index 000000000..2e238e1ed --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java @@ -0,0 +1,59 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers.world; + +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IBiomeColorWrapperSingleton; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.common.wrappers.block.BlockPosWrapper; + +import net.minecraft.client.renderer.BiomeColors; + +/** + * @author Cola? + * @version 11-15-2021 + */ +public class BiomeColorWrapperSingleton implements IBiomeColorWrapperSingleton +{ + private static final BiomeColorWrapperSingleton instance = new BiomeColorWrapperSingleton(); + + @Override + public IBiomeColorWrapperSingleton getInstance() + { + return instance; + } + + + @Override + public int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos) + { + return BiomeColors.getAverageGrassColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos()); + } + @Override + public int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos) + { + return BiomeColors.getAverageWaterColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos()); + } + @Override + public int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos) + { + return BiomeColors.getAverageFoliageColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos()); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java new file mode 100644 index 000000000..423fb5ad4 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java @@ -0,0 +1,157 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers.world; + +import java.awt.Color; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; + +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.material.MaterialColor; + +//This class wraps the minecraft BlockPos.Mutable (and BlockPos) class +public class BiomeWrapper implements IBiomeWrapper +{ + + public static final ConcurrentMap biomeWrapperMap = new ConcurrentHashMap<>(); + private Biome biome; + + public BiomeWrapper(Biome biome) + { + this.biome = biome; + } + + static public IBiomeWrapper getBiomeWrapper(Biome biome) + { + //first we check if the biome has already been wrapped + if(biomeWrapperMap.containsKey(biome) && biomeWrapperMap.get(biome) != null) + return biomeWrapperMap.get(biome); + + + //if it hasn't been created yet, we create it and save it in the map + BiomeWrapper biomeWrapper = new BiomeWrapper(biome); + biomeWrapperMap.put(biome, biomeWrapper); + + //we return the newly created wrapper + return biomeWrapper; + } + + + /** Returns a color int for the given biome. */ + @Override + public int getColorForBiome(int x, int z) + { + int colorInt; + + switch (biome.getBiomeCategory()) + { + + case NETHER: + colorInt = Blocks.NETHERRACK.defaultBlockState().getMaterial().getColor().col; + break; + + case THEEND: + colorInt = Blocks.END_STONE.defaultBlockState().getMaterial().getColor().col; + break; + + case BEACH: + case DESERT: + colorInt = Blocks.SAND.defaultBlockState().getMaterial().getColor().col; + break; + + case EXTREME_HILLS: + colorInt = Blocks.STONE.defaultMaterialColor().col; + break; + + case MUSHROOM: + colorInt = MaterialColor.COLOR_LIGHT_GRAY.col; + break; + + case ICY: + colorInt = Blocks.SNOW.defaultMaterialColor().col; + break; + + case MESA: + colorInt = Blocks.RED_SAND.defaultMaterialColor().col; + break; + + case OCEAN: + case RIVER: + colorInt = biome.getWaterColor(); + break; + + case NONE: + case FOREST: + case TAIGA: + case JUNGLE: + case PLAINS: + case SAVANNA: + case SWAMP: + default: + Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); + tmp = tmp.darker(); + colorInt = LodUtil.colorToInt(tmp); + break; + + } + + return colorInt; + } + + @Override + public int getGrassTint(int x, int z) + { + return biome.getGrassColor(x, z); + } + + @Override + public int getFolliageTint() + { + return biome.getFoliageColor(); + } + + @Override + public int getWaterTint() + { + return biome.getWaterColor(); + } + + + @Override public boolean equals(Object obj) + { + if (this == obj) + return true; + if (!(obj instanceof BiomeWrapper)) + return false; + BiomeWrapper that = (BiomeWrapper) obj; + return Objects.equals(biome, that.biome); + } + + @Override public int hashCode() + { + return Objects.hash(biome); + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java new file mode 100644 index 000000000..f59e490f8 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers.world; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; + +import net.minecraft.world.level.dimension.DimensionType; + +/** + * + * @author James Seibel + * @version 11-21-2021 + */ +public class DimensionTypeWrapper implements IDimensionTypeWrapper +{ + private static final ConcurrentMap dimensionTypeWrapperMap = new ConcurrentHashMap<>(); + private DimensionType dimensionType; + + public DimensionTypeWrapper(DimensionType dimensionType) + { + this.dimensionType = dimensionType; + } + + public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType) + { + //first we check if the biome has already been wrapped + if(dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null) + return dimensionTypeWrapperMap.get(dimensionType); + + + //if it hasn't been created yet, we create it and save it in the map + DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType); + dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper); + + //we return the newly created wrapper + return dimensionTypeWrapper; + } + + public static void clearMap() + { + dimensionTypeWrapperMap.clear(); + } + + + @Override + public String getDimensionName() + { + return dimensionType.effectsLocation().getPath(); + } + + @Override + public boolean hasCeiling() + { + return dimensionType.hasCeiling(); + } + + @Override + public boolean hasSkyLight() + { + return dimensionType.hasSkyLight(); + } +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java new file mode 100644 index 000000000..6c1bce99d --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java @@ -0,0 +1,174 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers.world; + +import java.io.File; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.seibel.lod.core.enums.WorldType; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.common.wrappers.block.BlockPosWrapper; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LightLayer; +import org.jetbrains.annotations.Nullable; + +/** + * @author James Seibel + * @author ?? + * @version 11-21-2021 + */ +public class WorldWrapper implements IWorldWrapper +{ + private static final ConcurrentMap worldWrapperMap = new ConcurrentHashMap<>(); + private final LevelAccessor world; + public final WorldType worldType; + + + public WorldWrapper(LevelAccessor newWorld) + { + world = newWorld; + + if (world.getClass() == ServerLevel.class) + worldType = WorldType.ServerWorld; + else if (world.getClass() == ClientLevel.class) + worldType = WorldType.ClientWorld; + else + worldType = WorldType.Unknown; + } + + + @Nullable + public static WorldWrapper getWorldWrapper(LevelAccessor world) + { + if (world == null) return null; + //first we check if the biome has already been wrapped + if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null) + return worldWrapperMap.get(world); + + + //if it hasn't been created yet, we create it and save it in the map + WorldWrapper worldWrapper = new WorldWrapper(world); + worldWrapperMap.put(world, worldWrapper); + + //we return the newly created wrapper + return worldWrapper; + } + + public static void clearMap() + { + worldWrapperMap.clear(); + } + + @Override + public WorldType getWorldType() + { + return worldType; + } + + @Override + public DimensionTypeWrapper getDimensionType() + { + return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType()); + } + + @Override + public int getBlockLight(AbstractBlockPosWrapper blockPos) + { + return world.getBrightness(LightLayer.BLOCK, ((BlockPosWrapper) blockPos).getBlockPos()); + } + + @Override + public int getSkyLight(AbstractBlockPosWrapper blockPos) + { + return world.getBrightness(LightLayer.SKY, ((BlockPosWrapper) blockPos).getBlockPos()); + } + + @Override + public IBiomeWrapper getBiome(AbstractBlockPosWrapper blockPos) + { + return BiomeWrapper.getBiomeWrapper(world.getBiome(((BlockPosWrapper) blockPos).getBlockPos())); + } + + public LevelAccessor getWorld() + { + return world; + } + + @Override + public boolean hasCeiling() + { + return world.dimensionType().hasCeiling(); + } + + @Override + public boolean hasSkyLight() + { + return world.dimensionType().hasSkyLight(); + } + + @Override + public boolean isEmpty() + { + return world == null; + } + + @Override + public int getHeight() + { + return world.getHeight(); + } + + /** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */ + @Override + public File getSaveFolder() throws UnsupportedOperationException + { + if (worldType != WorldType.ServerWorld) + throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds."); + + ServerChunkCache chunkSource = ((ServerLevel) world).getChunkSource(); + return chunkSource.getDataStorage().dataFolder; + } + + + /** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */ + public ServerLevel getServerWorld() throws UnsupportedOperationException + { + if (worldType != WorldType.ServerWorld) + throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds."); + + return (ServerLevel) world; + } + + @Override + public int getSeaLevel() + { + // TODO this is depreciated, what should we use instead? + return world.getSeaLevel(); + } + + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java new file mode 100644 index 000000000..4bb09f586 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java @@ -0,0 +1,357 @@ +/* + * 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 . + */ + +package com.seibel.lod.common.wrappers.worldGeneration; + +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.common.wrappers.WrapperUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.SectionPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.EmptyTickList; +import net.minecraft.world.level.TickList; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.feature.StructureFeature; +import net.minecraft.world.level.levelgen.structure.StructureStart; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.phys.AABB; +import org.jetbrains.annotations.Nullable; + + +/** + * This is a fake ServerWorld used when generating features. + * It allows us to keep each LodChunk generation independent + * of the actual ServerWorld, allowing + * multithread generation. + * + * @author James Seibel + * @version 7-26-2021 + */ +public class LodServerWorld implements WorldGenLevel +{ + + public HashMap heightmaps = new HashMap<>(); + + public final ChunkAccess chunk; + + public final ServerLevel serverWorld; + + public LodServerWorld(ServerLevel newServerWorld, ChunkAccess newChunk) + { + chunk = newChunk; + serverWorld = newServerWorld; + } + + + @Override + public int getHeight(Heightmap.Types heightmapType, int x, int z) + { + // make sure the block position is set relative to the chunk + x = x % LodUtil.CHUNK_WIDTH; + x = (x < 0) ? x + 16 : x; + + z = z % LodUtil.CHUNK_WIDTH; + z = (z < 0) ? z + 16 : z; + + return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z); + } + + @Override + public Biome getBiome(BlockPos pos) + { + return chunk.getBiomes().getNoiseBiome(pos.getX() >> 2, pos.getY() >> 2, pos.getZ() >> 2); + } + + @Override + public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) + { + return chunk.setBlockState(pos, state, false) == state; + } + + @Override + public BlockState getBlockState(BlockPos pos) + { + return chunk.getBlockState(pos); + } + + @Override + public FluidState getFluidState(BlockPos pos) + { + return chunk.getFluidState(pos); + } + + + @Override + public boolean isStateAtPosition(BlockPos pos, Predicate state) + { + return state.test(chunk.getBlockState(pos)); + } + + @Override + public boolean isFluidAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(chunk.getFluidState(blockPos)); + } + + @Override + public TickList getBlockTicks() + { + return EmptyTickList.empty(); + } + + @Override + public ChunkAccess getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull) + { + return chunk; + } + + @Override + public Stream> startsForFeature(SectionPos p_241827_1_, StructureFeature p_241827_2_) + { + return serverWorld.startsForFeature(p_241827_1_, p_241827_2_); + } + + @Override + public TickList getLiquidTicks() + { + return EmptyTickList.empty(); + } + + @Override + public LevelLightEngine getLightEngine() + { + return new LevelLightEngine(null, false, false); + } + + @Override + public long getSeed() + { + return serverWorld.getSeed(); + } + + @Override + public RegistryAccess registryAccess() + { + return serverWorld.registryAccess(); + } + + + /** + * All methods below shouldn't be needed + * and thus have been left unimplemented. + */ + + + @Override + public Random getRandom() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public void playSound(Player player, BlockPos pos, SoundEvent soundIn, SoundSource category, float volume, + float pitch) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public void addParticle(ParticleOptions particleData, double x, double y, double z, double xSpeed, double ySpeed, + double zSpeed) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public BiomeManager getBiomeManager() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public int getSeaLevel() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public float getShade(Direction p_230487_1_, boolean p_230487_2_) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public WorldBorder getWorldBorder() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean removeBlock(BlockPos pos, boolean isMoving) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public ServerLevel getLevel() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public ChunkSource getChunkSource() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Nullable + @Override + public MinecraftServer getServer() { + return serverWorld.getServer(); + } + + + @Override + public LevelData getLevelData() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public void levelEvent(Player arg0, int arg1, BlockPos arg2, int arg3) + { + throw new UnsupportedOperationException("Not Implemented"); + + } + + // TODO: Check if this causes any issues + @Override + public void gameEvent(@Nullable Entity entity, GameEvent gameEvent, BlockPos blockPos) { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public List getEntities(Entity arg0, AABB arg1, Predicate arg2) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + @Override + public List getEntities(EntityTypeTest entityTypeTest, AABB aABB, Predicate predicate) { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public List getEntitiesOfClass(Class arg0, AABB arg1, + Predicate arg2) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public List players() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public int getSkyDarken() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public boolean isClientSide() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public DimensionType dimensionType() + { + throw new UnsupportedOperationException("Not Implemented"); + } + + + @Override + public BlockEntity getBlockEntity(BlockPos p_175625_1_) + { + throw new UnsupportedOperationException("Not Implemented"); + } + +} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java new file mode 100644 index 000000000..7935d109e --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java @@ -0,0 +1,391 @@ +package com.seibel.lod.common.wrappers.worldGeneration; + +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Supplier; + +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig; +import com.seibel.lod.core.enums.config.DistanceGenerationMode; +import com.seibel.lod.core.objects.lod.LodDimension; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.util.SingletonHandler; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper; +import com.seibel.lod.common.wrappers.WrapperUtil; +import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper; +import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; +import com.seibel.lod.common.wrappers.world.WorldWrapper; + +import net.minecraft.core.Registry; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.chunk.*; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.SnowAndFreezeFeature; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; + +/** + * @author James Seibel + * @version 11-13-2021 + */ +public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper +{ + private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + + /** + * If a configured feature fails for whatever reason, + * add it to this list. This will hopefully remove any + * features that could cause issues down the line. + */ + private static final ConcurrentHashMap> FEATURES_TO_AVOID = new ConcurrentHashMap<>(); + + private static ExecutorService Executor = Executors.newSingleThreadExecutor(); + + + public final ServerLevel serverWorld; + public final LodDimension lodDim; + public final LodBuilder lodBuilder; + + public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) + { + super(newLodBuilder, newLodDimension, worldWrapper); + + lodBuilder = newLodBuilder; + lodDim = newLodDimension; + serverWorld = ((WorldWrapper) worldWrapper).getServerWorld(); + } + + + + + + + + /** takes about 2-5 ms */ + @Override + public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode) + { + List chunkList = new LinkedList<>(); + ProtoChunk chunk = new ProtoChunk(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY, serverWorld); + chunkList.add(chunk); + + ServerChunkCache chunkSource = serverWorld.getChunkSource(); + ChunkGenerator chunkGen = chunkSource.getGenerator(); + + // generate the terrain (this is thread safe) + ChunkStatus.EMPTY.generate(Executor, serverWorld, chunkGen, serverWorld.getStructureManager(), (ThreadedLevelLightEngine) serverWorld.getLightEngine(), null, chunkList); + // override the chunk status, so we can run the next generator stage + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + + + + + // generate fake height data for this LOD + int seaLevel = serverWorld.getSeaLevel(); + + boolean simulateHeight = generationMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; + boolean inTheEnd = false; + + // add fake heightmap data so our LODs aren't at height 0 + Heightmap heightmap = new Heightmap(chunk, WrapperUtil.DEFAULT_HEIGHTMAP); + for (int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++) + { + for (int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++) + { + if (simulateHeight) + { + // these heights are of course aren't super accurate, + // they are just to simulate height data where there isn't any + switch (chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory()) + { + case NETHER: + heightmap.setHeight(x, z, serverWorld.getHeight() / 2); + break; + + case EXTREME_HILLS: + heightmap.setHeight(x, z, seaLevel + 30); + break; + case MESA: + case JUNGLE: + heightmap.setHeight(x, z, seaLevel + 20); + break; + case BEACH: + heightmap.setHeight(x, z, seaLevel + 5); + break; + case NONE: + heightmap.setHeight(x, z, 0); + break; + + case OCEAN: + case RIVER: + heightmap.setHeight(x, z, seaLevel); + break; + + case THEEND: + inTheEnd = true; + break; + + // DESERT + // FOREST + // ICY + // MUSHROOM + // SAVANNA + // SWAMP + // TAIGA + // PLAINS + default: + heightmap.setHeight(x, z, seaLevel + 10); + break; + }// heightmap switch + } + else + { + // we aren't simulating height + // always use sea level + heightmap.setHeight(x, z, seaLevel); + } + }// z + }// x + + chunk.setHeightmap(WrapperUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData()); + + + if (!inTheEnd) + { + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false)); + } + else + { + // if we are in the end, don't generate any chunks. + // Since we don't know where the islands are, everything + // generates the same, and it looks awful. + //TODO it appears that 'if' can be collapsed, but comment says that it should not be a case + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false)); + } + + +// long startTime = System.currentTimeMillis(); +// long endTime = System.currentTimeMillis(); +// System.out.println(endTime - startTime); + } + + + /** takes about 10 - 20 ms */ + @Override + public void generateSurface(AbstractChunkPosWrapper pos) + { + List chunkList = new LinkedList<>(); + ProtoChunk chunk = new ProtoChunk(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY, serverWorld); + chunkList.add(chunk); + LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); + + ServerChunkCache chunkSource = serverWorld.getChunkSource(); + ThreadedLevelLightEngine lightEngine = (ThreadedLevelLightEngine) serverWorld.getLightEngine(); + StructureManager templateManager = serverWorld.getStructureManager(); + ChunkGenerator chunkGen = chunkSource.getGenerator(); + + + // generate the terrain (this is thread safe) + ChunkStatus.EMPTY.generate(Executor, serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + // override the chunk status, so we can run the next generator stage + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); + ChunkStatus.NOISE.generate(Executor, serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + // TODO[FABRIC]: Find whay this dosnt work + ChunkStatus.SURFACE.generate(Executor, serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + + // this feature has been proven to be thread safe, + // so we will add it +// SnowAndFreezeFeature snowFeature = new SnowAndFreezeFeature(NoFeatureConfig.CODEC); +// snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null); + + + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.SURFACE)); + + /*TODO if we want to use Biome utils and terrain utils for overworld + * lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/ + } + + + /** + * takes about 15 - 20 ms + *

+ * Causes concurrentModification Exceptions, + * which could cause instability or world generation bugs + */ + @Override + public void generateFeatures(AbstractChunkPosWrapper pos) + { + List chunkList = new LinkedList<>(); + ProtoChunk chunk = new ProtoChunk(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY, serverWorld); + chunkList.add(chunk); + LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); + + ServerChunkCache chunkSource = serverWorld.getChunkSource(); + ThreadedLevelLightEngine lightEngine = (ThreadedLevelLightEngine) serverWorld.getLightEngine(); + StructureManager templateManager = serverWorld.getStructureManager(); + ChunkGenerator chunkGen = chunkSource.getGenerator(); + + + // generate the terrain (this is thread safe) + ChunkStatus.EMPTY.generate(Executor, serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + // override the chunk status, so we can run the next generator stage + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); + ChunkStatus.NOISE.generate(Executor, serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + // TODO[FABRIC]: Find whay this dosnt work + ChunkStatus.SURFACE.generate(Executor, serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + + + // get all the biomes in the chunk + HashSet biomes = new HashSet<>(); + for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) + { + for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) + { + Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2); + + // Issue #35 + // For some reason Jungle biomes cause incredible lag + // the features here must be interacting with each other + // in unpredictable ways (specifically tree feature generation). + // When generating Features my CPU usage generally hovers around 30 - 40% + // when generating Jungles it spikes to 100%. + if (biome.getBiomeCategory() != Biome.BiomeCategory.JUNGLE) + { + // should probably use the heightmap here instead of seaLevel, + // but this seems to get the job done well enough + biomes.add(biome); + } + } + } + + boolean allowUnstableFeatures = CONFIG.client().worldGenerator().getAllowUnstableFeatureGeneration(); + + // generate all the features related to this chunk. + // this may or may not be thread safe + for (Biome biome : biomes) + { + List>>> featuresForState = biome.generationSettings.features(); + + for (List>> suppliers : featuresForState) + { + for (Supplier> featureSupplier : suppliers) + { + ConfiguredFeature configuredFeature = featureSupplier.get(); + + if (!allowUnstableFeatures && + FEATURES_TO_AVOID.containsKey(configuredFeature.hashCode())) + continue; + + + try + { + configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition()); + } + catch (ConcurrentModificationException | UnsupportedOperationException e) + { + // This will happen. I'm not sure what to do about it + // except pray that it doesn't affect the normal world generation + // in any harmful way. + // Update: this can cause crashes and high CPU usage. + + // Issue #35 + // I tried cloning the config for each feature, but that + // path was blocked since I can't clone lambda methods. + // I tried using a deep cloning library and discovered + // the problem there. + // ( https://github.com/kostaskougios/cloning + // and + // https://github.com/EsotericSoftware/kryo ) + + if (!allowUnstableFeatures) + FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature); +// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); + } + // This will happen when the LodServerWorld + // isn't able to return something that a feature + // generator needs + catch (Exception e) + { + // I'm not sure what happened, print to the log + + e.printStackTrace(); + + if (!allowUnstableFeatures) + FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature); +// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); + } + } + } + } + + // generate a Lod like normal + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.FEATURES)); + } + + + /** + * Generates using MC's ServerWorld. + *

+ * on pre generated chunks 0 - 1 ms
+ * on un generated chunks 0 - 50 ms
+ * with the median seeming to hover around 15 - 30 ms
+ * and outliers in the 100 - 200 ms range
+ *

+ * Note this should not be multithreaded and does cause server/simulation lag + * (Higher lag for generating than loading) + */ + @Override + public void generateFull(AbstractChunkPosWrapper pos) + { + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.FULL)); + } + + + + + + + + /* + * performance/generation tests related to + * serverWorld.getChunk(x, z, ChunkStatus. *** ) + + true/false is whether they generated blocks or not + the time is how long it took to generate + + ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P) + ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks) + ChunkStatus.BIOMES 1 - 10 ms false (no height) + ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone) + ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass) + ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass) + ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass) + ChunkStatus.FEATURES 7 - 25 ms true + ChunkStatus.HEIGHTMAPS 20 - 40 ms true + ChunkStatus.LIGHT 20 - 40 ms true + ChunkStatus.FULL 30 - 50 ms true + ChunkStatus.SPAWN 50 - 80 ms true + + + At this point I would suggest using FEATURES, as it generates snow and trees + (and any other object that are needed to make biomes distinct) + + Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much) + */ +} diff --git a/common/src/main/java/net/minecraftforge/client/model/data/IModelData.java b/common/src/main/java/net/minecraftforge/client/model/data/IModelData.java new file mode 100644 index 000000000..618552463 --- /dev/null +++ b/common/src/main/java/net/minecraftforge/client/model/data/IModelData.java @@ -0,0 +1,44 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.model.data; + +import org.jetbrains.annotations.Nullable; + +public interface IModelData +{ + /** + * Check if this data has a property, even if the value is {@code null}. Can be + * used by code that intends to fill in data for a render pipeline, such as the + * forge animation system. + *

+ * IMPORTANT: {@link #getData(ModelProperty)} can return {@code null} + * even if this method returns {@code true}. + * + * @param prop The property to check for inclusion in this model data + * @return {@code true} if this data has the given property, even if no value is present + */ + boolean hasProperty(ModelProperty prop); + + @Nullable + T getData(ModelProperty prop); + + @Nullable + T setData(ModelProperty prop, T data); +} diff --git a/common/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java b/common/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java new file mode 100644 index 000000000..3d57c1c87 --- /dev/null +++ b/common/src/main/java/net/minecraftforge/client/model/data/ModelDataMap.java @@ -0,0 +1,82 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.model.data; + +import java.util.IdentityHashMap; +import java.util.Map; + +import com.google.common.base.Preconditions; + +public class ModelDataMap implements IModelData +{ + private final Map, Object> backingMap; + + private ModelDataMap(Map, Object> map) + { + this.backingMap = new IdentityHashMap<>(map); + } + + protected ModelDataMap() + { + this.backingMap = new IdentityHashMap<>(); + } + + @Override + public boolean hasProperty(ModelProperty prop) + { + return backingMap.containsKey(prop); + } + + @SuppressWarnings("unchecked") + @Override + public T getData(ModelProperty prop) + { + return (T) backingMap.get(prop); + } + + @SuppressWarnings("unchecked") + @Override + public T setData(ModelProperty prop, T data) + { + Preconditions.checkArgument(prop.test(data), "Value is invalid for this property"); + return (T) backingMap.put(prop, data); + } + + public static class Builder + { + private final Map, Object> defaults = new IdentityHashMap<>(); + + public Builder withProperty(ModelProperty prop) + { + return withInitial(prop, null); + } + + public Builder withInitial(ModelProperty prop, T data) + { + this.defaults.put(prop, data); + return this; + } + + public ModelDataMap build() + { + return new ModelDataMap(defaults); + } + } +} diff --git a/common/src/main/java/net/minecraftforge/client/model/data/ModelProperty.java b/common/src/main/java/net/minecraftforge/client/model/data/ModelProperty.java new file mode 100644 index 000000000..57071d26d --- /dev/null +++ b/common/src/main/java/net/minecraftforge/client/model/data/ModelProperty.java @@ -0,0 +1,42 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library 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 2.1 + * of the License. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.model.data; + +import java.util.function.Predicate; + +import com.google.common.base.Predicates; + +public class ModelProperty implements Predicate { + + private final Predicate pred; + + public ModelProperty() { + this(Predicates.alwaysTrue()); + } + + public ModelProperty(Predicate pred) { + this.pred = pred; + } + + @Override + public boolean test(T t) { + return pred.test(t); + } +} diff --git a/fabric/src/main/java/com/seibel/lod/fabric/ClientProxy.java b/fabric/src/main/java/com/seibel/lod/fabric/ClientProxy.java index d7e6abef6..4771c2f48 100644 --- a/fabric/src/main/java/com/seibel/lod/fabric/ClientProxy.java +++ b/fabric/src/main/java/com/seibel/lod/fabric/ClientProxy.java @@ -100,7 +100,9 @@ public class ClientProxy /** This is also called when a new dimension loads */ public void worldLoadEvent(Level level) { - eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(level)); + if (level != null) { + eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(level)); + } } public void worldUnloadEvent() diff --git a/fabric/src/main/java/com/seibel/lod/fabric/Main.java b/fabric/src/main/java/com/seibel/lod/fabric/Main.java index 0623366c2..3864c871b 100644 --- a/fabric/src/main/java/com/seibel/lod/fabric/Main.java +++ b/fabric/src/main/java/com/seibel/lod/fabric/Main.java @@ -19,6 +19,7 @@ package com.seibel.lod.fabric; +import com.seibel.lod.common.LodCommonMain; import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.fabric.wrappers.DependencySetup; @@ -35,7 +36,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; * * @author coolGi2007 * @author Ran - * @version 11-21-2021 + * @version 12-1-2021 */ public class Main implements ClientModInitializer { @@ -58,6 +59,7 @@ public class Main implements ClientModInitializer // This loads the mod after minecraft loads which doesn't causes a lot of issues public static void init() { + LodCommonMain.startup(null); DependencySetup.createInitialBindings(); ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION); diff --git a/fabric/src/main/java/com/seibel/lod/fabric/wrappers/DependencySetup.java b/fabric/src/main/java/com/seibel/lod/fabric/wrappers/DependencySetup.java index a315fca69..ac4a3bf07 100644 --- a/fabric/src/main/java/com/seibel/lod/fabric/wrappers/DependencySetup.java +++ b/fabric/src/main/java/com/seibel/lod/fabric/wrappers/DependencySetup.java @@ -20,18 +20,13 @@ import com.seibel.lod.fabric.wrappers.minecraft.MinecraftWrapper; * are loaded. * * @author James Seibel - * @version 11-20-2021 + * @author Ran + * @version 12-1-2021 */ public class DependencySetup { public static void createInitialBindings() { - SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE); - SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE); - SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE); - SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); - SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); - - SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions())); + SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE);; } } diff --git a/forge/src/main/java/com/seibel/lod/LodMain.java b/forge/src/main/java/com/seibel/lod/LodMain.java index 8bec692f5..c3ce62773 100644 --- a/forge/src/main/java/com/seibel/lod/LodMain.java +++ b/forge/src/main/java/com/seibel/lod/LodMain.java @@ -19,10 +19,18 @@ package com.seibel.lod; +import com.seibel.lod.common.LodCommonMain; +import com.seibel.lod.common.forge.LodForgeMethodCaller; +import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper; import com.seibel.lod.forge.ForgeClientProxy; import com.seibel.lod.forge.ForgeConfig; import com.seibel.lod.forge.wrappers.ForgeDependencySetup; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.data.ModelDataMap; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModLoadingContext; @@ -33,6 +41,9 @@ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fmlserverevents.FMLServerStartedEvent; +import java.util.List; +import java.util.Random; + /** * Initialize and setup the Mod. *
@@ -43,7 +54,7 @@ import net.minecraftforge.fmlserverevents.FMLServerStartedEvent; * @version 7-3-2021 */ @Mod(ModInfo.ID) -public class LodMain +public class LodMain implements LodForgeMethodCaller { public static LodMain instance; @@ -52,6 +63,7 @@ public class LodMain private void init(final FMLCommonSetupEvent event) { + LodCommonMain.startup(this); ForgeDependencySetup.createInitialBindings(); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ForgeConfig.CLIENT_SPEC); } @@ -80,5 +92,9 @@ public class LodMain { // this is called when the server starts } - + + @Override + public List getQuads(MinecraftWrapper mc, Block block, BlockState blockState, Direction direction, Random random, ModelDataMap dataMap) { + return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap); + } } diff --git a/forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java b/forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java index f5267f435..49b92fcd9 100644 --- a/forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java +++ b/forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java @@ -20,18 +20,13 @@ import com.seibel.lod.forge.wrappers.minecraft.MinecraftWrapper; * are loaded. * * @author James Seibel - * @version 11-20-2021 + * @author Ran + * @version 12-1-2021 */ public class ForgeDependencySetup { public static void createInitialBindings() { SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE); - SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE); - SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE); - SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); - SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); - - SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions())); } }