Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b6b108a287 | |||
| ab4ef429db | |||
| 86473e022e | |||
| fd89f569d0 | |||
| eefc765823 | |||
| ebccb2516b | |||
| 8c62a40da9 | |||
| d56af5c38f | |||
| 39b1ec61ba | |||
| cb613cf7df | |||
| 28e33b4c36 | |||
| 855e6b8180 | |||
| d62161f529 | |||
| 71d48411f1 | |||
| 731842e09c | |||
| 61169f87c0 | |||
| 9fb3b196d2 | |||
| 867b875cf9 | |||
| 3875c8c4ce | |||
| 89b959d3f5 | |||
| d62e50d6f4 | |||
| 16836a2b49 | |||
| f5651f26a5 | |||
| 82ff59c857 | |||
| 8af61041f0 | |||
| 2a9136b56f | |||
| 64da6c811d | |||
| e6b93e0d92 | |||
| f874219a64 | |||
| b4822740f4 | |||
| af205a50b4 | |||
| 2f6eaf79bd | |||
| 625f1e700f | |||
| 897d5b0b11 | |||
| 95641e2f4e | |||
| cd856b86c7 | |||
| c00aa6d627 | |||
| 398a3fb0bc | |||
| e0fa638ad9 | |||
| 4e42cbd4ce | |||
| b6c6be77cd | |||
| 0964293a72 | |||
| c8b6141ce0 | |||
| 948540369f | |||
| 363df0ad6f | |||
| a37e105434 | |||
| aeea0c00c3 | |||
| 137352674e | |||
| 4734552954 | |||
| 879c2f1ec4 | |||
| 7dc9d2a352 | |||
| cabc470ebd | |||
| 0bf1f493aa | |||
| 705bd14ee4 | |||
| 155955e49b | |||
| c76a793b18 | |||
| 50cc8501a0 | |||
| 209279e3e4 | |||
| 41239572a5 | |||
| 106ab47c3d | |||
| a84f9b60e5 | |||
| 4481e8634a | |||
| 3e432682fb | |||
| 05569c03a4 | |||
| 2a37bec8ff | |||
| 2d567b84be | |||
| e2a378250f | |||
| e2083a1836 | |||
| 334946ab59 | |||
| 8c9bb98125 | |||
| 726f0f3d3c | |||
| 50e5898692 | |||
| de05a5f674 | |||
| 31b57fae50 | |||
| 2f686057f3 | |||
| 132251341f | |||
| 2bac5f933a | |||
| 2e565aa83a | |||
| 4e9d0f4861 | |||
| 7216b193e8 | |||
| c33a5bf814 | |||
| 97756a5196 | |||
| 377f7d23e3 | |||
| 7005202384 | |||
| 99e8f57bac | |||
| afddf4168e | |||
| fbffdc0c9f | |||
| e6d3647490 | |||
| 13363ff363 | |||
| 7f98e4b1eb | |||
| 408460b0ae | |||
| b69ef5835d | |||
| 0428fa0912 | |||
| 9f3124fa56 | |||
| fbbdab73c6 | |||
| ee9441c521 | |||
| a9e0fd5d9b | |||
| 98464889ca | |||
| eed5fd60c6 | |||
| ac43cd5496 | |||
| 1f16a7c808 | |||
| 39e4c70754 | |||
| 82eb27af4c | |||
| 3aaab94b39 | |||
| 07a0779ca4 | |||
| 2adba02a38 | |||
| 9dd76db3fc | |||
| 97dacf2429 | |||
| 1c189e162a | |||
| f7a0fff869 | |||
| 2f985d0926 | |||
| 2a3c544fba | |||
| 09d133b994 | |||
| 26a4223ecf | |||
| e2943fdcaf | |||
| f1053251b4 | |||
| be1dcaf43c | |||
| a899d988fc | |||
| 06b5b2c514 | |||
| 864a19b79f | |||
| 8974323406 | |||
| 46c9e0103a | |||
| 02203466ed | |||
| 87b22ea1cc | |||
| d26327a930 | |||
| 469d2bdcb7 | |||
| 5516603a0c | |||
| b737adc3da | |||
| f3a8afeee3 | |||
| a4501f86e9 | |||
| f0f864d824 | |||
| 5abbff42d8 | |||
| 53e2c4f806 | |||
| cb4c32e0a9 | |||
| c620086ed1 | |||
| 513cecf318 | |||
| 749921b436 | |||
| 0d565895e2 | |||
| 2c6849a0fd | |||
| 4d53ec5b64 | |||
| 4467af4798 | |||
| d3f953686a | |||
| 67901d82c5 | |||
| 013fbf638a |
+1
-1
@@ -30,7 +30,7 @@ build:
|
||||
stage: build
|
||||
parallel:
|
||||
matrix:
|
||||
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21"]
|
||||
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1"]
|
||||
script:
|
||||
# this both runs the unit tests and assembles the code
|
||||
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.seibel.distanthorizons.common.commonMixins;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.ServerApi;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
public class MixinChunkMapCommon
|
||||
{
|
||||
|
||||
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
|
||||
{
|
||||
// is this position already being updated?
|
||||
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(chunk.getPos().x, chunk.getPos().z))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// is this chunk being saved to disk?
|
||||
boolean savingChunkToDisk = ci.getReturnValue();
|
||||
// true means a chunk was saved to disk
|
||||
if (!savingChunkToDisk)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO are the following validations necessary since we are checking above if
|
||||
// the callback return value should state if the chunk was actually saved or not?
|
||||
// Do we trust it to always be correct?
|
||||
|
||||
|
||||
|
||||
// corrupt/incomplete chunk validation //
|
||||
|
||||
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
|
||||
// this logic should prevent that from happening
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// biome validation //
|
||||
|
||||
// some chunks may be missing their biomes, which cause issues when attempting to save them
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
if (chunk.getBiomes() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
try
|
||||
{
|
||||
// this will throw an exception if the biomes aren't set up
|
||||
chunk.getNoiseBiome(0,0,0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// submit the update event
|
||||
ServerApi.INSTANCE.serverChunkSaveEvent(
|
||||
new ChunkWrapper(chunk, level, ServerLevelWrapper.getWrapper(level)),
|
||||
ServerLevelWrapper.getWrapper(level)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+6
-39
@@ -20,13 +20,11 @@
|
||||
package com.seibel.distanthorizons.common.wrappers;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -137,41 +135,10 @@ public class McObjectConverter
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockPos Convert(DhBlockPos wrappedPos)
|
||||
{
|
||||
return new BlockPos(wrappedPos.x, wrappedPos.y, wrappedPos.z);
|
||||
}
|
||||
public static ChunkPos Convert(DhChunkPos wrappedPos)
|
||||
{
|
||||
return new ChunkPos(wrappedPos.x, wrappedPos.z);
|
||||
}
|
||||
public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); }
|
||||
public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); }
|
||||
|
||||
public static Direction Convert(EDhDirection lodDirection)
|
||||
{
|
||||
return directions[lodDirection.ordinal()];
|
||||
}
|
||||
public static EDhDirection Convert(Direction direction)
|
||||
{
|
||||
return lodDirections[direction.ordinal()];
|
||||
}
|
||||
public static void DebugCheckAllPackers()
|
||||
{
|
||||
BiConsumer<Integer, Integer> func = (x, z) -> DhChunkPos._DebugCheckPacker(x, z, ChunkPos.asLong(x, z));
|
||||
func.accept(0, 0);
|
||||
func.accept(12345, 134);
|
||||
func.accept(-12345, -134);
|
||||
func.accept(-30000000 / 16, 30000000 / 16);
|
||||
func.accept(30000000 / 16, -30000000 / 16);
|
||||
func.accept(30000000 / 16, 30000000 / 16);
|
||||
func.accept(-30000000 / 16, -30000000 / 16);
|
||||
Consumer<BlockPos> func2 = (p) -> DhBlockPos._DebugCheckPacker(p.getX(), p.getY(), p.getZ(), p.asLong());
|
||||
func2.accept(new BlockPos(0, 0, 0));
|
||||
func2.accept(new BlockPos(12345, 134, 123));
|
||||
func2.accept(new BlockPos(-12345, -134, -80));
|
||||
func2.accept(new BlockPos(-30000000, 2047, 30000000));
|
||||
func2.accept(new BlockPos(30000000, -2048, -30000000));
|
||||
func2.accept(new BlockPos(30000000, 2047, 30000000));
|
||||
func2.accept(new BlockPos(-30000000, -2048, -30000000));
|
||||
}
|
||||
public static Direction Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; }
|
||||
public static EDhDirection Convert(Direction direction) { return lodDirections[direction.ordinal()]; }
|
||||
|
||||
}
|
||||
|
||||
+39
-38
@@ -55,9 +55,6 @@ import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* This handles creating abstract wrapper objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-12-5
|
||||
*/
|
||||
public class WrapperFactory implements IWrapperFactory
|
||||
{
|
||||
@@ -82,6 +79,27 @@ public class WrapperFactory implements IWrapperFactory
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
|
||||
{
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("levelWrapper must be returned by DH and of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
|
||||
return BiomeWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
|
||||
}
|
||||
@Override
|
||||
public IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
|
||||
{
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
|
||||
return BlockStateWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); }
|
||||
@Override
|
||||
@@ -104,6 +122,13 @@ public class WrapperFactory implements IWrapperFactory
|
||||
|
||||
@Override
|
||||
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
|
||||
@Override
|
||||
public HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
|
||||
|
||||
@Override
|
||||
public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); }
|
||||
@Override
|
||||
public void resetRendererIgnoredBlocksSet() { BlockStateWrapper.clearRendererIgnoredBlocks(); }
|
||||
|
||||
|
||||
/**
|
||||
@@ -130,7 +155,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
}
|
||||
}
|
||||
|
||||
#if MC_VER <= MC_1_21
|
||||
//#if MC_VER <= MC_1_XX_X
|
||||
else if (objectArray.length == 2)
|
||||
{
|
||||
// correct number of parameters from the API
|
||||
@@ -176,16 +201,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
{
|
||||
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
|
||||
}
|
||||
#else
|
||||
// Intentional compiler error to bring attention to the missing wrapper function.
|
||||
// If you need to work on an unimplemented version but don't have the ability to implement this yet
|
||||
// you can comment it out, but please don't commit it. Someone will have to implement it.
|
||||
|
||||
// After implementing the new version please read this method's javadocs for instructions
|
||||
// on what other locations also need to be updated, the DhAPI specifically needs to
|
||||
// be updated to state which objects this method accepts.
|
||||
not implemented for this version of Minecraft!
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
/**
|
||||
* Note: when this is updated for different MC versions,
|
||||
@@ -195,16 +211,13 @@ public class WrapperFactory implements IWrapperFactory
|
||||
{
|
||||
String[] expectedClassNames;
|
||||
|
||||
#if MC_VER <= MC_1_21
|
||||
//#if MC_VER <= MC_1_XX_X
|
||||
expectedClassNames = new String[]
|
||||
{
|
||||
ChunkAccess.class.getName(),
|
||||
ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName()
|
||||
};
|
||||
#else
|
||||
// See preprocessor comment in createChunkWrapper() for full documentation
|
||||
not implemented for this version of Minecraft!
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
|
||||
}
|
||||
@@ -222,7 +235,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
// confirm the API level wrapper is also a Core wrapper
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("Unable to cast... only DH provided IDhApiLevelWrapper's can be used."); // TODO
|
||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
|
||||
|
||||
@@ -243,7 +256,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
|
||||
Biome biome = (Biome) objectArray[0];
|
||||
return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper);
|
||||
#elif MC_VER <= MC_1_21
|
||||
#else
|
||||
if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome))
|
||||
{
|
||||
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
|
||||
@@ -251,9 +264,6 @@ public class WrapperFactory implements IWrapperFactory
|
||||
|
||||
Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0];
|
||||
return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper);
|
||||
#else
|
||||
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
|
||||
not implemented for this version of Minecraft!
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
@@ -266,11 +276,8 @@ public class WrapperFactory implements IWrapperFactory
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
expectedClassNames = new String[] { Biome.class.getName() };
|
||||
#elif MC_VER <= MC_1_21
|
||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
||||
#else
|
||||
// See preprocessor comment in createChunkWrapper() for full documentation
|
||||
not implemented for this version of Minecraft!
|
||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
||||
#endif
|
||||
|
||||
return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray);
|
||||
@@ -281,13 +288,13 @@ public class WrapperFactory implements IWrapperFactory
|
||||
// confirm the API level wrapper is also a Core wrapper
|
||||
if (!(levelWrapper instanceof ILevelWrapper))
|
||||
{
|
||||
throw new ClassCastException("Unable to cast... only DH provided IDhApiLevelWrapper's can be used."); // TODO
|
||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
||||
}
|
||||
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
|
||||
|
||||
|
||||
|
||||
#if MC_VER <= MC_1_21
|
||||
//#if MC_VER <= MC_1_XX_X
|
||||
if (objectArray.length != 1)
|
||||
{
|
||||
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
|
||||
@@ -299,10 +306,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
|
||||
BlockState blockState = (BlockState) objectArray[0];
|
||||
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
|
||||
#else
|
||||
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
|
||||
not implemented for this version of Minecraft!
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
/**
|
||||
* Note: when this is updated for different MC versions,
|
||||
@@ -314,11 +318,8 @@ public class WrapperFactory implements IWrapperFactory
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
expectedClassNames = new String[] { Biome.class.getName() };
|
||||
#elif MC_VER <= MC_1_21
|
||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
||||
#else
|
||||
// See preprocessor comment in createChunkWrapper() for full documentation
|
||||
not implemented for this version of Minecraft!
|
||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
||||
#endif
|
||||
|
||||
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
|
||||
|
||||
+1
-1
@@ -293,7 +293,7 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
ResourceLocation resourceLocation;
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#else
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
|
||||
+189
-45
@@ -19,20 +19,28 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.world.level.block.BeaconBeamBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
@@ -72,9 +80,8 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|
||||
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
|
||||
|
||||
// TODO: Make this changeable through the config
|
||||
public static final String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = { AIR_STRING, "minecraft:barrier", "minecraft:structure_void", "minecraft:light", "minecraft:tripwire" };
|
||||
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
|
||||
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
|
||||
|
||||
/** keep track of broken blocks so we don't log every time */
|
||||
private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>();
|
||||
@@ -89,11 +96,17 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
private final int hashCode;
|
||||
/**
|
||||
* Cached opacity value, -1 if not populated. <br>
|
||||
* Should be between {@link IBlockStateWrapper#FULLY_OPAQUE} and {@link IBlockStateWrapper#FULLY_OPAQUE}
|
||||
* Should be between {@link LodUtil#BLOCK_FULLY_OPAQUE} and {@link LodUtil#BLOCK_FULLY_OPAQUE}
|
||||
*/
|
||||
private int opacity = -1;
|
||||
/** used by the Iris shader mod to determine how each LOD should be rendered */
|
||||
private byte irisBlockMaterialId = 0;
|
||||
private byte blockMaterialId = 0;
|
||||
|
||||
private final boolean isBeaconBlock;
|
||||
private final boolean isBeaconBaseBlock;
|
||||
/** null if this block can't tint beacons */
|
||||
private final Color beaconTintColor;
|
||||
private final Color mapColor;
|
||||
|
||||
|
||||
|
||||
@@ -126,16 +139,68 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
this.blockState = blockState;
|
||||
this.serialString = this.serialize(levelWrapper);
|
||||
this.hashCode = Objects.hash(this.serialString);
|
||||
this.irisBlockMaterialId = this.calculateIrisBlockMaterialId();
|
||||
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
|
||||
|
||||
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.irisBlockMaterialId+"]");
|
||||
// beacon blocks
|
||||
String lowercaseSerial = this.serialString.toLowerCase();
|
||||
boolean isBeaconBaseBlock = false;
|
||||
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
|
||||
{
|
||||
String baseBlockName = LodUtil.BEACON_BASE_BLOCK_NAME_LIST.get(i);
|
||||
if (lowercaseSerial.contains(baseBlockName))
|
||||
{
|
||||
isBeaconBaseBlock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.isBeaconBaseBlock = isBeaconBaseBlock;
|
||||
this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon");
|
||||
|
||||
// beacon tint color
|
||||
Color beaconTintColor = null;
|
||||
if (this.blockState != null
|
||||
// beacon blocks also show up here, but since they block the beacon beam we don't want their color
|
||||
&& !this.isBeaconBlock)
|
||||
{
|
||||
Block block = this.blockState.getBlock();
|
||||
if (block instanceof BeaconBeamBlock)
|
||||
{
|
||||
int colorInt;
|
||||
#if MC_VER <= MC_1_19_4
|
||||
colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col;
|
||||
#else
|
||||
colorInt = ((BeaconBeamBlock) block).getColor().getMapColor().col;
|
||||
#endif
|
||||
|
||||
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
|
||||
}
|
||||
}
|
||||
this.beaconTintColor = beaconTintColor;
|
||||
|
||||
|
||||
int mcColor = 0;
|
||||
if (this.blockState != null)
|
||||
{
|
||||
#if MC_VER < MC_1_20_1
|
||||
mcColor = this.blockState.getMaterial().getColor().col;
|
||||
#else
|
||||
mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col;
|
||||
#endif
|
||||
this.mapColor = ColorUtil.toColorObjRGB(mcColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.mapColor = new Color(0,0,0,0);
|
||||
}
|
||||
|
||||
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
//====================//
|
||||
// LodBuilder methods //
|
||||
//====================//
|
||||
|
||||
/**
|
||||
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
|
||||
@@ -149,37 +214,104 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
return rendererIgnoredBlocks;
|
||||
}
|
||||
|
||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
||||
baseIgnoredBlock.add(AIR_STRING);
|
||||
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
return rendererIgnoredBlocks;
|
||||
}
|
||||
/**
|
||||
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
|
||||
* This way the method won't accidentally be called before the deserialization can be completed.
|
||||
*/
|
||||
public static HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
|
||||
{
|
||||
// use the cached version if possible
|
||||
if (rendererIgnoredCaveBlocks != null)
|
||||
{
|
||||
return rendererIgnoredCaveBlocks;
|
||||
}
|
||||
|
||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
||||
baseIgnoredBlock.add(AIR_STRING);
|
||||
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
return rendererIgnoredCaveBlocks;
|
||||
}
|
||||
|
||||
public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; }
|
||||
public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; }
|
||||
|
||||
|
||||
|
||||
// lod builder helpers //
|
||||
|
||||
private static HashSet<IBlockStateWrapper> getBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
|
||||
{
|
||||
// get the base blocks
|
||||
HashSet<String> blockStringList = new HashSet<>();
|
||||
if (baseResourceLocations != null)
|
||||
{
|
||||
blockStringList.addAll(baseResourceLocations);
|
||||
}
|
||||
|
||||
// get the config blocks
|
||||
String ignoreBlockCsv = config.get();
|
||||
if (ignoreBlockCsv != null)
|
||||
{
|
||||
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
|
||||
}
|
||||
|
||||
return getBlockWrappers(blockStringList, levelWrapper);
|
||||
}
|
||||
private static HashSet<IBlockStateWrapper> getBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
|
||||
{
|
||||
// deserialize each of the given resource locations
|
||||
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
|
||||
for (String blockResourceLocation : RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS)
|
||||
for (String blockResourceLocation : blockResourceLocationSet)
|
||||
{
|
||||
try
|
||||
{
|
||||
BlockStateWrapper DefaultBlockStateToIgnore = (BlockStateWrapper) deserialize(blockResourceLocation, levelWrapper);
|
||||
blockStateWrappers.add(DefaultBlockStateToIgnore);
|
||||
|
||||
if (DefaultBlockStateToIgnore == AIR)
|
||||
if (blockResourceLocation == null)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
continue;
|
||||
}
|
||||
String cleanedResourceLocation = blockResourceLocation.trim();
|
||||
if (cleanedResourceLocation.length() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// add all possible blockstates (to account for light blocks with different light values and such)
|
||||
List<BlockState> blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
|
||||
for (BlockState blockState : blockStatesToIgnore)
|
||||
|
||||
BlockStateWrapper defaultBlockStateToIgnore = (BlockStateWrapper) deserialize(cleanedResourceLocation, levelWrapper);
|
||||
blockStateWrappers.add(defaultBlockStateToIgnore);
|
||||
|
||||
if (defaultBlockStateToIgnore != AIR)
|
||||
{
|
||||
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
|
||||
blockStateWrappers.add(newBlockToIgnore);
|
||||
// add all possible blockstates (to account for light blocks with different light values and such)
|
||||
List<BlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
|
||||
for (BlockState blockState : blockStatesToIgnore)
|
||||
{
|
||||
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
|
||||
blockStateWrappers.add(newBlockToIgnore);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// air is a special case so it must be handled separately
|
||||
blockStateWrappers.add(AIR);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize rendererIgnoredBlock with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
||||
LOGGER.warn("Unable to deserialize block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Unexpected error deserializing block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
rendererIgnoredBlocks = blockStateWrappers;
|
||||
return rendererIgnoredBlocks;
|
||||
return blockStateWrappers;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,23 +334,23 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
int opacity;
|
||||
if (this.isAir())
|
||||
{
|
||||
opacity = FULLY_TRANSPARENT;
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||
}
|
||||
else if (this.isLiquid() && !this.blockState.canOcclude())
|
||||
{
|
||||
// probably not a waterlogged block (which should block light entirely)
|
||||
|
||||
// +1 to indicate that the block is translucent (in between transparent and opaque)
|
||||
opacity = FULLY_TRANSPARENT + 1;
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
|
||||
}
|
||||
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
|
||||
{
|
||||
opacity = FULLY_TRANSPARENT;
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// default for all other blocks
|
||||
opacity = FULLY_OPAQUE;
|
||||
opacity = LodUtil.BLOCK_FULLY_OPAQUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +419,19 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getIrisBlockMaterialId() { return this.irisBlockMaterialId; }
|
||||
public boolean isBeaconBlock() { return this.isBeaconBlock; }
|
||||
@Override
|
||||
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
|
||||
@Override
|
||||
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
|
||||
|
||||
@Override
|
||||
public Color getMapColor() { return this.mapColor; }
|
||||
@Override
|
||||
public Color getBeaconTintColor() { return this.beaconTintColor; }
|
||||
|
||||
@Override
|
||||
public byte getMaterialId() { return this.blockMaterialId; }
|
||||
|
||||
@Override
|
||||
public String toString() { return this.getSerialString(); }
|
||||
@@ -380,7 +524,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
ResourceLocation resourceLocation;
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
#else
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
@@ -512,11 +656,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
// Iris methods //
|
||||
//==============//
|
||||
|
||||
private byte calculateIrisBlockMaterialId()
|
||||
private EDhApiBlockMaterial calculateEDhApiBlockMaterialId()
|
||||
{
|
||||
if (this.blockState == null)
|
||||
{
|
||||
return IrisBlockMaterial.AIR;
|
||||
return EDhApiBlockMaterial.AIR;
|
||||
}
|
||||
|
||||
|
||||
@@ -529,15 +673,15 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|| serialString.contains("mushroom")
|
||||
)
|
||||
{
|
||||
return IrisBlockMaterial.LEAVES;
|
||||
return EDhApiBlockMaterial.LEAVES;
|
||||
}
|
||||
else if (this.blockState.is(Blocks.LAVA))
|
||||
{
|
||||
return IrisBlockMaterial.LAVA;
|
||||
return EDhApiBlockMaterial.LAVA;
|
||||
}
|
||||
else if (this.isLiquid() || this.blockState.is(Blocks.WATER))
|
||||
{
|
||||
return IrisBlockMaterial.WATER;
|
||||
return EDhApiBlockMaterial.WATER;
|
||||
}
|
||||
else if (this.blockState.getSoundType() == SoundType.WOOD
|
||||
|| serialString.contains("root")
|
||||
@@ -546,7 +690,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return IrisBlockMaterial.WOOD;
|
||||
return EDhApiBlockMaterial.WOOD;
|
||||
}
|
||||
else if (this.blockState.getSoundType() == SoundType.METAL
|
||||
#if MC_VER >= MC_1_19_2
|
||||
@@ -558,11 +702,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return IrisBlockMaterial.METAL;
|
||||
return EDhApiBlockMaterial.METAL;
|
||||
}
|
||||
else if (serialString.contains("grass_block"))
|
||||
{
|
||||
return IrisBlockMaterial.GRASS;
|
||||
return EDhApiBlockMaterial.GRASS;
|
||||
}
|
||||
else if (
|
||||
serialString.contains("dirt")
|
||||
@@ -572,7 +716,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|| serialString.contains("mycelium")
|
||||
)
|
||||
{
|
||||
return IrisBlockMaterial.DIRT;
|
||||
return EDhApiBlockMaterial.DIRT;
|
||||
}
|
||||
#if MC_VER >= MC_1_17_1
|
||||
else if (this.blockState.getSoundType() == SoundType.DEEPSLATE
|
||||
@@ -581,37 +725,37 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|| this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE
|
||||
|| serialString.contains("deepslate") )
|
||||
{
|
||||
return IrisBlockMaterial.DEEPSLATE;
|
||||
return EDhApiBlockMaterial.DEEPSLATE;
|
||||
}
|
||||
#endif
|
||||
else if (this.serialString.contains("snow"))
|
||||
{
|
||||
return IrisBlockMaterial.SNOW;
|
||||
return EDhApiBlockMaterial.SNOW;
|
||||
}
|
||||
else if (serialString.contains("sand"))
|
||||
{
|
||||
return IrisBlockMaterial.SAND;
|
||||
return EDhApiBlockMaterial.SAND;
|
||||
}
|
||||
else if (serialString.contains("terracotta"))
|
||||
{
|
||||
return IrisBlockMaterial.TERRACOTTA;
|
||||
return EDhApiBlockMaterial.TERRACOTTA;
|
||||
}
|
||||
else if (this.blockState.is(BlockTags.BASE_STONE_NETHER))
|
||||
{
|
||||
return IrisBlockMaterial.NETHER_STONE;
|
||||
return EDhApiBlockMaterial.NETHER_STONE;
|
||||
}
|
||||
else if (serialString.contains("stone")
|
||||
|| serialString.contains("ore"))
|
||||
{
|
||||
return IrisBlockMaterial.STONE;
|
||||
return EDhApiBlockMaterial.STONE;
|
||||
}
|
||||
else if (this.blockState.getLightEmission() > 0)
|
||||
{
|
||||
return IrisBlockMaterial.ILLUMINATED;
|
||||
return EDhApiBlockMaterial.ILLUMINATED;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IrisBlockMaterial.UNKOWN;
|
||||
return EDhApiBlockMaterial.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+485
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.FlowerBlock;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||
#if MC_VER >= MC_1_19_2
|
||||
import net.minecraft.util.RandomSource;
|
||||
#else
|
||||
import java.util.Random;
|
||||
#endif
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* This stores and calculates the colors
|
||||
* the given {@link BlockState} should have based
|
||||
* on the given {@link IClientLevelWrapper}.
|
||||
*
|
||||
* @see ColorUtil
|
||||
*/
|
||||
public class ClientBlockStateColorCache
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
|
||||
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Methods using MC's "RandomSource" object aren't thread safe <br>
|
||||
* so we need to put locks around that logic. <br>
|
||||
* specifically:
|
||||
* <code>
|
||||
* getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM)
|
||||
* </code>
|
||||
*/
|
||||
private static final ReentrantLock RESOLVE_LOCK = new ReentrantLock();
|
||||
|
||||
|
||||
/** This is the order each direction on a block is processed when attempting to get the texture/color */
|
||||
private static final Direction[] COLOR_RESOLUTION_DIRECTION_ORDER = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN };
|
||||
|
||||
private static final int FLOWER_COLOR_SCALE = 5;
|
||||
|
||||
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
private static final Random RANDOM = new Random(0);
|
||||
#else
|
||||
/** Note: this object isn't thread safe and must be put in a lock */
|
||||
private static final RandomSource RANDOM = RandomSource.create();
|
||||
#endif
|
||||
|
||||
private final IClientLevelWrapper levelWrapper;
|
||||
private final BlockState blockState;
|
||||
private final LevelReader level;
|
||||
|
||||
private boolean isColorResolved = false;
|
||||
private int baseColor = 0;
|
||||
private boolean needShade = true;
|
||||
private boolean needPostTinting = false;
|
||||
private int tintIndex = 0;
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// constants //
|
||||
//===========//
|
||||
|
||||
private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13)
|
||||
private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
|
||||
private static final float MIN_SRGB_BOUND = Float.intBitsToFloat(MIN_SRGB_BITS);
|
||||
private static final float MAX_SRGB_BOUND = Float.intBitsToFloat(MAX_SRGB_BITS);
|
||||
|
||||
private static final int[] linearToSrgbTable = new int[]
|
||||
{
|
||||
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
|
||||
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
|
||||
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
|
||||
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
|
||||
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
|
||||
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
|
||||
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
|
||||
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
|
||||
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
|
||||
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
|
||||
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
|
||||
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
|
||||
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
|
||||
};
|
||||
|
||||
private static final float[] srgbToLinearTable = new float[]
|
||||
{
|
||||
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
|
||||
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
|
||||
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
|
||||
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
|
||||
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
|
||||
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
|
||||
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
|
||||
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
|
||||
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
|
||||
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
|
||||
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
|
||||
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
|
||||
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
|
||||
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
|
||||
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
|
||||
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
|
||||
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
|
||||
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
|
||||
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
|
||||
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
|
||||
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
|
||||
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
|
||||
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
|
||||
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
|
||||
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
|
||||
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
|
||||
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
|
||||
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
|
||||
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
|
||||
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
|
||||
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
|
||||
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
|
||||
};
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
|
||||
{
|
||||
this.blockState = blockState;
|
||||
this.levelWrapper = samplingLevel;
|
||||
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
|
||||
this.resolveColors();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// color calculation //
|
||||
//===================//
|
||||
|
||||
private void resolveColors()
|
||||
{
|
||||
if (this.isColorResolved)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// getQuads() isn't thread safe so we need to put this logic in a lock
|
||||
RESOLVE_LOCK.lock();
|
||||
|
||||
if (this.blockState.getFluidState().isEmpty())
|
||||
{
|
||||
// look for the first non-empty direction
|
||||
List<BakedQuad> quads = null;
|
||||
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
|
||||
|
||||
if (quads != null && !quads.isEmpty()
|
||||
&& !(
|
||||
this.blockState.getBlock() instanceof RotatedPillarBlock
|
||||
&& direction == Direction.UP
|
||||
)
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (quads == null || quads.isEmpty())
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).getQuads(this.blockState, null, RANDOM);
|
||||
}
|
||||
|
||||
if (quads != null && !quads.isEmpty())
|
||||
{
|
||||
this.needPostTinting = quads.get(0).isTinted();
|
||||
this.needShade = quads.get(0).isShade();
|
||||
this.tintIndex = quads.get(0).getTintIndex();
|
||||
this.baseColor = calculateColorFromTexture(
|
||||
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
|
||||
#else quads.get(0).getSprite(), #endif
|
||||
ColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backup method.
|
||||
this.needPostTinting = false;
|
||||
this.needShade = false;
|
||||
this.tintIndex = 0;
|
||||
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
|
||||
ColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Liquid Block
|
||||
this.needPostTinting = true;
|
||||
this.needShade = false;
|
||||
this.tintIndex = 0;
|
||||
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
|
||||
ColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
|
||||
this.isColorResolved = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
RESOLVE_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
//TODO: Perhaps make this not just use the first frame?
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
|
||||
{
|
||||
int count = 0;
|
||||
int alpha = 0;
|
||||
double red = 0;
|
||||
double green = 0;
|
||||
double blue = 0;
|
||||
int tempColor;
|
||||
|
||||
// don't render Chiseled blocks.
|
||||
// Since ColorMode is set per block, you only need to check this once.
|
||||
if (colorMode != ColorMode.Chisel)
|
||||
{
|
||||
// textures normally use u and v instead of x and y
|
||||
for (int v = 0; v < getTextureHeight(texture); v++)
|
||||
{
|
||||
for (int u = 0; u < getTextureWidth(texture); u++)
|
||||
{
|
||||
//note: Minecraft color format is: 0xAA BB GG RR
|
||||
//________ DH mod color format is: 0xAA RR GG BB
|
||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
|
||||
|
||||
int r = (tempColor & 0x000000FF);
|
||||
int g = (tempColor & 0x0000FF00) >>> 8;
|
||||
int b = (tempColor & 0x00FF0000) >>> 16;
|
||||
int a = (tempColor & 0xFF000000) >>> 24;
|
||||
int scale = 1;
|
||||
if (colorMode == ColorMode.Leaves)
|
||||
{
|
||||
//switch (//FIXME add config option)
|
||||
// case BLACK:
|
||||
// a = 255; //simulate black background of fast leaves
|
||||
// break;
|
||||
// case IGNORE:
|
||||
if (a == 0) {
|
||||
continue; //same long grass
|
||||
}
|
||||
else
|
||||
{
|
||||
a = 255; //just in case there are semi transparent pixels
|
||||
}
|
||||
// break;
|
||||
// case TRANSPARENT:
|
||||
// break; //do nothing, let it count towards transparency
|
||||
|
||||
}
|
||||
else if (a == 0 && colorMode != ColorMode.Glass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
|
||||
{
|
||||
scale = FLOWER_COLOR_SCALE;
|
||||
}
|
||||
count += scale;
|
||||
//apparently alpha is linear
|
||||
alpha += a * scale;
|
||||
//gamma correction is complicated
|
||||
red += srgbToLinearTable[r] * a * scale;
|
||||
green += srgbToLinearTable[g] * a * scale;
|
||||
blue += srgbToLinearTable[b] * a * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
// this block is entirely transparent
|
||||
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine the average color
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
alpha / count,
|
||||
linearToSrgb((float) (red / (double) alpha)),
|
||||
linearToSrgb((float) (green / (double) alpha)),
|
||||
linearToSrgb((float) (blue / (double) alpha)));
|
||||
}
|
||||
|
||||
//check if not missing texture
|
||||
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
|
||||
{
|
||||
//make it not render at all
|
||||
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
|
||||
}
|
||||
return tempColor;
|
||||
}
|
||||
private static int getTextureWidth(TextureAtlasSprite texture)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
return texture.getWidth();
|
||||
#else
|
||||
return texture.contents().width();
|
||||
#endif
|
||||
}
|
||||
private static int getTextureHeight(TextureAtlasSprite texture)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
return texture.getHeight();
|
||||
#else
|
||||
return texture.contents().height();
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* This method was suggested by IMS from the Iris/Sodium team.
|
||||
* That's where the numbers and code are based.
|
||||
*/
|
||||
private static int linearToSrgb(float c)
|
||||
{
|
||||
if (!(c > MIN_SRGB_BOUND)) {
|
||||
c = MIN_SRGB_BOUND;
|
||||
}
|
||||
|
||||
if (c > MAX_SRGB_BOUND) {
|
||||
c = MAX_SRGB_BOUND;
|
||||
}
|
||||
int inputBits = Float.floatToRawIntBits(c);
|
||||
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
|
||||
|
||||
int bias = (entry >>> 16) << 9;
|
||||
int scale = entry & 0xffff;
|
||||
int t = (inputBits >>> 12) & 0xff;
|
||||
|
||||
return (bias + (scale * t)) >>> 16;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// public getter //
|
||||
//===============//
|
||||
|
||||
public int getColor(BiomeWrapper biome, DhBlockPos pos)
|
||||
{
|
||||
// only get the tint if the block needs to be tinted
|
||||
if (!this.needPostTinting)
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
// don't try tinting blocks that don't support our method of tint getting
|
||||
if (BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
|
||||
// attempt to get the tint
|
||||
int tintColor = -1;
|
||||
try
|
||||
{
|
||||
// try to use the fast tint getter logic first
|
||||
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
try
|
||||
{
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.levelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
catch (UnsupportedOperationException e)
|
||||
{
|
||||
// this exception generally occurs if the tint requires other blocks besides itself
|
||||
LOGGER.debug("Unable to use ["+TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
|
||||
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
// use the level logic only if requested
|
||||
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
// this logic can't be used all the time due to it breaking some blocks tinting
|
||||
// specifically oceans don't render correctly
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// only display the error once per block/biome type to reduce log spam
|
||||
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
|
||||
BROKEN_BLOCK_STATES.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (tintColor != -1)
|
||||
{
|
||||
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unable to get the tinted color, use the base color instead
|
||||
return this.baseColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
enum ColorMode
|
||||
{
|
||||
Default,
|
||||
Flower,
|
||||
Leaves,
|
||||
Chisel,
|
||||
Glass;
|
||||
|
||||
static ColorMode getColorMode(Block block)
|
||||
{
|
||||
if (block instanceof LeavesBlock) return Leaves;
|
||||
if (block instanceof FlowerBlock) return Flower;
|
||||
if (block.toString().contains("glass")) return Glass;
|
||||
if (block.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ClientBlockDetailMap
|
||||
{
|
||||
private final ConcurrentHashMap<BlockState, ClientBlockStateCache> blockCache = new ConcurrentHashMap<>();
|
||||
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
|
||||
private final ClientLevelWrapper level;
|
||||
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
|
||||
|
||||
public ClientBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
|
||||
{ //TODO: Allow a per pos unique setting
|
||||
return blockCache.computeIfAbsent(state, (s) -> new ClientBlockStateCache(s, level, pos));
|
||||
}
|
||||
|
||||
public void clear() { blockCache.clear(); }
|
||||
|
||||
public int getColor(BlockState state, BiomeWrapper biome, DhBlockPos pos)
|
||||
{
|
||||
return getBlockStateData(state, pos).getAndResolveFaceColor(biome, pos);
|
||||
}
|
||||
|
||||
}
|
||||
-412
@@ -1,412 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.TextureAtlasSpriteWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.TintWithoutLevelOverrider;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.*;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.FlowerBlock;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||
#if MC_VER >= MC_1_19_2
|
||||
import net.minecraft.util.RandomSource;
|
||||
#else
|
||||
import java.util.Random;
|
||||
#endif
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @version 2022-9-16
|
||||
*/
|
||||
public class ClientBlockStateCache
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
|
||||
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
public static final Random random = new Random(0);
|
||||
#else
|
||||
public static final RandomSource random = RandomSource.create();
|
||||
#endif
|
||||
|
||||
public final IClientLevelWrapper levelWrapper;
|
||||
public final BlockState blockState;
|
||||
public final LevelReader level;
|
||||
public final BlockPos pos;
|
||||
|
||||
|
||||
|
||||
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos)
|
||||
{
|
||||
this.blockState = blockState;
|
||||
this.levelWrapper = samplingLevel;
|
||||
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
|
||||
this.pos = McObjectConverter.Convert(samplingPos);
|
||||
this.resolveColors();
|
||||
//LOGGER.info("ClientBlocKCache created for {}", blockState);
|
||||
}
|
||||
|
||||
boolean isColorResolved = false;
|
||||
int baseColor = 0; //TODO: Impl per-face color
|
||||
boolean needShade = true;
|
||||
boolean needPostTinting = false;
|
||||
int tintIndex = 0;
|
||||
|
||||
|
||||
public static final int FLOWER_COLOR_SCALE = 5;
|
||||
|
||||
enum ColorMode
|
||||
{
|
||||
Default,
|
||||
Flower,
|
||||
Leaves,
|
||||
Chisel,
|
||||
Glass;
|
||||
static ColorMode getColorMode(Block b)
|
||||
{
|
||||
if (b instanceof LeavesBlock) return Leaves;
|
||||
if (b instanceof FlowerBlock) return Flower;
|
||||
if (b.toString().contains("glass")) return Glass;
|
||||
if (b.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
|
||||
//Way to efficiently do this was suggested by IMS from sodium. This is where those numbers and support code was lifted from.
|
||||
private static final int MIN_BITS = 0x39000000; // 2^(-13)
|
||||
private static final int MAX_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
|
||||
private static final float MIN_BOUND = Float.intBitsToFloat(MIN_BITS);
|
||||
private static final float MAX_BOUND = Float.intBitsToFloat(MAX_BITS);
|
||||
|
||||
private static final int[] linearToSrgbTable = new int[] {
|
||||
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
|
||||
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
|
||||
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
|
||||
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
|
||||
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
|
||||
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
|
||||
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
|
||||
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
|
||||
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
|
||||
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
|
||||
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
|
||||
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
|
||||
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
|
||||
};
|
||||
|
||||
private static final float[] srgbToLinearTable = new float[] {
|
||||
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
|
||||
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
|
||||
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
|
||||
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
|
||||
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
|
||||
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
|
||||
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
|
||||
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
|
||||
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
|
||||
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
|
||||
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
|
||||
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
|
||||
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
|
||||
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
|
||||
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
|
||||
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
|
||||
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
|
||||
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
|
||||
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
|
||||
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
|
||||
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
|
||||
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
|
||||
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
|
||||
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
|
||||
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
|
||||
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
|
||||
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
|
||||
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
|
||||
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
|
||||
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
|
||||
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
|
||||
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
|
||||
};
|
||||
|
||||
private static int linearToSrgb(float c) {
|
||||
if (!(c > MIN_BOUND)) {
|
||||
c = MIN_BOUND;
|
||||
}
|
||||
|
||||
if (c > MAX_BOUND) {
|
||||
c = MAX_BOUND;
|
||||
}
|
||||
int inputBits = Float.floatToRawIntBits(c);
|
||||
int entry = linearToSrgbTable[((inputBits - MIN_BITS) >> 20)];
|
||||
|
||||
int bias = (entry >>> 16) << 9;
|
||||
int scale = entry & 0xffff;
|
||||
int t = (inputBits >>> 12) & 0xff;
|
||||
|
||||
return (bias + (scale * t)) >>> 16;
|
||||
}
|
||||
//////////////
|
||||
|
||||
private static int getWidth(TextureAtlasSprite texture)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
return texture.getWidth();
|
||||
#else
|
||||
return texture.contents().width();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static int getHeight(TextureAtlasSprite texture)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
return texture.getHeight();
|
||||
#else
|
||||
return texture.contents().height();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//TODO: Perhaps make this not just use the first frame?
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
|
||||
{
|
||||
int count = 0;
|
||||
int alpha = 0;
|
||||
double red = 0;
|
||||
double green = 0;
|
||||
double blue = 0;
|
||||
int tempColor;
|
||||
//make Chiseled block not render. Since ColorMode is set per block, you only need to check it once
|
||||
if (colorMode != ColorMode.Chisel)
|
||||
{
|
||||
// textures normally use u and v instead of x and y
|
||||
for (int v = 0; v < getHeight(texture); v++)
|
||||
{
|
||||
for (int u = 0; u < getWidth(texture); u++)
|
||||
{
|
||||
//note: Minecraft color format is: 0xAA BB GG RR
|
||||
//________ DH mod color format is: 0xAA RR GG BB
|
||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
|
||||
|
||||
int r = (tempColor & 0x000000FF);
|
||||
int g = (tempColor & 0x0000FF00) >>> 8;
|
||||
int b = (tempColor & 0x00FF0000) >>> 16;
|
||||
int a = (tempColor & 0xFF000000) >>> 24;
|
||||
int scale = 1;
|
||||
if (colorMode == ColorMode.Leaves)
|
||||
{
|
||||
//switch (//FIXME add config option)
|
||||
// case BLACK:
|
||||
// a = 255; //simulate black background of fast leaves
|
||||
// break;
|
||||
// case IGNORE:
|
||||
if (a == 0) {
|
||||
continue; //same long grass
|
||||
}
|
||||
else
|
||||
{
|
||||
a = 255; //just in case there are semi transparent pixels
|
||||
}
|
||||
// break;
|
||||
// case TRANSPARENT:
|
||||
// break; //do nothing, let it count towards transparency
|
||||
|
||||
}
|
||||
else if (a == 0 && colorMode != ColorMode.Glass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
|
||||
{
|
||||
scale = FLOWER_COLOR_SCALE;
|
||||
}
|
||||
count += scale;
|
||||
//apparently alpha is linear
|
||||
alpha += a * scale;
|
||||
//gamma correction is complicated
|
||||
red += srgbToLinearTable[r] * a * scale;
|
||||
green += srgbToLinearTable[g] * a * scale;
|
||||
blue += srgbToLinearTable[b] * a * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
// this block is entirely transparent
|
||||
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
|
||||
else
|
||||
{
|
||||
// determine the average color
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
alpha / count,
|
||||
linearToSrgb((float) (red / (double) alpha)),
|
||||
linearToSrgb((float) (green / (double) alpha)),
|
||||
linearToSrgb((float) (blue / (double) alpha)));
|
||||
}
|
||||
//check if not missing texture
|
||||
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
|
||||
{
|
||||
//make it not render at all
|
||||
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
|
||||
}
|
||||
return tempColor;
|
||||
}
|
||||
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
|
||||
|
||||
private void resolveColors()
|
||||
{
|
||||
if (isColorResolved) return;
|
||||
if (blockState.getFluidState().isEmpty())
|
||||
{
|
||||
List<BakedQuad> quads = null;
|
||||
for (Direction direction : DIRECTION_ORDER)
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(blockState).getQuads(blockState, direction, random); // TODO getQuads sometimes throws a "makeThreadingException", is there anything we can do about that? Note: this isn't a critical issue, it just prints an ugly error and the render data will need to be regenered.
|
||||
if (quads != null && !quads.isEmpty() &&
|
||||
!(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
|
||||
break;
|
||||
} ;
|
||||
if (quads == null || quads.isEmpty())
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(blockState).getQuads(blockState, null, random);
|
||||
}
|
||||
if (quads != null && !quads.isEmpty())
|
||||
{
|
||||
needPostTinting = quads.get(0).isTinted();
|
||||
needShade = quads.get(0).isShade();
|
||||
tintIndex = quads.get(0).getTintIndex();
|
||||
baseColor = calculateColorFromTexture(
|
||||
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
|
||||
#else quads.get(0).getSprite(), #endif
|
||||
ColorMode.getColorMode(blockState.getBlock()));
|
||||
}
|
||||
else
|
||||
{ // Backup method.
|
||||
needPostTinting = false;
|
||||
needShade = false;
|
||||
tintIndex = 0;
|
||||
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
|
||||
ColorMode.getColorMode(blockState.getBlock()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Liquid Block
|
||||
needPostTinting = true;
|
||||
needShade = false;
|
||||
tintIndex = 0;
|
||||
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
|
||||
ColorMode.getColorMode(blockState.getBlock()));
|
||||
}
|
||||
isColorResolved = true;
|
||||
}
|
||||
|
||||
public int getAndResolveFaceColor(BiomeWrapper biome, DhBlockPos pos)
|
||||
{
|
||||
// FIXME: impl per-face colors
|
||||
|
||||
// only get the tint if the block needs to be tinted
|
||||
if (!this.needPostTinting)
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
// don't try tinting blocks that don't support our method of tint getting
|
||||
if (BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
return this.baseColor;
|
||||
}
|
||||
|
||||
|
||||
// attempt to get the tint
|
||||
int tintColor = -1;
|
||||
try
|
||||
{
|
||||
// try to use the fast tint getter logic first
|
||||
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
try
|
||||
{
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.levelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
catch (UnsupportedOperationException e)
|
||||
{
|
||||
// this exception generally occurs if the tint requires other blocks besides itself
|
||||
LOGGER.debug("Unable to use ["+TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
|
||||
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
// use the level logic only if requested
|
||||
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
// this logic can't be used all the time due to it breaking some blocks tinting
|
||||
// specifically oceans don't render correctly
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// only display the error once per block/biome type to reduce log spam
|
||||
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
|
||||
BROKEN_BLOCK_STATES.add(this.blockState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (tintColor != -1)
|
||||
{
|
||||
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unable to get the tinted color, use the base color instead
|
||||
return this.baseColor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
|
||||
public class ServerBlockDetailMap
|
||||
{
|
||||
private final ConcurrentHashMap<BlockState, ServerBlockStateCache> blockCache = new ConcurrentHashMap<>();
|
||||
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
|
||||
private final ServerLevelWrapper level;
|
||||
public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; }
|
||||
|
||||
public ServerBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
|
||||
{ //TODO: Allow a per pos unique setting
|
||||
return blockCache.computeIfAbsent(state, (s) -> new ServerBlockStateCache(s, level, new DhBlockPos(0, 0, 0)));
|
||||
}
|
||||
|
||||
public void clear() { blockCache.clear(); }
|
||||
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block.cache;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @version 2022-9-16
|
||||
*/
|
||||
public class ServerBlockStateCache
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public final BlockState state;
|
||||
public final LevelReader level;
|
||||
public final BlockPos pos;
|
||||
|
||||
public ServerBlockStateCache(BlockState blockState, ILevelWrapper samplingLevel, DhBlockPos samplingPos)
|
||||
{
|
||||
state = blockState;
|
||||
level = (LevelReader) samplingLevel.getWrappedMcObject();
|
||||
pos = McObjectConverter.Convert(samplingPos);
|
||||
resolveShapes();
|
||||
//LOGGER.info("ServerBlockState created for {}", blockState);
|
||||
}
|
||||
|
||||
boolean noCollision = false;
|
||||
boolean[] occludeFaces = null;
|
||||
boolean[] fullFaces = null;
|
||||
boolean isShapeResolved = false;
|
||||
public void resolveShapes()
|
||||
{
|
||||
if (isShapeResolved) return;
|
||||
if (state.getFluidState().isEmpty())
|
||||
{
|
||||
noCollision = state.getCollisionShape(level, pos).isEmpty();
|
||||
occludeFaces = new boolean[6];
|
||||
if (state.canOcclude())
|
||||
{
|
||||
for (Direction dir : Direction.values())
|
||||
{
|
||||
// Note: isEmpty() isn't quite correct... best would be a isFull() or something...
|
||||
occludeFaces[McObjectConverter.Convert(dir).ordinal()]
|
||||
= !state.getFaceOcclusionShape(level, pos, dir).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
VoxelShape voxelShape = state.getShape(level, pos);
|
||||
fullFaces = new boolean[6];
|
||||
if (!voxelShape.isEmpty())
|
||||
{
|
||||
for (Direction dir : Direction.values())
|
||||
{
|
||||
VoxelShape faceShape = voxelShape.getFaceShape(dir);
|
||||
AABB aabb = faceShape.bounds();
|
||||
boolean xFull = aabb.minX <= 0.01 && aabb.maxX >= 0.99;
|
||||
boolean yFull = aabb.minY <= 0.01 && aabb.maxY >= 0.99;
|
||||
boolean zFull = aabb.minZ <= 0.01 && aabb.maxZ >= 0.99;
|
||||
fullFaces[McObjectConverter.Convert(dir).ordinal()] =
|
||||
(xFull || dir.getAxis().equals(Direction.Axis.X))
|
||||
&& (yFull || dir.getAxis().equals(Direction.Axis.Y))
|
||||
&& (zFull || dir.getAxis().equals(Direction.Axis.Z));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Liquid Block. Treat as full block
|
||||
occludeFaces = new boolean[6];
|
||||
Arrays.fill(occludeFaces, true);
|
||||
fullFaces = new boolean[6];
|
||||
Arrays.fill(fullFaces, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+19
-14
@@ -23,7 +23,7 @@ import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||
@@ -223,6 +223,9 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
LevelChunkSection[] sections = this.chunk.getSections();
|
||||
for (int index = sections.length-1; index >= 0; index--)
|
||||
{
|
||||
// update at each position to fix using the max height if the chunk is empty
|
||||
this.maxNonEmptyHeight = this.getChunkSectionMinHeight(index) + 16;
|
||||
|
||||
if (sections[index] == null)
|
||||
{
|
||||
continue;
|
||||
@@ -230,7 +233,7 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
|
||||
if (!isChunkSectionEmpty(sections[index]))
|
||||
{
|
||||
this.maxNonEmptyHeight = this.getChunkSectionMinHeight(index) + 16;
|
||||
// non-empty section found
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -247,15 +250,7 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
return section.hasOnlyAir();
|
||||
#endif
|
||||
}
|
||||
private int getChunkSectionMinHeight(int index)
|
||||
{
|
||||
// convert from an index to a block coordinate
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
return this.chunk.getSections()[index].bottomBlockY();
|
||||
#else
|
||||
return this.chunk.getSectionYFromSectionIndex(index) * 16;
|
||||
#endif
|
||||
}
|
||||
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getMinBuildHeight(); }
|
||||
|
||||
|
||||
@Override
|
||||
@@ -310,7 +305,7 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
|
||||
public static ChunkStatus getStatus(ChunkAccess chunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
return chunk.getStatus();
|
||||
#else
|
||||
return chunk.getPersistedStatus();
|
||||
@@ -395,6 +390,8 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
return this.blockLightStorage;
|
||||
}
|
||||
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
|
||||
@Override
|
||||
public void clearDhBlockLighting() { this.getBlockLightStorage().clear(); }
|
||||
|
||||
|
||||
@Override
|
||||
@@ -409,6 +406,8 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
||||
this.getSkyLightStorage().set(relX, y, relZ, lightValue);
|
||||
}
|
||||
@Override
|
||||
public void clearDhSkyLighting() { this.getSkyLightStorage().clear(); }
|
||||
|
||||
private ChunkLightStorage getSkyLightStorage()
|
||||
{
|
||||
@@ -464,7 +463,7 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
* before the list has finished populating.
|
||||
*/
|
||||
@Override
|
||||
public synchronized ArrayList<DhBlockPos> getBlockLightPosList()
|
||||
public synchronized ArrayList<DhBlockPos> getWorldBlockLightPosList()
|
||||
{
|
||||
// only populate the list once
|
||||
if (this.blockLightPosList == null)
|
||||
@@ -480,7 +479,13 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
#else
|
||||
this.chunk.findBlockLightSources((blockPos, blockState) ->
|
||||
{
|
||||
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
|
||||
DhBlockPos pos = new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
|
||||
// this can be uncommented if MC decides to return relative block positions in the future instead of world positions
|
||||
//pos.mutateToChunkRelativePos(pos);
|
||||
//pos.mutateOffset(this.chunkPos.getMinBlockX(), 0, this.chunkPos.getMinBlockZ(), pos);
|
||||
|
||||
this.blockLightPosList.add(pos);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
+13
-8
@@ -158,19 +158,22 @@ public class ClassicConfigGUI
|
||||
// button.active = entries.stream().allMatch(e -> e.inLimits);
|
||||
|
||||
|
||||
if (((ConfigEntry) info).isValid(value) == 0 && info.getType() != List.class)
|
||||
if (info.getType() == String.class
|
||||
|| info.getType() == List.class)
|
||||
{
|
||||
((ConfigEntry) info).uiSetWithoutSaving(stringValue);
|
||||
}
|
||||
else if (((ConfigEntry) info).isValid(value) == 0)
|
||||
{
|
||||
if (!cast)
|
||||
{
|
||||
((ConfigEntry) info).uiSetWithoutSaving(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
((ConfigEntry) info).uiSetWithoutSaving(value.intValue());
|
||||
}
|
||||
}
|
||||
// else if (((ConfigEntry) info).isValidMemoryAddress() == 0)
|
||||
// {
|
||||
// if (((List<String>) info.get()).size() == ((EntryInfo) info.guiValue).index)
|
||||
// info.uiSet(((List<String>) info.get()).add(""));
|
||||
// info.uiSet(((List<String>) info.get()).set(((EntryInfo) info.guiValue).index, Arrays.stream(((EntryInfo) info.guiValue).tempValue.replace("[", "").replace("]", "").split(", ")).collect(Collectors.toList()).get(0)));
|
||||
// }
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -256,7 +259,7 @@ public class ClassicConfigGUI
|
||||
0, 0,
|
||||
// Some textuary stuff
|
||||
0,
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
@@ -443,7 +446,9 @@ public class ClassicConfigGUI
|
||||
String key = translationPrefix + (newInfo.category.isEmpty() ? "" : newInfo.category + ".") + newInfo.getName() + ".@tooltip";
|
||||
|
||||
if (((EntryInfo) newInfo.guiValue).error != null && text.equals(name))
|
||||
{
|
||||
DhRenderTooltip(matrices, font, ((EntryInfo) newInfo.guiValue).error.getValue(), mouseX, mouseY);
|
||||
}
|
||||
else if (I18n.exists(key) && (text != null && text.equals(name)))
|
||||
{
|
||||
List<Component> list = new ArrayList<>();
|
||||
|
||||
+9
-3
@@ -101,11 +101,17 @@ public class ChangelogScreen extends DhScreen
|
||||
this.changelog.add("");
|
||||
this.changelog.add("");
|
||||
|
||||
String changelog = ModrinthGetter.changeLogs.get(versionID);
|
||||
if (changelog == null)
|
||||
{
|
||||
// in case something goes wrong this will prevent null pointers
|
||||
changelog = "";
|
||||
}
|
||||
|
||||
// Get the release changelog and split it by the new lines
|
||||
String[] unwrappedChangelog = // Arrays.asList could be used if a list object is desired here vs List.of which is only available for Java 9+
|
||||
new MarkdownFormatter.MinecraftFormat().convertTo( // This formats markdown to minecraft's "§" characters
|
||||
ModrinthGetter.changeLogs.get(versionID)
|
||||
).split("\\n");
|
||||
// This formats markdown to minecraft's "§" charactersnew MarkdownFormatter.MinecraftFormat().convertTo(
|
||||
new MarkdownFormatter.MinecraftFormat().convertTo(changelog).split("\\n");
|
||||
// Makes the words wrap around to not go off the screen
|
||||
for (String str : unwrappedChangelog)
|
||||
{
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ public class UpdateModScreen extends DhScreen
|
||||
0, 0,
|
||||
// Some textuary stuff
|
||||
0,
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "logo.png"),
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
|
||||
@@ -107,7 +107,7 @@ public class UpdateModScreen extends DhScreen
|
||||
0, 0,
|
||||
// Some textuary stuff
|
||||
0,
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
|
||||
+65
-69
@@ -31,6 +31,7 @@ import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
@@ -38,13 +39,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
#if MC_VER < MC_1_19_2
|
||||
@@ -59,16 +60,15 @@ import org.jetbrains.annotations.Nullable;
|
||||
* A singleton that wraps the Minecraft object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 3-5-2022
|
||||
*/
|
||||
//@Environment(EnvType.CLIENT)
|
||||
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||
|
||||
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
|
||||
|
||||
public final Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
|
||||
/**
|
||||
* The lightmap for the current:
|
||||
@@ -99,10 +99,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
* This doesn't affect OpenGL objects in any way.
|
||||
*/
|
||||
@Override
|
||||
public void clearFrameObjectCache()
|
||||
{
|
||||
lightMap = null;
|
||||
}
|
||||
public void clearFrameObjectCache() { this.lightMap = null; }
|
||||
|
||||
|
||||
|
||||
@@ -118,10 +115,10 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
{
|
||||
default:
|
||||
case AUTO:
|
||||
if (this.mc.level != null)
|
||||
if (MINECRAFT.level != null)
|
||||
{
|
||||
Direction mcDir = McObjectConverter.Convert(lodDirection);
|
||||
return this.mc.level.getShade(mcDir, true);
|
||||
return MINECRAFT.level.getShade(mcDir, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -150,47 +147,63 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSinglePlayerServer() { return mc.hasSingleplayerServer(); }
|
||||
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
|
||||
@Override
|
||||
public boolean clientConnectedToDedicatedServer() { return mc.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
|
||||
public boolean clientConnectedToDedicatedServer() { return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
|
||||
@Override
|
||||
public boolean connectedToReplay() { return !MINECRAFT.hasSingleplayerServer() && MINECRAFT.getCurrentServer() == null; }
|
||||
|
||||
@Override
|
||||
public String getCurrentServerName() { return mc.getCurrentServer().name; }
|
||||
public String getCurrentServerName()
|
||||
{
|
||||
if (this.connectedToReplay())
|
||||
{
|
||||
return ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = MINECRAFT.getCurrentServer();
|
||||
return (server != null) ? server.name : "NULL";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentServerIp() { return mc.getCurrentServer().ip; }
|
||||
public String getCurrentServerIp()
|
||||
{
|
||||
if (this.connectedToReplay())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = MINECRAFT.getCurrentServer();
|
||||
return (server != null) ? server.ip : "NA";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentServerVersion()
|
||||
{
|
||||
return mc.getCurrentServer().version.getString();
|
||||
ServerData server = MINECRAFT.getCurrentServer();
|
||||
return (server != null) ? server.version.getString() : "UNKOWN";
|
||||
}
|
||||
|
||||
//=============//
|
||||
// Simple gets //
|
||||
//=============//
|
||||
|
||||
public LocalPlayer getPlayer()
|
||||
{
|
||||
return mc.player;
|
||||
}
|
||||
public LocalPlayer getPlayer() { return MINECRAFT.player; }
|
||||
|
||||
@Override
|
||||
public boolean playerExists()
|
||||
{
|
||||
return mc.player != null;
|
||||
}
|
||||
public boolean playerExists() { return MINECRAFT.player != null; }
|
||||
|
||||
@Override
|
||||
public UUID getPlayerUUID()
|
||||
{
|
||||
return getPlayer().getUUID();
|
||||
}
|
||||
public UUID getPlayerUUID() { return this.getPlayer().getUUID(); }
|
||||
|
||||
@Override
|
||||
public DhBlockPos getPlayerBlockPos()
|
||||
{
|
||||
BlockPos playerPos = getPlayer().blockPosition();
|
||||
BlockPos playerPos = this.getPlayer().blockPosition();
|
||||
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
|
||||
}
|
||||
|
||||
@@ -198,46 +211,38 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
public DhChunkPos getPlayerChunkPos()
|
||||
{
|
||||
#if MC_VER < MC_1_17_1
|
||||
ChunkPos playerPos = new ChunkPos(getPlayer().blockPosition());
|
||||
ChunkPos playerPos = new ChunkPos(this.getPlayer().blockPosition());
|
||||
#else
|
||||
ChunkPos playerPos = getPlayer().chunkPosition();
|
||||
ChunkPos playerPos = this.getPlayer().chunkPosition();
|
||||
#endif
|
||||
return new DhChunkPos(playerPos.x, playerPos.z);
|
||||
}
|
||||
|
||||
public ModelManager getModelManager()
|
||||
{
|
||||
return mc.getModelManager();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IClientLevelWrapper getWrappedClientLevel()
|
||||
{
|
||||
if (this.mc.level == null)
|
||||
if (MINECRAFT.level == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ClientLevelWrapper.getWrapperIgnoringOverride(this.mc.level);
|
||||
}
|
||||
|
||||
/** Please move over to getInstallationDirectory() */
|
||||
@Deprecated
|
||||
@Override
|
||||
public File getGameDirectory()
|
||||
{
|
||||
return getInstallationDirectory();
|
||||
return ClientLevelWrapper.getWrapperIgnoringOverride(MINECRAFT.level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IProfilerWrapper getProfiler()
|
||||
{
|
||||
if (profilerWrapper == null)
|
||||
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
|
||||
else if (mc.getProfiler() != profilerWrapper.profiler)
|
||||
profilerWrapper.profiler = mc.getProfiler();
|
||||
return profilerWrapper;
|
||||
if (this.profilerWrapper == null)
|
||||
{
|
||||
this.profilerWrapper = new ProfilerWrapper(MINECRAFT.getProfiler());
|
||||
}
|
||||
else if (MINECRAFT.getProfiler() != this.profilerWrapper.profiler)
|
||||
{
|
||||
this.profilerWrapper.profiler = MINECRAFT.getProfiler();
|
||||
}
|
||||
|
||||
return this.profilerWrapper;
|
||||
}
|
||||
|
||||
/** Returns all worlds available to the server */
|
||||
@@ -246,7 +251,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
{
|
||||
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
|
||||
|
||||
Iterable<ServerLevel> serverWorlds = mc.getSingleplayerServer().getAllLevels();
|
||||
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
|
||||
for (ServerLevel world : serverWorlds)
|
||||
{
|
||||
worlds.add(ServerLevelWrapper.getWrapper(world));
|
||||
@@ -260,12 +265,12 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
@Override
|
||||
public void sendChatMessage(String string)
|
||||
{
|
||||
LocalPlayer p = getPlayer();
|
||||
if (p == null) return;
|
||||
LocalPlayer player = this.getPlayer();
|
||||
if (player == null) return;
|
||||
#if MC_VER < MC_1_19_2
|
||||
p.sendMessage(new TextComponent(string), getPlayer().getUUID());
|
||||
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
|
||||
#else
|
||||
p.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
|
||||
player.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -290,24 +295,15 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOptionsObject()
|
||||
{
|
||||
return mc.options;
|
||||
}
|
||||
public Object getOptionsObject() { return MINECRAFT.options; }
|
||||
|
||||
@Override
|
||||
public boolean isDedicatedServer()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public boolean isDedicatedServer() { return false; }
|
||||
|
||||
@Override
|
||||
public File getInstallationDirectory()
|
||||
{
|
||||
return mc.gameDirectory;
|
||||
}
|
||||
public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
|
||||
|
||||
@Override
|
||||
public void executeOnRenderThread(Runnable runnable) { this.mc.execute(runnable); }
|
||||
public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
|
||||
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
return this.dedicatedServer.getServerDirectory();
|
||||
#else
|
||||
return this.dedicatedServer.getServerDirectory().toFile();
|
||||
|
||||
+10
-139
@@ -21,31 +21,26 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
|
||||
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_19_4
|
||||
import org.joml.Vector3f;
|
||||
#else
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
#else
|
||||
#endif
|
||||
#if MC_VER >= MC_1_20_2
|
||||
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||
@@ -55,20 +50,14 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOpt
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3f;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
#if MC_VER < MC_1_17_1
|
||||
import net.minecraft.tags.FluidTags;
|
||||
@@ -78,10 +67,8 @@ import org.lwjgl.opengl.GL15;
|
||||
#else
|
||||
import net.minecraft.world.level.material.FogType;
|
||||
#endif
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
|
||||
/**
|
||||
@@ -123,14 +110,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhBlockPos getCameraBlockPosition()
|
||||
{
|
||||
Camera camera = MC.gameRenderer.getMainCamera();
|
||||
BlockPos blockPos = camera.getBlockPosition();
|
||||
return new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
|
||||
public boolean playerHasBlindingEffect()
|
||||
@@ -151,43 +130,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mat4f getWorldViewMatrix()
|
||||
{
|
||||
Camera camera = MC.gameRenderer.getMainCamera();
|
||||
Vector3f cameraVec3 = new Vector3f(
|
||||
(float)camera.getPosition().x,
|
||||
(float)camera.getPosition().y,
|
||||
(float)camera.getPosition().z);
|
||||
cameraVec3 = cameraVec3.negate();
|
||||
|
||||
Matrix4f matWorldView = new Matrix4f()
|
||||
.rotateX((float)Math.toRadians(camera.getXRot()))
|
||||
.rotateY((float)Math.toRadians(camera.getYRot() + 180f))
|
||||
.translate(cameraVec3);
|
||||
return new Mat4f(matWorldView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mat4f getDefaultProjectionMatrix(float partialTicks)
|
||||
{
|
||||
#if MC_VER < MC_1_17_1
|
||||
return McObjectConverter.Convert(Minecraft.getInstance().gameRenderer.getProjectionMatrix(Minecraft.getInstance().gameRenderer.getMainCamera(), partialTicks, true));
|
||||
#else
|
||||
return McObjectConverter.Convert(MC.gameRenderer.getProjectionMatrix(MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getGamma()
|
||||
{
|
||||
#if MC_VER < MC_1_19_2
|
||||
return MC.options.gamma;
|
||||
#else
|
||||
return MC.options.gamma().get();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getFogColor(float partialTicks)
|
||||
{
|
||||
@@ -213,7 +155,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
if (MC.level.dimensionType().hasSkyLight())
|
||||
{
|
||||
float frameTime;
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
frameTime = MC.getFrameTime();
|
||||
#else
|
||||
frameTime = MC.getTimer().getRealtimeDeltaTicks();
|
||||
@@ -317,77 +259,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
return getRenderTarget().viewHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of all chunks that Minecraft
|
||||
* is going to render this frame. <br><br>
|
||||
* <p>
|
||||
*/
|
||||
|
||||
public boolean usingBackupGetVanillaRenderedChunks = false;
|
||||
@Override
|
||||
public HashSet<DhChunkPos> getVanillaRenderedChunks()
|
||||
{
|
||||
ISodiumAccessor sodium = ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class);
|
||||
if (sodium != null)
|
||||
{
|
||||
return sodium.getNormalRenderedChunks();
|
||||
}
|
||||
IOptifineAccessor optifine = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
|
||||
if (optifine != null)
|
||||
{
|
||||
HashSet<DhChunkPos> pos = optifine.getNormalRenderedChunks();
|
||||
if (pos == null)
|
||||
pos = getMaximumRenderedChunks();
|
||||
return pos;
|
||||
}
|
||||
if (!usingBackupGetVanillaRenderedChunks)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_20_2
|
||||
LevelRenderer levelRenderer = MC.levelRenderer;
|
||||
Collection<LevelRenderer.RenderChunkInfo> chunks =
|
||||
#if MC_VER < MC_1_18_2 levelRenderer.renderChunks;
|
||||
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
|
||||
|
||||
return (chunks.stream().map((chunk) -> {
|
||||
AABB chunkBoundingBox =
|
||||
#if MC_VER < MC_1_18_2 chunk.chunk.bb;
|
||||
#else chunk.chunk.getBoundingBox(); #endif
|
||||
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
|
||||
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
|
||||
}).collect(Collectors.toCollection(HashSet::new)));
|
||||
#else
|
||||
LevelRenderer levelRenderer = MC.levelRenderer;
|
||||
Collection<SectionRenderDispatcher.RenderSection> chunks = levelRenderer.visibleSections;
|
||||
|
||||
return (chunks.stream().map((chunk) -> {
|
||||
AABB chunkBoundingBox = chunk.getBoundingBox();
|
||||
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
|
||||
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
|
||||
}).collect(Collectors.toCollection(HashSet::new)));
|
||||
#endif
|
||||
}
|
||||
catch (LinkageError e)
|
||||
{
|
||||
try
|
||||
{
|
||||
MinecraftClientWrapper.INSTANCE.sendChatMessage(
|
||||
"\u00A7e\u00A7l\u00A7uWARNING: Distant Horizons: getVanillaRenderedChunks method failed."
|
||||
+ " Using Backup Method.");
|
||||
MinecraftClientWrapper.INSTANCE.sendChatMessage(
|
||||
"\u00A7eOverdraw prevention will be worse than normal.");
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
}
|
||||
LOGGER.error("getVanillaRenderedChunks Error: ", e);
|
||||
usingBackupGetVanillaRenderedChunks = true;
|
||||
}
|
||||
}
|
||||
return getMaximumRenderedChunks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
|
||||
|
||||
@@ -415,7 +286,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
// so this will have to do for now
|
||||
IDimensionTypeWrapper dimensionType = level.getDimensionType();
|
||||
|
||||
LightMapWrapper wrapper = this.lightmapByDimensionType.compute(dimensionType, (dimType, oldWrapper) -> new LightMapWrapper());
|
||||
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
|
||||
wrapper.uploadLightmap(lightPixels);
|
||||
}
|
||||
|
||||
|
||||
+12
-9
@@ -36,14 +36,6 @@ public class LightMapWrapper implements ILightMapWrapper
|
||||
|
||||
public LightMapWrapper() { }
|
||||
|
||||
private void createLightmap(NativeImage image)
|
||||
{
|
||||
this.textureId = GL32.glGenTextures();
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
|
||||
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
@@ -53,14 +45,25 @@ public class LightMapWrapper implements ILightMapWrapper
|
||||
public void uploadLightmap(NativeImage image)
|
||||
{
|
||||
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
|
||||
if (this.textureId == 0)
|
||||
{
|
||||
this.createLightmap(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
|
||||
}
|
||||
image.upload(0, 0, 0, false);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
|
||||
}
|
||||
private void createLightmap(NativeImage image)
|
||||
{
|
||||
this.textureId = GL32.glGenTextures();
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
|
||||
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void bind()
|
||||
|
||||
+67
-18
@@ -1,17 +1,16 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.world;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailMap;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
||||
import com.seibel.distanthorizons.core.level.*;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
@@ -19,16 +18,18 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -41,14 +42,18 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
|
||||
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>(); // TODO can leak
|
||||
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
||||
|
||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||
|
||||
private final ClientLevel level;
|
||||
private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this);
|
||||
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>();
|
||||
|
||||
private BlockStateWrapper dirtBlockWrapper;
|
||||
private BiomeWrapper plainsBiomeWrapper;
|
||||
@Deprecated // TODO circular references are bad
|
||||
private IDhLevel parentDhLevel;
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +93,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
{
|
||||
try
|
||||
{
|
||||
Iterable<ServerLevel> serverLevels = MinecraftClientWrapper.INSTANCE.mc.getSingleplayerServer().getAllLevels();
|
||||
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
|
||||
|
||||
// attempt to find the server level with the same dimension type
|
||||
// TODO this assumes only one level per dimension type, the SubDimensionLevelMatcher will need to be added for supporting multiple levels per dimension
|
||||
@@ -108,7 +113,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Failed to get server side wrapper for client level: " + level);
|
||||
LOGGER.error("Failed to get server side wrapper for client level: " + this.level);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -120,9 +125,13 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
//====================//
|
||||
|
||||
@Override
|
||||
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
|
||||
{
|
||||
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos);
|
||||
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
|
||||
((BlockStateWrapper) blockWrapper).blockState,
|
||||
(block) -> new ClientBlockStateColorCache(block, this));
|
||||
|
||||
return blockColorCache.getColor((BiomeWrapper) biome, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,9 +151,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
}
|
||||
}
|
||||
|
||||
return this.blockMap.getColor(this.dirtBlockWrapper.blockState, BiomeWrapper.EMPTY_WRAPPER, DhBlockPos.ZERO);
|
||||
return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBlockColorCache() { this.blockCache.clear(); }
|
||||
|
||||
@Override
|
||||
public IBiomeWrapper getPlainsBiomeWrapper()
|
||||
{
|
||||
@@ -180,7 +192,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
|
||||
|
||||
@Override
|
||||
public int getHeight() { return this.level.getHeight(); }
|
||||
public int getMaxHeight() { return this.level.getHeight(); }
|
||||
|
||||
@Override
|
||||
public int getMinHeight()
|
||||
@@ -195,12 +207,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
@Override
|
||||
public IChunkWrapper tryGetChunk(DhChunkPos pos)
|
||||
{
|
||||
if (!this.level.hasChunk(pos.x, pos.z))
|
||||
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ChunkAccess chunk = this.level.getChunk(pos.x, pos.z, ChunkStatus.EMPTY, false);
|
||||
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false);
|
||||
if (chunk == null)
|
||||
{
|
||||
return null;
|
||||
@@ -229,7 +241,44 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
public ClientLevel getWrappedMcObject() { return this.level; }
|
||||
|
||||
@Override
|
||||
public void onUnload() { LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level); }
|
||||
public void onUnload()
|
||||
{
|
||||
LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level);
|
||||
this.parentDhLevel = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// generic rendering //
|
||||
//===================//
|
||||
|
||||
@Override
|
||||
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
|
||||
|
||||
@Override
|
||||
public IDhApiCustomRenderRegister getRenderRegister()
|
||||
{
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.parentDhLevel.getGenericRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getCloudColor(float tickDelta)
|
||||
{
|
||||
Vec3 colorVec3 = this.level.getCloudColor(tickDelta);
|
||||
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
|
||||
+31
-29
@@ -23,19 +23,18 @@ import java.io.File;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.block.cache.ServerBlockDetailMap;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -49,7 +48,6 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @version 2022-9-16
|
||||
@@ -59,8 +57,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
|
||||
|
||||
final ServerLevel level;
|
||||
ServerBlockDetailMap blockMap = new ServerBlockDetailMap(this);
|
||||
private final ServerLevel level;
|
||||
@Deprecated // TODO circular references are bad
|
||||
private IDhLevel parentDhLevel;
|
||||
|
||||
|
||||
|
||||
@@ -81,19 +80,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IClientLevelWrapper tryGetClientLevelWrapper()
|
||||
{
|
||||
MinecraftClientWrapper client = MinecraftClientWrapper.INSTANCE;
|
||||
if (client.mc.level == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ClientLevelWrapper.getWrapper(client.mc.level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getSaveFolder()
|
||||
{
|
||||
@@ -127,7 +113,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
public int getMaxHeight()
|
||||
{
|
||||
return level.getHeight();
|
||||
}
|
||||
@@ -141,11 +127,12 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
return level.getMinBuildHeight();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkWrapper tryGetChunk(DhChunkPos pos)
|
||||
{
|
||||
if (!level.hasChunk(pos.x, pos.z)) return null;
|
||||
ChunkAccess chunk = level.getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
|
||||
if (!level.hasChunk(pos.getX(), pos.getZ())) return null;
|
||||
ChunkAccess chunk = level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FULL, false);
|
||||
if (chunk == null) return null;
|
||||
return new ChunkWrapper(chunk, level, this);
|
||||
}
|
||||
@@ -171,18 +158,33 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevel getWrappedMcObject()
|
||||
{
|
||||
return level;
|
||||
}
|
||||
public ServerLevel getWrappedMcObject() { return this.level; }
|
||||
|
||||
@Override
|
||||
public void onUnload() { LEVEL_WRAPPER_BY_SERVER_LEVEL.remove(this.level); }
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
|
||||
|
||||
@Override
|
||||
public IDhApiCustomRenderRegister getRenderRegister()
|
||||
{
|
||||
return "Wrapped{" + level.toString() + "@" + getDimensionType().getDimensionName() + "}";
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.parentDhLevel.getGenericRenderer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}"; }
|
||||
|
||||
}
|
||||
|
||||
+225
-159
@@ -224,8 +224,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static ImmutableMap<EDhApiWorldGenerationStep, Integer> BorderNeeded;
|
||||
public static int MaxBorderNeeded;
|
||||
public static final ImmutableMap<EDhApiWorldGenerationStep, Integer> WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP;
|
||||
public static final int MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
|
||||
|
||||
static
|
||||
{
|
||||
@@ -253,8 +253,13 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
|
||||
builder.put(EDhApiWorldGenerationStep.FEATURES, 0);
|
||||
builder.put(EDhApiWorldGenerationStep.LIGHT, 0);
|
||||
BorderNeeded = builder.build();
|
||||
MaxBorderNeeded = BorderNeeded.values().stream().mapToInt(Integer::intValue).max().getAsInt();
|
||||
WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP = builder.build();
|
||||
|
||||
// TODO this is a test to see if the additional boarder is actually necessary or not.
|
||||
// If world generators end up having infinite loops or other unexplained issues,
|
||||
// this should be set back to the commented out logic below
|
||||
MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = 0;
|
||||
//MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.values().stream().mapToInt(Integer::intValue).max().getAsInt();
|
||||
}
|
||||
|
||||
public BatchGenerationEnvironment(IDhServerLevel serverlevel)
|
||||
@@ -346,8 +351,11 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
}
|
||||
else if (event.hasTimeout(Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get(), TimeUnit.SECONDS))
|
||||
{
|
||||
EVENT_LOGGER.error("Batching World Generator: " + event + " timed out and terminated! Please lower your CPU load.");
|
||||
EVENT_LOGGER.info("Dump PrefEvent: " + event.timer);
|
||||
EVENT_LOGGER.warn(
|
||||
"Batching World Generator: [" + event + "] timed out and terminated after ["+Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get()+"] seconds. " +
|
||||
"\nYour computer might be overloaded or your world gen mods might be causing world gen to take longer than expected. " +
|
||||
"\nEither increase DH's world gen timeout or reduce your computer's CPU load.");
|
||||
EVENT_LOGGER.debug("Dump PrefEvent: " + event.timer);
|
||||
try
|
||||
{
|
||||
if (!event.terminate())
|
||||
@@ -380,172 +388,177 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
{
|
||||
EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos);
|
||||
|
||||
ArrayGridList<ChunkWrapper> chunkWrapperList;
|
||||
DhLitWorldGenRegion region;
|
||||
DummyLightEngine dummyLightEngine;
|
||||
LightGetterAdaptor adaptor;
|
||||
// Minecraft's generation events expect odd chunk width areas (3x3, 7x7, or 11x11),
|
||||
// but DH submits square generation events (4x4).
|
||||
// We handle this later, although that handling would need to change if the gen size ever changed.
|
||||
LodUtil.assertTrue(genEvent.size % 2 == 0, "Generation events are expected to be an evan number of chunks wide.");
|
||||
|
||||
int borderSize = MaxBorderNeeded;
|
||||
int refSize = genEvent.size + borderSize * 2;
|
||||
int refPosX = genEvent.minPos.x - borderSize;
|
||||
int refPosZ = genEvent.minPos.z - borderSize;
|
||||
|
||||
try
|
||||
|
||||
int borderSize = MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
|
||||
// genEvent.size - 1 converts the even width size to an odd number for MC compatability
|
||||
int refSize = (genEvent.size - 1) + (borderSize * 2);
|
||||
int refPosX = genEvent.minPos.getX() - borderSize;
|
||||
int refPosZ = genEvent.minPos.getZ() - borderSize;
|
||||
|
||||
LightGetterAdaptor lightGetterAdaptor = new LightGetterAdaptor(this.params.level);
|
||||
DummyLightEngine dummyLightEngine = new DummyLightEngine(lightGetterAdaptor);
|
||||
|
||||
|
||||
|
||||
//====================================//
|
||||
// offset and generate odd width area //
|
||||
//====================================//
|
||||
|
||||
// reused data between each offset
|
||||
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
|
||||
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
|
||||
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos = new HashMap<>();
|
||||
HashMap<DhChunkPos, ChunkWrapper> chunkWrappersByDhPos = new HashMap<>();
|
||||
|
||||
// offset 1 chunk in both X and Z direction so we can generate an even number of chunks wide
|
||||
// while still submitting odd numbers to MC's internal generators
|
||||
for (int xOffset = 0; xOffset < 2; xOffset++)
|
||||
{
|
||||
ArrayGridList<ChunkAccess> totalChunks;
|
||||
|
||||
adaptor = new LightGetterAdaptor(this.params.level);
|
||||
dummyLightEngine = new DummyLightEngine(adaptor);
|
||||
|
||||
|
||||
|
||||
//=============================//
|
||||
// try getting existing chunks //
|
||||
//=============================//
|
||||
|
||||
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
|
||||
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
|
||||
IEmptyChunkGeneratorFunc emptyChunkGeneratorFunc = (int x, int z) ->
|
||||
// final is so the offset can be used in lambdas
|
||||
final int xOffsetFinal = xOffset;
|
||||
for (int zOffset = 0; zOffset < 2; zOffset++)
|
||||
{
|
||||
ChunkPos chunkPos = new ChunkPos(x, z);
|
||||
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
|
||||
ChunkAccess newChunk = null;
|
||||
try
|
||||
{
|
||||
// get the chunk
|
||||
CompoundTag chunkData = this.getChunkNbtData(chunkPos);
|
||||
newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
|
||||
|
||||
// get chunk lighting
|
||||
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
|
||||
if (combinedLights != null)
|
||||
{
|
||||
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
|
||||
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException loadChunkError)
|
||||
{
|
||||
// Continue...
|
||||
}
|
||||
final int zOffsetFinal = zOffset;
|
||||
|
||||
if (newChunk == null)
|
||||
|
||||
|
||||
//================//
|
||||
// variable setup //
|
||||
//================//
|
||||
|
||||
int radius = refSize / 2;
|
||||
int centerX = refPosX + radius + xOffset;
|
||||
int centerZ = refPosZ + radius + zOffset;
|
||||
|
||||
// get/create the list of chunks we're going to generate
|
||||
ArrayGridList<ChunkAccess> regionChunks = new ArrayGridList<>(
|
||||
refSize,
|
||||
(x, z) -> this.generateEmptyChunk(
|
||||
x + refPosX + xOffsetFinal,
|
||||
z + refPosZ + zOffsetFinal,
|
||||
chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos));
|
||||
ChunkAccess centerChunk = regionChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
|
||||
|
||||
genEvent.refreshTimeout();
|
||||
DhLitWorldGenRegion region = new DhLitWorldGenRegion(
|
||||
centerX, centerZ,
|
||||
centerChunk,
|
||||
this.params.level, dummyLightEngine, regionChunks,
|
||||
ChunkStatus.STRUCTURE_STARTS, radius,
|
||||
// this method shouldn't be necessary since we're passing in a pre-populated
|
||||
// list of chunks, but just in case
|
||||
(x, z) -> this.generateEmptyChunk(x, z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos)
|
||||
);
|
||||
lightGetterAdaptor.setRegion(region);
|
||||
genEvent.threadedParam.makeStructFeat(region, this.params);
|
||||
|
||||
|
||||
|
||||
//=========================//
|
||||
// create chunk wrappers //
|
||||
// and get existing chunks //
|
||||
//=========================//
|
||||
|
||||
ArrayGridList<ChunkWrapper> chunkWrapperList = new ArrayGridList<>(regionChunks.gridSize);
|
||||
regionChunks.forEachPos((relX, relZ) ->
|
||||
{
|
||||
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY
|
||||
#if MC_VER >= MC_1_17_1 , this.params.level #endif
|
||||
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif
|
||||
);
|
||||
}
|
||||
return newChunk;
|
||||
};
|
||||
totalChunks = new ArrayGridList<>(refSize, (x, z) -> emptyChunkGeneratorFunc.generate(x + refPosX, z + refPosZ));
|
||||
|
||||
int radius = refSize / 2;
|
||||
int centerX = refPosX + radius;
|
||||
int centerZ = refPosZ + radius;
|
||||
|
||||
ChunkAccess centerChunk = totalChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
|
||||
|
||||
genEvent.refreshTimeout();
|
||||
region = new DhLitWorldGenRegion(
|
||||
centerX, centerZ,
|
||||
centerChunk,
|
||||
this.params.level, dummyLightEngine, totalChunks,
|
||||
ChunkStatus.STRUCTURE_STARTS, radius, emptyChunkGeneratorFunc);
|
||||
adaptor.setRegion(region);
|
||||
genEvent.threadedParam.makeStructFeat(region, this.params);
|
||||
|
||||
|
||||
|
||||
//=======================//
|
||||
// create chunk wrappers //
|
||||
//=======================//
|
||||
|
||||
chunkWrapperList = new ArrayGridList<>(totalChunks.gridSize);
|
||||
totalChunks.forEachPos((x, z) ->
|
||||
{
|
||||
ChunkAccess chunk = totalChunks.get(x, z);
|
||||
if (chunk != null)
|
||||
{
|
||||
// wrap the chunk
|
||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
|
||||
chunkWrapperList.set(x, z, chunkWrapper);
|
||||
// ArrayGridList's use relative positions and don't have a center position
|
||||
// so we need to use the offsetFinal to select the correct position
|
||||
DhChunkPos chunkPos = new DhChunkPos(relX + xOffsetFinal, relZ + zOffsetFinal);
|
||||
ChunkAccess chunk = regionChunks.get(relX, relZ);
|
||||
|
||||
// try getting the chunk lighting
|
||||
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
|
||||
if (chunkWrappersByDhPos.containsKey(chunkPos))
|
||||
{
|
||||
chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos()));
|
||||
chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()));
|
||||
chunkWrapper.setUseDhLighting(true);
|
||||
chunkWrapper.setIsDhLightCorrect(true);
|
||||
chunkWrapperList.set(relX, relZ, chunkWrappersByDhPos.get(chunkPos));
|
||||
}
|
||||
else
|
||||
else if (chunk != null)
|
||||
{
|
||||
int k = 0;
|
||||
// wrap the chunk
|
||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
|
||||
chunkWrapperList.set(relX, relZ, chunkWrapper);
|
||||
|
||||
// try setting the wrapper's lighting
|
||||
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
|
||||
{
|
||||
chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos()));
|
||||
chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()));
|
||||
chunkWrapper.setUseDhLighting(true);
|
||||
chunkWrapper.setIsDhLightCorrect(true);
|
||||
}
|
||||
|
||||
chunkWrappersByDhPos.put(chunkPos, chunkWrapper);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// generate chunks //
|
||||
//=================//
|
||||
|
||||
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region);
|
||||
genEvent.timer.nextEvent("cleanup");
|
||||
}
|
||||
catch (StepStructureStart.StructStartCorruptedException f)
|
||||
{
|
||||
genEvent.threadedParam.markAsInvalid();
|
||||
throw (RuntimeException) f.getCause();
|
||||
else //if (chunk == null)
|
||||
{
|
||||
LodUtil.assertNotReach("Programmer Error: No chunk found in grid list, position offset is likely wrong.");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// generate chunks //
|
||||
//=================//
|
||||
|
||||
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region);
|
||||
|
||||
genEvent.timer.nextEvent("cleanup");
|
||||
}
|
||||
}
|
||||
|
||||
ArrayGridList<ChunkWrapper> finalGenChunks = GetCutoutFrom(chunkWrapperList, borderSize);
|
||||
for (int offsetY = 0; offsetY < finalGenChunks.gridSize; offsetY++)
|
||||
|
||||
|
||||
//=========================//
|
||||
// submit generated chunks //
|
||||
//=========================//
|
||||
|
||||
for (DhChunkPos dhChunkPos : chunkWrappersByDhPos.keySet())
|
||||
{
|
||||
for (int offsetX = 0; offsetX < finalGenChunks.gridSize; offsetX++)
|
||||
ChunkWrapper wrappedChunk = chunkWrappersByDhPos.get(dhChunkPos);
|
||||
ChunkAccess target = wrappedChunk.getChunk();
|
||||
if (target instanceof LevelChunk)
|
||||
{
|
||||
ChunkWrapper wrappedChunk = finalGenChunks.get(offsetX, offsetY);
|
||||
ChunkAccess target = wrappedChunk.getChunk();
|
||||
if (target instanceof LevelChunk)
|
||||
{
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
((LevelChunk) target).setLoaded(true);
|
||||
#else
|
||||
((LevelChunk) target).loaded = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!wrappedChunk.isLightCorrect())
|
||||
{
|
||||
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
|
||||
}
|
||||
|
||||
boolean isFull = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
boolean isPartial = target.isOldNoiseGeneration();
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
((LevelChunk) target).setLoaded(true);
|
||||
#else
|
||||
((LevelChunk) target).loaded = true;
|
||||
#endif
|
||||
if (isFull)
|
||||
{
|
||||
LOAD_LOGGER.info("Detected full existing chunk at {}", target.getPos());
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
#if MC_VER >= MC_1_18_2
|
||||
else if (isPartial)
|
||||
{
|
||||
LOAD_LOGGER.info("Detected old existing chunk at {}", target.getPos());
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
#endif
|
||||
else if (ChunkWrapper.getStatus(target) == ChunkStatus.EMPTY)
|
||||
{
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (!wrappedChunk.isLightCorrect())
|
||||
{
|
||||
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
|
||||
}
|
||||
|
||||
boolean isFull = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
boolean isPartial = target.isOldNoiseGeneration();
|
||||
#endif
|
||||
if (isFull)
|
||||
{
|
||||
LOAD_LOGGER.debug("Detected full existing chunk at {}", target.getPos());
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
#if MC_VER >= MC_1_18_2
|
||||
else if (isPartial)
|
||||
{
|
||||
LOAD_LOGGER.debug("Detected old existing chunk at {}", target.getPos());
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
#endif
|
||||
else if (ChunkWrapper.getStatus(target) == ChunkStatus.EMPTY)
|
||||
{
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
genEvent.resultConsumer.accept(wrappedChunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,9 +567,58 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
if (PREF_LOGGER.canMaybeLog())
|
||||
{
|
||||
genEvent.threadedParam.perf.recordEvent(genEvent.timer);
|
||||
PREF_LOGGER.infoInc("{}", genEvent.timer);
|
||||
PREF_LOGGER.debugInc("{}", genEvent.timer);
|
||||
}
|
||||
}
|
||||
private ChunkAccess generateEmptyChunk(
|
||||
int x, int z,
|
||||
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
|
||||
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
|
||||
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos)
|
||||
{
|
||||
ChunkPos chunkPos = new ChunkPos(x, z);
|
||||
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
|
||||
|
||||
if (generatedChunkByDhPos.containsKey(dhChunkPos))
|
||||
{
|
||||
return generatedChunkByDhPos.get(dhChunkPos);
|
||||
}
|
||||
|
||||
|
||||
ChunkAccess newChunk = null;
|
||||
try
|
||||
{
|
||||
// get the chunk
|
||||
CompoundTag chunkData = this.getChunkNbtData(chunkPos);
|
||||
newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
|
||||
|
||||
if (Config.Client.Advanced.LodBuilding.pullLightingForPregeneratedChunks.get())
|
||||
{
|
||||
// attempt to get chunk lighting
|
||||
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
|
||||
if (combinedLights != null)
|
||||
{
|
||||
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
|
||||
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RuntimeException loadChunkError)
|
||||
{
|
||||
// Continue...
|
||||
}
|
||||
|
||||
if (newChunk == null)
|
||||
{
|
||||
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY
|
||||
#if MC_VER >= MC_1_17_1 , this.params.level #endif
|
||||
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif
|
||||
);
|
||||
}
|
||||
|
||||
generatedChunkByDhPos.put(dhChunkPos, newChunk);
|
||||
return newChunk;
|
||||
}
|
||||
private CompoundTag getChunkNbtData(ChunkPos chunkPos)
|
||||
{
|
||||
ServerLevel level = this.params.level;
|
||||
@@ -607,7 +669,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
{
|
||||
try
|
||||
{
|
||||
LOAD_LOGGER.info("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
|
||||
LOAD_LOGGER.debug("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
|
||||
return ChunkLoader.read(level, chunkPos, chunkData);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -637,6 +699,9 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void generateDirect(
|
||||
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border,
|
||||
EDhApiWorldGenerationStep step, DhLitWorldGenRegion region) throws InterruptedException
|
||||
@@ -768,7 +833,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
}
|
||||
}
|
||||
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
|
||||
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - BorderNeeded.get(step)); }
|
||||
//private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); }
|
||||
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, 0); }
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
+6
-22
@@ -43,6 +43,7 @@ public final class GenerationEvent
|
||||
public final int id;
|
||||
public final ThreadedParameters threadedParam;
|
||||
public final DhChunkPos minPos;
|
||||
/** the number of chunks wide this event is */
|
||||
public final int size;
|
||||
public final EDhApiWorldGenerationStep targetGenerationStep;
|
||||
public EventTimer timer = null;
|
||||
@@ -73,10 +74,10 @@ public final class GenerationEvent
|
||||
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
|
||||
ExecutorService worldGeneratorThreadPool)
|
||||
{
|
||||
if (size % 2 == 0)
|
||||
{
|
||||
size += 1; // size must be odd for vanilla world gen regions to work
|
||||
}
|
||||
//if (size % 2 == 0)
|
||||
//{
|
||||
// size += 1; // size must be odd for vanilla world gen regions to work
|
||||
//}
|
||||
|
||||
|
||||
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
|
||||
@@ -93,9 +94,7 @@ public final class GenerationEvent
|
||||
//LOGGER.info("generating [{}]", event.minPos);
|
||||
genEnvironment.generateLodFromList(generationEvent);
|
||||
}
|
||||
catch (InterruptedException ignored)
|
||||
{
|
||||
}
|
||||
catch (InterruptedException ignored) { }
|
||||
finally
|
||||
{
|
||||
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
|
||||
@@ -126,21 +125,6 @@ public final class GenerationEvent
|
||||
return this.future.isCancelled();
|
||||
}
|
||||
|
||||
public boolean tooClose(int minX, int minZ, int width)
|
||||
{
|
||||
int aMinX = this.minPos.x;
|
||||
int aMinZ = this.minPos.z;
|
||||
int aSize = this.size;
|
||||
// Account for required empty chunks in the border
|
||||
aSize += 1;
|
||||
width += 1;
|
||||
// Do a AABB to AABB intersection test
|
||||
return (aMinX + aSize >= minX &&
|
||||
aMinX <= minX + width &&
|
||||
aMinZ + aSize >= minZ &&
|
||||
aMinZ <= minZ + width);
|
||||
}
|
||||
|
||||
public void refreshTimeout()
|
||||
{
|
||||
this.timeoutTime = System.nanoTime();
|
||||
|
||||
+12
-8
@@ -85,7 +85,7 @@ import net.minecraft.world.level.material.Fluids;
|
||||
#if MC_VER == MC_1_20_6
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkType;
|
||||
#elif MC_VER == MC_1_21
|
||||
#elif MC_VER >= MC_1_21_1
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkType;
|
||||
#endif
|
||||
@@ -252,8 +252,8 @@ public class ChunkLoader
|
||||
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
|
||||
#endif
|
||||
#endif
|
||||
int i = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
|
||||
LevelChunkSection[] chunkSections = new LevelChunkSection[i];
|
||||
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
|
||||
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
|
||||
|
||||
boolean isLightOn = chunkData.getBoolean("isLightOn");
|
||||
boolean hasSkyLight = level.dimensionType().hasSkyLight();
|
||||
@@ -288,7 +288,7 @@ public class ChunkLoader
|
||||
#endif
|
||||
|
||||
blockStateContainer = tagSection.contains("block_states", 10)
|
||||
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string))
|
||||
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
||||
#if MC_VER < MC_1_20_6
|
||||
.getOrThrow(false, LOGGER::error)
|
||||
#else
|
||||
@@ -303,7 +303,7 @@ public class ChunkLoader
|
||||
#else
|
||||
|
||||
biomeContainer = tagSection.contains("biomes", 10)
|
||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, i, (String) string))
|
||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
|
||||
#if MC_VER < MC_1_20_6
|
||||
.getOrThrow(false, LOGGER::error)
|
||||
#else
|
||||
@@ -325,7 +325,7 @@ public class ChunkLoader
|
||||
}
|
||||
private static
|
||||
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
|
||||
#elif MC_VER < MC_1_21 ChunkType
|
||||
#elif MC_VER < MC_1_21_1 ChunkType
|
||||
#else ChunkType #endif
|
||||
readChunkType(CompoundTag tagLevel)
|
||||
{
|
||||
@@ -495,9 +495,13 @@ public class ChunkLoader
|
||||
}
|
||||
}
|
||||
|
||||
private static void logErrors(ChunkPos chunkPos, int i, String string)
|
||||
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGER.error("Distant Horizons: Recoverable errors when loading section [" + chunkPos.x + ", " + i + ", " + chunkPos.z + "]: " + string);
|
||||
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
}
|
||||
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
|
||||
#if MC_VER >= MC_1_21
|
||||
#if MC_VER >= MC_1_21_1
|
||||
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
+18
-3
@@ -20,7 +20,6 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
@@ -63,7 +62,7 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.*;
|
||||
#endif
|
||||
|
||||
#if MC_VER == MC_1_21
|
||||
#if MC_VER >= MC_1_21_1
|
||||
import net.minecraft.util.StaticCache2D;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
@@ -127,7 +126,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
{
|
||||
#if MC_VER == MC_1_16_5
|
||||
super(serverLevel, chunkList);
|
||||
#elif MC_VER < MC_1_21
|
||||
#elif MC_VER < MC_1_21_1
|
||||
super(serverLevel, chunkList, chunkStatus, writeRadius);
|
||||
#else
|
||||
super(serverLevel,
|
||||
@@ -235,6 +234,22 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* This needs to be manually overridden to make sure Lithium 0.11.2 and lower
|
||||
* don't try to get null chunks. <br><br>
|
||||
*
|
||||
* Problematic Lithium code was removed in 0.13.0 (MC 1.21.1) and higher: <br>
|
||||
* https://github.com/CaffeineMC/lithium-fabric/commit/b7cfd53a1ed0197e1d13dea2799b898eb52ecab3
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos blockPos)
|
||||
{
|
||||
int chunkX = SectionPos.blockToSectionCoord(blockPos.getX());
|
||||
int chunkZ = SectionPos.blockToSectionCoord(blockPos.getZ());
|
||||
return this.getChunk(chunkX, chunkZ).getBlockState(blockPos);
|
||||
}
|
||||
|
||||
/** Skip BlockEntity stuff. They aren't needed for our use case. */
|
||||
@Override
|
||||
public boolean addFreshEntity(@NotNull Entity entity) { return true; }
|
||||
|
||||
+8
-3
@@ -61,9 +61,14 @@ public final class StepBiomes
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (!chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
@@ -84,7 +89,7 @@ public final class StepBiomes
|
||||
#elif MC_VER < MC_1_19_4
|
||||
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#elif MC_VER < MC_1_21
|
||||
#elif MC_VER < MC_1_21_1
|
||||
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#else
|
||||
|
||||
+8
-4
@@ -59,10 +59,14 @@ public final class StepFeatures
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (!chunkWrapper.getStatus().isOrAfter(STATUS)
|
||||
&& chunk instanceof ProtoChunk)
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
@@ -76,7 +80,7 @@ public final class StepFeatures
|
||||
worldGenRegion.setOverrideCenter(chunk.getPos());
|
||||
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat);
|
||||
#else
|
||||
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().x, chunkWrapper.getChunkPos().z))
|
||||
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ()))
|
||||
{
|
||||
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion));
|
||||
}
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ public final class StepNoise
|
||||
continue;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
@@ -87,7 +87,7 @@ public final class StepNoise
|
||||
#elif MC_VER < MC_1_19_2
|
||||
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#elif MC_VER < MC_1_21
|
||||
#elif MC_VER < MC_1_21_1
|
||||
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), this.environment.params.randomState,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#else
|
||||
|
||||
+7
-2
@@ -59,9 +59,14 @@ public final class StepStructureReference
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (!chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
|
||||
+10
-5
@@ -76,9 +76,14 @@ public final class StepStructureStart
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (!chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
@@ -88,12 +93,12 @@ public final class StepStructureStart
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
if (environment.params.worldGenSettings.generateFeatures())
|
||||
if (this.environment.params.worldGenSettings.generateFeatures())
|
||||
{
|
||||
#elif MC_VER < MC_1_19_4
|
||||
if (environment.params.worldGenSettings.generateStructures()) {
|
||||
if (this.environment.params.worldGenSettings.generateStructures()) {
|
||||
#else
|
||||
if (environment.params.worldOptions.generateStructures())
|
||||
if (this.environment.params.worldOptions.generateStructures())
|
||||
{
|
||||
#endif
|
||||
for (ChunkAccess chunk : chunksToDo)
|
||||
|
||||
+7
-2
@@ -58,9 +58,14 @@ public final class StepSurface
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (!chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
#if MC_VER < MC_1_21
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
|
||||
+1
-1
Submodule coreSubProjects updated: 28e1eaae77...57c5b2d5fc
@@ -29,16 +29,14 @@ import com.mojang.blaze3d.platform.InputConstants;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor;
|
||||
//import io.netty.buffer.ByteBuf;
|
||||
import net.fabricmc.api.EnvType;
|
||||
@@ -55,6 +53,7 @@ import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import java.nio.FloatBuffer;
|
||||
#endif
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
@@ -118,11 +117,15 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
|
||||
// ClientChunkLoadEvent
|
||||
ClientChunkEvents.CHUNK_LOAD.register((level, chunk) ->
|
||||
{
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
|
||||
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
|
||||
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
|
||||
}
|
||||
});
|
||||
|
||||
// (kinda) block break event
|
||||
// Since fabric doesn't have a client-side break-block API event, this is the next best thing
|
||||
AttackBlockCallback.EVENT.register((player, level, interactionHand, blockPos, direction) ->
|
||||
{
|
||||
// if we have access to the server, use the chunk save event instead
|
||||
@@ -130,48 +133,26 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
|
||||
{
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ()))
|
||||
{
|
||||
// Since fabric doesn't have a client-side break-block API event, this is the next best thing
|
||||
ChunkAccess chunk = level.getChunk(blockPos);
|
||||
if (chunk != null)
|
||||
// executor to prevent locking up the render/event thread
|
||||
// if the getChunk() takes longer than expected
|
||||
// (which can be caused by certain mods)
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor != null)
|
||||
{
|
||||
//LOGGER.trace("attack block at blockPos: " + blockPos);
|
||||
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
|
||||
SharedApi.INSTANCE.chunkBlockChangedEvent(
|
||||
new ChunkWrapper(chunk, level, wrappedLevel),
|
||||
wrappedLevel
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't stop the callback
|
||||
return InteractionResult.PASS;
|
||||
});
|
||||
|
||||
// (kinda) block place event
|
||||
UseBlockCallback.EVENT.register((player, level, hand, hitResult) ->
|
||||
{
|
||||
// if we have access to the server, use the chunk save event instead
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
|
||||
{
|
||||
// Since fabric doesn't have a client-side place-block API event, this is the next best thing
|
||||
if (hitResult.getType() == HitResult.Type.BLOCK
|
||||
&& !hitResult.isInside())
|
||||
{
|
||||
ChunkAccess chunk = level.getChunk(hitResult.getBlockPos());
|
||||
if (chunk != null)
|
||||
executor.execute(() ->
|
||||
{
|
||||
//LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos());
|
||||
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
|
||||
SharedApi.INSTANCE.chunkBlockChangedEvent(
|
||||
new ChunkWrapper(chunk, level, wrappedLevel),
|
||||
wrappedLevel
|
||||
);
|
||||
}
|
||||
ChunkAccess chunk = level.getChunk(blockPos);
|
||||
if (chunk != null)
|
||||
{
|
||||
//LOGGER.trace("attack block at blockPos: " + blockPos);
|
||||
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
|
||||
SharedApi.INSTANCE.chunkBlockChangedEvent(
|
||||
new ChunkWrapper(chunk, level, wrappedLevel),
|
||||
wrappedLevel
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,12 +161,45 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
|
||||
return InteractionResult.PASS;
|
||||
});
|
||||
|
||||
|
||||
// Client Chunk Save
|
||||
ClientChunkEvents.CHUNK_UNLOAD.register((level, chunk) ->
|
||||
// (kinda) block place event
|
||||
// Since fabric doesn't have a client-side place-block API event, this is the next best thing
|
||||
UseBlockCallback.EVENT.register((player, level, hand, hitResult) ->
|
||||
{
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
|
||||
SharedApi.INSTANCE.chunkUnloadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
|
||||
// if we have access to the server, use the chunk save event instead
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
if (hitResult.getType() == HitResult.Type.BLOCK
|
||||
&& !hitResult.isInside())
|
||||
{
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
|
||||
{
|
||||
// executor to prevent locking up the render/event thread
|
||||
// if the getChunk() takes longer than expected
|
||||
// (which can be caused by certain mods)
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor != null)
|
||||
{
|
||||
executor.execute(() ->
|
||||
{
|
||||
ChunkAccess chunk = level.getChunk(hitResult.getBlockPos());
|
||||
if (chunk != null)
|
||||
{
|
||||
//LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos());
|
||||
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
|
||||
SharedApi.INSTANCE.chunkBlockChangedEvent(
|
||||
new ChunkWrapper(chunk, level, wrappedLevel),
|
||||
wrappedLevel
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't stop the callback
|
||||
return InteractionResult.PASS;
|
||||
});
|
||||
|
||||
|
||||
@@ -208,14 +222,14 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
|
||||
this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()),
|
||||
modelViewMatrix,
|
||||
projectionMatrix,
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
renderContext.tickDelta()
|
||||
#else
|
||||
renderContext.tickCounter().getGameTimeDeltaTicks()
|
||||
#endif
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
// Debug keyboard event
|
||||
// FIXME: Use better hooks so it doesn't trigger key press events in text boxes
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client ->
|
||||
|
||||
@@ -124,7 +124,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
|
||||
|
||||
#if MC_VER >= MC_1_20_1
|
||||
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
|
||||
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); // FIXME: This is a tmp fix for sodium 0.5.0, and 0.5.1. This is fixed in sodium 0.5.2
|
||||
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false);
|
||||
#endif
|
||||
|
||||
if (ConfigBase.INSTANCE == null)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.seibel.distanthorizons.fabric;
|
||||
|
||||
import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister;
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
|
||||
import com.seibel.distanthorizons.common.AbstractModInitializer;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
|
||||
@@ -10,6 +12,7 @@ import com.seibel.distanthorizons.core.api.internal.ServerApi;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
@@ -75,6 +78,14 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
|
||||
// ServerTickEvent
|
||||
ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent());
|
||||
|
||||
|
||||
// can be enabled to test world gen overrides without having to build a separate API project
|
||||
if (false)
|
||||
{
|
||||
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent());
|
||||
}
|
||||
|
||||
|
||||
// ServerWorldLoadEvent
|
||||
//TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?)
|
||||
ServerLifecycleEvents.SERVER_STARTING.register((server) ->
|
||||
|
||||
+7
-11
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.fabric.mixins.client;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#if MC_VER < MC_1_19_4
|
||||
import com.mojang.math.Matrix4f;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
#else
|
||||
import org.joml.Matrix4f;
|
||||
#endif
|
||||
@@ -29,23 +30,17 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@@ -99,7 +94,7 @@ public class MixinLevelRenderer
|
||||
#if MC_VER == MC_1_16_5
|
||||
// get the matrices from the OpenGL fixed pipeline
|
||||
float[] mcProjMatrixRaw = new float[16];
|
||||
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
|
||||
GL32.glGetFloatv(GL32.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
|
||||
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
|
||||
mcProjectionMatrix.transpose();
|
||||
|
||||
@@ -110,17 +105,18 @@ public class MixinLevelRenderer
|
||||
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
|
||||
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
|
||||
#else
|
||||
// get the matrices directly from MC
|
||||
// MC combined the model view and projection matricies
|
||||
Mat4f mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix);
|
||||
Mat4f mcProjectionMatrix = new Mat4f();
|
||||
mcProjectionMatrix.setIdentity();
|
||||
#endif
|
||||
|
||||
if (renderType.equals(RenderType.translucent())) {
|
||||
if (renderType.equals(RenderType.translucent()))
|
||||
{
|
||||
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level),
|
||||
mcModelViewMatrix,
|
||||
mcProjectionMatrix,
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
Minecraft.getInstance().getFrameTime()
|
||||
#else
|
||||
Minecraft.getInstance().getTimer().getRealtimeDeltaTicks()
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
import net.minecraft.client.gui.screens.OptionsScreen;
|
||||
#else
|
||||
import net.minecraft.client.gui.screens.options.OptionsScreen;
|
||||
@@ -64,7 +64,7 @@ public class MixinOptionsScreen extends Screen
|
||||
/** Texture used for the config opening button */
|
||||
@Unique
|
||||
private static final ResourceLocation ICON_TEXTURE =
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
|
||||
|
||||
+2
-60
@@ -1,8 +1,6 @@
|
||||
package com.seibel.distanthorizons.fabric.mixins.server;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.ServerApi;
|
||||
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
@@ -32,62 +30,6 @@ public class MixinChunkMap
|
||||
// don't need the chunk(s) before MC has finished saving them
|
||||
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
|
||||
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
|
||||
{
|
||||
// true means a chunk was saved to disk
|
||||
if (ci.getReturnValue())
|
||||
{
|
||||
// TODO is this validation necessary since we are checking above if
|
||||
// the callback return value should state if the chunk was actually saved or not?
|
||||
// Do we trust it to always be correct?
|
||||
|
||||
//=====================================//
|
||||
// corrupt/incomplete chunk validation //
|
||||
//=====================================//
|
||||
|
||||
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
|
||||
// this logic should prevent that from happening
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//==================//
|
||||
// biome validation //
|
||||
//==================//
|
||||
|
||||
// some chunks may be missing their biomes, which cause issues when attempting to save them
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
if (chunk.getBiomes() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
try
|
||||
{
|
||||
// this will throw an exception if the biomes aren't set up
|
||||
chunk.getNoiseBiome(0,0,0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
ServerApi.INSTANCE.serverChunkSaveEvent(
|
||||
new ChunkWrapper(chunk, this.level, ServerLevelWrapper.getWrapper(this.level)),
|
||||
ServerLevelWrapper.getWrapper(this.level)
|
||||
);
|
||||
}
|
||||
}
|
||||
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
|
||||
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.seibel.distanthorizons.fabric.testing;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
@Override
|
||||
public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event)
|
||||
{
|
||||
LOGGER.info("DH Level: ["+event.value.levelWrapper.getDimensionType()+"] loaded.");
|
||||
|
||||
// Note: whenever you use a wrapper method on a new Minecraft version it is recommended that you
|
||||
// call wrapper.getClass() to determine which object the API will return before you try casting it.
|
||||
ServerLevel level = (ServerLevel) event.value.levelWrapper.getWrappedMcObject();
|
||||
|
||||
// override the core DH world generator for this level
|
||||
IDhApiWorldGenerator exampleWorldGen = new TestWorldGenerator(level);
|
||||
DhApi.worldGenOverrides.registerWorldGeneratorOverride(event.value.levelWrapper, exampleWorldGen);
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package com.seibel.distanthorizons.fabric.testing;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratorReturnType;
|
||||
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.AbstractDhApiChunkWorldGenerator;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
|
||||
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class TestWorldGenerator extends AbstractDhApiChunkWorldGenerator
|
||||
{
|
||||
private final ServerLevel level;
|
||||
private final IDhApiLevelWrapper levelWrapper;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public TestWorldGenerator(ServerLevel level)
|
||||
{
|
||||
this.level = level;
|
||||
this.levelWrapper = ServerLevelWrapper.getWrapper(level);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// properties //
|
||||
//============//
|
||||
|
||||
@Override
|
||||
public EDhApiWorldGeneratorReturnType getReturnType() { return EDhApiWorldGeneratorReturnType.API_CHUNKS; }
|
||||
|
||||
@Override
|
||||
public boolean runApiChunkValidation() { return true; }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// chunk generation //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public Object[] generateChunk(int chunkX, int chunkZ, EDhApiDistantGeneratorMode eDhApiDistantGeneratorMode)
|
||||
{
|
||||
ChunkAccess chunk = this.level.getChunk(chunkX, chunkZ);
|
||||
return new Object[] { chunk, this.level };
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhApiChunk generateApiChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode)
|
||||
{
|
||||
// this test is only validated for 1.18.2 and up
|
||||
// (and it is only needed when testing world gen overrides/API chunks, so it isn't normally needed)
|
||||
#if MC_VER >= MC_1_18_2
|
||||
ChunkAccess chunk = this.level.getChunk(chunkPosX, chunkPosZ);
|
||||
|
||||
|
||||
int minBuildHeight = this.level.getMinBuildHeight();
|
||||
int maxBuildHeight = this.level.getMaxBuildHeight();
|
||||
|
||||
DhApiChunk apiChunk = DhApiChunk.create(chunkPosX, chunkPosZ, minBuildHeight, maxBuildHeight);
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
for (int z = 0; z < 16; z++)
|
||||
{
|
||||
ArrayList<DhApiTerrainDataPoint> dataPoints = new ArrayList<>();
|
||||
|
||||
IDhApiBlockStateWrapper block = null;
|
||||
IDhApiBiomeWrapper biome = null;
|
||||
|
||||
for (int y = minBuildHeight; y < maxBuildHeight; y++)
|
||||
{
|
||||
block = DhApi.Delayed.wrapperFactory.getBlockStateWrapper(new Object[]{chunk.getBlockState(new BlockPos(x, y, z))}, this.levelWrapper);
|
||||
biome = DhApi.Delayed.wrapperFactory.getBiomeWrapper(new Object[]{chunk.getNoiseBiome(x, y, z)}, this.levelWrapper);
|
||||
dataPoints.add(DhApiTerrainDataPoint.create((byte) 0, 0, 15, y, y + 1, block, biome));
|
||||
}
|
||||
|
||||
apiChunk.setDataPoints(x, z, dataPoints);
|
||||
}
|
||||
}
|
||||
return apiChunk;
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preGeneratorTaskStart() { /* do nothing */ }
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// cleanup //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public void close() { /* do nothing */ }
|
||||
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IBCLibAcces
|
||||
#elif MC_VER == MC_1_18_2
|
||||
import ru.bclib.config.ClientConfig;
|
||||
import ru.bclib.config.Configs;
|
||||
#elif MC_VER < MC_1_21
|
||||
#elif MC_VER < MC_1_21_1
|
||||
import org.betterx.bclib.config.ClientConfig;
|
||||
import org.betterx.bclib.config.Configs;
|
||||
#endif
|
||||
|
||||
+90
-95
@@ -19,20 +19,18 @@
|
||||
|
||||
package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
|
||||
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
#if MC_VER < MC_1_20_1
|
||||
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
#endif
|
||||
#if MC_VER < MC_1_17_1
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
@@ -40,103 +38,100 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
#else
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
#else
|
||||
#endif
|
||||
|
||||
public class SodiumAccessor implements ISodiumAccessor
|
||||
{
|
||||
private final IWrapperFactory factory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
private final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
public IClientLevelWrapper levelWrapper;
|
||||
public Mat4f mcModelViewMatrix;
|
||||
public Mat4f mcProjectionMatrix;
|
||||
public float partialTicks;
|
||||
|
||||
@Override
|
||||
public String getModName()
|
||||
{
|
||||
return "Sodium-Fabric";
|
||||
}
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public HashSet<DhChunkPos> getNormalRenderedChunks()
|
||||
{
|
||||
SodiumWorldRenderer renderer = SodiumWorldRenderer.instance();
|
||||
LevelHeightAccessor height = Minecraft.getInstance().level;
|
||||
|
||||
#if MC_VER >= MC_1_20_1
|
||||
// TODO: This is just a tmp solution, use a proper solution later
|
||||
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
|
||||
return (renderer.isBoxVisible(
|
||||
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
|
||||
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
|
||||
}).collect(Collectors.toCollection(HashSet::new));
|
||||
#elif MC_VER >= MC_1_18_2
|
||||
// 0b11 = Lighted chunk & loaded chunk
|
||||
return renderer.getChunkTracker().getChunks(0b00).filter(
|
||||
(long l) -> {
|
||||
return true;
|
||||
}).mapToObj(DhChunkPos::new).collect(Collectors.toCollection(HashSet::new));
|
||||
#else
|
||||
// TODO: Maybe use a mixin to make this more efficient, and maybe ignore changes behind the camera
|
||||
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
|
||||
return (renderer.isBoxVisible(
|
||||
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
|
||||
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
|
||||
}).collect(Collectors.toCollection(HashSet::new));
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
@Override
|
||||
public HashSet<DhChunkPos> getNormalRenderedChunks() {
|
||||
SodiumWorldRenderer renderer = SodiumWorldRenderer.getInstance();
|
||||
LevelAccessor height = Minecraft.getInstance().level;
|
||||
// TODO: Maybe use a mixin to make this more efficient
|
||||
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
|
||||
FakeChunkEntity AABB = new FakeChunkEntity(chunk.x, chunk.z, height.getMaxBuildHeight());
|
||||
return (renderer.isEntityVisible(AABB));
|
||||
}).collect(Collectors.toCollection(HashSet::new));
|
||||
}
|
||||
|
||||
private static class FakeChunkEntity extends Entity {
|
||||
public int cx;
|
||||
public int cz;
|
||||
public int my;
|
||||
public FakeChunkEntity(int chunkX, int chunkZ, int maxHeight) {
|
||||
super(EntityType.AREA_EFFECT_CLOUD, null);
|
||||
cx = chunkX;
|
||||
cz = chunkZ;
|
||||
my = maxHeight;
|
||||
}
|
||||
@Override
|
||||
public AABB getBoundingBoxForCulling() {
|
||||
return new AABB(cx*16+1, 1, cz*16+1,
|
||||
cx*16+15, my-1, cz*16+15);
|
||||
}
|
||||
@Override
|
||||
protected void defineSynchedData() {}
|
||||
@Override
|
||||
protected void readAdditionalSaveData(CompoundTag paramCompoundTag) {}
|
||||
@Override
|
||||
protected void addAdditionalSaveData(CompoundTag paramCompoundTag) {}
|
||||
@Override
|
||||
public Packet<?> getAddEntityPacket() {
|
||||
throw new UnsupportedOperationException("This is a FAKE CHUNK ENTITY... For tricking the Sodium to check a AABB.");
|
||||
}
|
||||
}
|
||||
#if MC_VER >= MC_1_20_1
|
||||
private static MethodHandle setFogOcclusionMethod;
|
||||
private static Object sodiumPerformanceOptions;
|
||||
#endif
|
||||
|
||||
/** A temporary overwrite for a config in sodium 0.5 to fix their terrain from showing, will be removed once a proper fix is added */
|
||||
// FIXME
|
||||
|
||||
|
||||
|
||||
//======================//
|
||||
// mod accessor methods //
|
||||
//======================//
|
||||
|
||||
@Override
|
||||
public void setFogOcclusion(boolean b)
|
||||
public String getModName() { return "Sodium-Fabric"; }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// sodium methods //
|
||||
//================//
|
||||
|
||||
/** An overwrite for a config in sodium 0.5 to fix their terrain from showing */
|
||||
@Override
|
||||
public void setFogOcclusion(boolean occlusionEnabled)
|
||||
{
|
||||
#if MC_VER >= MC_1_20_1
|
||||
me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b;
|
||||
try
|
||||
{
|
||||
if (sodiumPerformanceOptions == null)
|
||||
{
|
||||
boolean sodiumV6 = classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
|
||||
if (!sodiumV6)
|
||||
{
|
||||
// sodium 0.5
|
||||
|
||||
Class<?> optionsClass = Class.forName("me.jellysquid.mods.sodium.client.gui.SodiumGameOptions");
|
||||
Object basicOptions = MethodHandles.lookup().findStatic(
|
||||
Class.forName("me.jellysquid.mods.sodium.client.SodiumClientMod"),
|
||||
"options", MethodType.methodType(optionsClass)).invoke();
|
||||
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
|
||||
setFogOcclusionMethod = MethodHandles.lookup()
|
||||
.findSetter(Class.forName(
|
||||
"me.jellysquid.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
|
||||
"useFogOcclusion", boolean.class);
|
||||
|
||||
// alternate option if referencing Sodium 0.5 directly
|
||||
//me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
// sodium 0.6
|
||||
|
||||
Class<?> optionsClass = Class.forName("net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions");
|
||||
Object basicOptions = MethodHandles.lookup().findStatic(
|
||||
Class.forName("net.caffeinemc.mods.sodium.client.SodiumClientMod"),
|
||||
"options", MethodType.methodType(optionsClass)).invoke();
|
||||
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
|
||||
setFogOcclusionMethod = MethodHandles.lookup()
|
||||
.findSetter(Class.forName(
|
||||
"net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
|
||||
"useFogOcclusion", boolean.class);
|
||||
}
|
||||
}
|
||||
|
||||
setFogOcclusionMethod.invoke(sodiumPerformanceOptions, occlusionEnabled);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
private static boolean classPresent(String className)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName(className);
|
||||
return true;
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
@@ -65,6 +66,8 @@ import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* This handles all events sent to the client,
|
||||
* and is the starting point for most of the mod.
|
||||
@@ -173,61 +176,76 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
|
||||
@SubscribeEvent
|
||||
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
|
||||
{
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
return;
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
LevelAccessor level = event.getWorld();
|
||||
#else
|
||||
LevelAccessor level = event.getLevel();
|
||||
#endif
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor != null)
|
||||
{
|
||||
executor.execute(() ->
|
||||
{
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
LevelAccessor level = event.getWorld();
|
||||
#else
|
||||
LevelAccessor level = event.getLevel();
|
||||
#endif
|
||||
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
|
||||
{
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
return;
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
LevelAccessor level = event.getWorld();
|
||||
#else
|
||||
LevelAccessor level = event.getLevel();
|
||||
#endif
|
||||
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor != null)
|
||||
{
|
||||
executor.execute(() ->
|
||||
{
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
LevelAccessor level = event.getWorld();
|
||||
#else
|
||||
LevelAccessor level = event.getLevel();
|
||||
#endif
|
||||
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
}
|
||||
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
|
||||
{
|
||||
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(level);
|
||||
SharedApi.INSTANCE.chunkBlockChangedEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void clientChunkLoadEvent(ChunkEvent.Load event)
|
||||
{
|
||||
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
|
||||
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void clientChunkUnloadEvent(ChunkEvent.Unload event)
|
||||
{
|
||||
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
|
||||
SharedApi.INSTANCE.chunkUnloadEvent(chunk, wrappedLevel);
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
|
||||
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ package com.seibel.distanthorizons.forge;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.seibel.distanthorizons.common.AbstractModInitializer;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
@@ -104,6 +106,20 @@ public class ForgeMain extends AbstractModInitializer
|
||||
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
|
||||
() -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
|
||||
#endif
|
||||
|
||||
|
||||
if (Config.Client.Advanced.Logging.showModCompatibilityWarningsOnStartup.get())
|
||||
{
|
||||
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
|
||||
if (modChecker.isModLoaded("alexscaves"))
|
||||
{
|
||||
String message =
|
||||
// orange text
|
||||
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
|
||||
"You may have to change Alex's config for DH to render. ";
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -137,14 +137,6 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
|
||||
this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void serverChunkSaveEvent(ChunkEvent.Unload event)
|
||||
{
|
||||
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
|
||||
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
|
||||
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+5
-6
@@ -22,6 +22,10 @@ package com.seibel.distanthorizons.forge.mixins.client;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#if MC_VER < MC_1_19_4
|
||||
import com.mojang.math.Matrix4f;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
#else
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -34,13 +38,9 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -50,7 +50,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.seibel.distanthorizons.forge.mixins.server;
|
||||
|
||||
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ChunkMap.class)
|
||||
public class MixinChunkMap
|
||||
{
|
||||
|
||||
@Unique
|
||||
private static final String CHUNK_SERIALIZER_WRITE
|
||||
= "Lnet/minecraft/world/level/chunk/storage/ChunkSerializer;write(" +
|
||||
"Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ChunkAccess;)" +
|
||||
"Lnet/minecraft/nbt/CompoundTag;";
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
ServerLevel level;
|
||||
|
||||
// firing at INVOKE causes issues with C2ME and is probably unnecessary since we
|
||||
// don't need the chunk(s) before MC has finished saving them
|
||||
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
|
||||
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
|
||||
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
|
||||
|
||||
}
|
||||
@@ -5,7 +5,8 @@
|
||||
"mixins": [
|
||||
"server.MixinUtilBackgroundThread",
|
||||
"server.MixinChunkGenerator",
|
||||
"server.MixinTFChunkGenerator"
|
||||
"server.MixinTFChunkGenerator",
|
||||
"server.MixinChunkMap"
|
||||
],
|
||||
"client": [
|
||||
"client.MixinClientPacketListener",
|
||||
|
||||
+3
-3
@@ -5,8 +5,8 @@ org.gradle.caching=true
|
||||
|
||||
# Mod Info
|
||||
mod_name=DistantHorizons
|
||||
mod_version=2.1.2-a
|
||||
api_version=2.1.0
|
||||
mod_version=2.2.1-a
|
||||
api_version=3.0.0
|
||||
maven_group=com.seibel.distanthorizons
|
||||
mod_readable_name=Distant Horizons
|
||||
mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow.
|
||||
@@ -49,7 +49,7 @@ versionStr=
|
||||
|
||||
# This defines what MC version Intellij will use for the preprocessor
|
||||
# and what version is used automatically by build and run commands
|
||||
mcVer=1.21
|
||||
mcVer=1.21.1
|
||||
|
||||
# Defines the maximum amount of memory Minecraft is allowed when run in a development environment
|
||||
#minecraftMemoryJavaArg="-Xmx4G"
|
||||
|
||||
+46
-36
@@ -28,14 +28,14 @@ import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
//import io.netty.buffer.ByteBuf;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
@@ -63,6 +63,8 @@ import org.lwjgl.opengl.GL32;
|
||||
import net.neoforged.neoforge.event.TickEvent;
|
||||
#else
|
||||
import net.neoforged.neoforge.client.event.ClientTickEvent;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -164,32 +166,56 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
|
||||
@SubscribeEvent
|
||||
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
|
||||
{
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
return;
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// executor to prevent locking up the render/event thread
|
||||
// if the getChunk() takes longer than expected
|
||||
// (which can be caused by certain mods)
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor != null)
|
||||
{
|
||||
executor.execute(() ->
|
||||
{
|
||||
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
|
||||
|
||||
LevelAccessor level = event.getLevel();
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
|
||||
|
||||
LevelAccessor level = event.getLevel();
|
||||
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
|
||||
{
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
return;
|
||||
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// executor to prevent locking up the render/event thread
|
||||
// if the getChunk() takes longer than expected
|
||||
// (which can be caused by certain mods)
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor != null)
|
||||
{
|
||||
executor.execute(() ->
|
||||
{
|
||||
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
|
||||
|
||||
LevelAccessor level = event.getLevel();
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
|
||||
|
||||
LevelAccessor level = event.getLevel();
|
||||
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onBlockChangeEvent(level, chunk);
|
||||
}
|
||||
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
|
||||
{
|
||||
@@ -198,22 +224,6 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void clientChunkLoadEvent(ChunkEvent.Load event)
|
||||
{
|
||||
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
|
||||
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void clientChunkUnloadEvent(ChunkEvent.Unload event)
|
||||
{
|
||||
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
|
||||
SharedApi.INSTANCE.chunkUnloadEvent(chunk, wrappedLevel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// key bindings //
|
||||
|
||||
@@ -22,6 +22,8 @@ package com.seibel.distanthorizons.neoforge;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.seibel.distanthorizons.common.AbstractModInitializer;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
|
||||
@@ -83,6 +85,20 @@ public class NeoforgeMain extends AbstractModInitializer
|
||||
// TODO fix potential null pointer
|
||||
() -> (client, parent) -> GetConfigScreen.getScreen(parent));
|
||||
#endif
|
||||
|
||||
|
||||
if (Config.Client.Advanced.Logging.showModCompatibilityWarningsOnStartup.get())
|
||||
{
|
||||
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
|
||||
if (modChecker.isModLoaded("alexscaves"))
|
||||
{
|
||||
String message =
|
||||
// orange text
|
||||
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
|
||||
"You may have to change Alex's config for DH to render. ";
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -122,14 +122,6 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
|
||||
this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void serverChunkSaveEvent(ChunkEvent.Unload event)
|
||||
{
|
||||
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
|
||||
|
||||
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
|
||||
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -24,6 +24,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#if MC_VER < MC_1_19_4
|
||||
import com.mojang.math.Matrix4f;
|
||||
#else
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.neoforge.NeoforgeClientProxy;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -36,7 +37,6 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
@@ -120,7 +120,7 @@ public class MixinLevelRenderer
|
||||
|
||||
|
||||
float frameTime;
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
frameTime = Minecraft.getInstance().getFrameTime();
|
||||
#else
|
||||
frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
import net.minecraft.client.gui.screens.OptionsScreen;
|
||||
#else
|
||||
import net.minecraft.client.gui.screens.options.OptionsScreen;
|
||||
@@ -64,7 +64,7 @@ public class MixinOptionsScreen extends Screen
|
||||
/** Texture used for the config opening button */
|
||||
@Unique
|
||||
private static final ResourceLocation ICON_TEXTURE =
|
||||
#if MC_VER < MC_1_21
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
|
||||
#else
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.seibel.distanthorizons.neoforge.mixins.server;
|
||||
|
||||
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ChunkMap.class)
|
||||
public class MixinChunkMap
|
||||
{
|
||||
|
||||
@Unique
|
||||
private static final String CHUNK_SERIALIZER_WRITE
|
||||
= "Lnet/minecraft/world/level/chunk/storage/ChunkSerializer;write(" +
|
||||
"Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ChunkAccess;)" +
|
||||
"Lnet/minecraft/nbt/CompoundTag;";
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
ServerLevel level;
|
||||
|
||||
// firing at INVOKE causes issues with C2ME and is probably unnecessary since we
|
||||
// don't need the chunk(s) before MC has finished saving them
|
||||
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
|
||||
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
|
||||
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
|
||||
|
||||
}
|
||||
@@ -5,7 +5,8 @@
|
||||
"mixins": [
|
||||
"server.MixinUtilBackgroundThread",
|
||||
"server.MixinChunkGenerator",
|
||||
"server.MixinTFChunkGenerator"
|
||||
"server.MixinTFChunkGenerator",
|
||||
"server.MixinChunkMap"
|
||||
],
|
||||
"client": [
|
||||
"client.MixinClientPacketListener",
|
||||
|
||||
@@ -20,7 +20,7 @@ fabric_api_version=0.90.4+1.20.1
|
||||
immersive_portals_version=
|
||||
canvas_version=
|
||||
|
||||
fabric_incompatibility_list={ "iris": "<=1.6.20" }
|
||||
fabric_incompatibility_list={ "iris": "<=1.7.4" }
|
||||
fabric_recommend_list={}
|
||||
|
||||
# Fabric mod run
|
||||
|
||||
@@ -20,7 +20,7 @@ fabric_api_version=0.90.4+1.20.2
|
||||
immersive_portals_version=
|
||||
canvas_version=
|
||||
|
||||
fabric_incompatibility_list={ "iris": "<=1.6.20" }
|
||||
fabric_incompatibility_list={ "iris": "<=1.7.4" }
|
||||
fabric_recommend_list={}
|
||||
|
||||
# Fabric mod run
|
||||
|
||||
@@ -21,7 +21,7 @@ fabric_api_version=0.91.2+1.20.4
|
||||
immersive_portals_version=
|
||||
canvas_version=
|
||||
|
||||
fabric_incompatibility_list={ "iris": "<=1.6.20" }
|
||||
fabric_incompatibility_list={ "iris": "<=1.7.4" }
|
||||
fabric_recommend_list={}
|
||||
|
||||
# Fabric mod run
|
||||
|
||||
@@ -21,7 +21,7 @@ fabric_api_version=0.97.8+1.20.6
|
||||
immersive_portals_version=
|
||||
canvas_version=
|
||||
|
||||
fabric_incompatibility_list={ "iris": "<=1.6.20" }
|
||||
fabric_incompatibility_list={ "iris": "<=1.7.4" }
|
||||
fabric_recommend_list={}
|
||||
|
||||
# Fabric mod run
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# 1.21 version
|
||||
# 1.21.1 version
|
||||
java_version=21
|
||||
minecraft_version=1.21
|
||||
minecraft_version=1.21.1
|
||||
parchment_version=1.20.6:2024.05.01
|
||||
compatible_minecraft_versions=["1.21.0"]
|
||||
compatible_minecraft_versions=["1.21", "1.21.1"]
|
||||
accessWidenerVersion=1_20_6
|
||||
builds_for=fabric,neoforge
|
||||
# forge is broken due to gradle/build script issues
|
||||
@@ -21,7 +21,7 @@ fabric_api_version=0.100.1+1.21
|
||||
immersive_portals_version=
|
||||
canvas_version=
|
||||
|
||||
fabric_incompatibility_list={ "iris": "<=1.6.20" }
|
||||
fabric_incompatibility_list={ "iris": "<=1.7.4" }
|
||||
fabric_recommend_list={}
|
||||
|
||||
# Fabric mod run
|
||||
@@ -38,8 +38,8 @@ fabric_api_version=0.100.1+1.21
|
||||
enable_canvas=0
|
||||
|
||||
# (Neo)Forge loader
|
||||
forge_version=50.0.19
|
||||
neoforge_version=21.0.4-beta
|
||||
forge_version=
|
||||
neoforge_version=21.1.6
|
||||
# (Neo)Forge mod versions
|
||||
starlight_version_forge=
|
||||
terraforged_version=
|
||||
Reference in New Issue
Block a user