Architectury (pls don't fail me IntelliJ)

This commit is contained in:
Ran
2021-11-24 20:22:28 +06:00
parent faf38ef8e3
commit 514e12580d
167 changed files with 20124 additions and 363 deletions
+127
View File
@@ -0,0 +1,127 @@
plugins {
id "com.github.johnrengelman.shadow" version "7.0.0"
}
loom {
accessWidenerPath.set(project(":common").file("src/main/resources/lod.accesswidener"))
}
architectury {
platformSetupLoomIde()
fabric()
}
configurations {
common
shadowMe // Don't use shadow from the shadow plugin because we don't want IDEA to index this.
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
developmentFabric.extendsFrom common
implementation.extendsFrom shadowMe
}
repositories {
// Required for ModMenu and ClothAPI
maven { url "https://maven.shedaniel.me/" }
maven { url "https://maven.terraformersmc.com/" }
}
dependencies {
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
// TODO: This is only for LodMain, try to find a way to remove it
// Fabric API
modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
// Mod Menu
modImplementation("com.terraformersmc:modmenu:2.0.14") {
exclude(group: "net.fabricmc.fabric-api")
}
// Cloth Config (including Auto Config)
modApi("me.shedaniel.cloth:cloth-config-fabric:5.0.38") {
exclude(group: "net.fabricmc.fabric-api")
}
common(project(path: ":common", configuration: "namedElements")) { transitive false }
shadowMe(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
common 'org.tukaani:xz:1.9'
common 'org.apache.commons:commons-compress:1.21'
shadowMe 'org.tukaani:xz:1.9'
shadowMe 'org.apache.commons:commons-compress:1.21'
}
// This method copies the access wideners from the common project to the fabric project. And it was generated by Github Copilot
task copyAccessWidener(type: Copy) {
from project(":common").file("src/main/resources/lod.accesswidener")
into file("src/generated/resources")
}
task copyAccessWidenerAtRuntime(type: Copy) {
from project(":common").file("src/main/resources/lod.accesswidener")
into file("build/resources/main")
}
task deleteAccessWidenerAtRuntime(type: Delete) {
file("build/resources/main/lod.accesswidener")
}
runClient {
dependsOn(copyAccessWidenerAtRuntime)
finalizedBy(deleteAccessWidenerAtRuntime)
}
processResources {
dependsOn(copyAccessWidener)
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
shadowJar {
configurations = [project.configurations.shadowMe]
relocate 'org.tukaani', 'shaded.tukaani'
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
classifier "dev-shadow"
}
remapJar {
input.set shadowJar.archiveFile
dependsOn shadowJar
classifier null
}
jar {
classifier "dev"
}
sourcesJar {
def commonSources = project(":common").sourcesJar
dependsOn commonSources
from commonSources.archiveFile.map { zipTree(it) }
}
components.java {
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
skip()
}
}
publishing {
publications {
mavenFabric(MavenPublication) {
artifactId = rootProject.archives_base_name + "-" + project.name
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
}
}
@@ -0,0 +1,156 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric;
import com.seibel.lod.core.api.EventApi;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.fabric.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.fabric.wrappers.world.DimensionTypeWrapper;
import com.seibel.lod.fabric.wrappers.world.WorldWrapper;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.minecraft.client.KeyMapping;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.BlockEventData;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.chunk.LevelChunk;
import org.lwjgl.glfw.GLFW;
/**
* This handles all events sent to the client,
* and is the starting point for most of the mod.
*
* @author coolGi2007
* @author Ran
* @version 11-23-2021
*/
public class ClientProxy
{
private final EventApi eventApi = EventApi.INSTANCE;
/**
* Registers Fabric Events
* @author Ran
*/
public void registerEvents() {
// TODO: Fix this if it's wrong
/* World Events */
ServerTickEvents.START_SERVER_TICK.register(this::serverTickEvent);
ServerTickEvents.END_SERVER_TICK.register(this::serverTickEvent);
/* World Events */
ServerChunkEvents.CHUNK_LOAD.register(this::chunkLoadEvent);
ClientChunkEvents.CHUNK_LOAD.register(this::chunkLoadEvent);
/* World Events */
ServerWorldEvents.LOAD.register((server, level) -> this.worldLoadEvent(level));
ServerWorldEvents.UNLOAD.register((server, level) -> this.worldUnloadEvent());
/* The Client World Events are in the mixins
Client world load event is in MixinClientLevel
Client world unload event is in MixinMinecraft */
/* The save events are in MixinServerLevel */
/* Keyboard Events */
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.player != null) onKeyInput();
});
}
public void serverTickEvent(MinecraftServer server)
{
eventApi.serverTickEvent();
}
public void chunkLoadEvent(LevelAccessor level, LevelChunk chunk)
{
eventApi.chunkLoadEvent(new ChunkWrapper(chunk), DimensionTypeWrapper.getDimensionTypeWrapper(level.dimensionType()));
}
public void worldSaveEvent()
{
eventApi.worldSaveEvent();
}
/** This is also called when a new dimension loads */
public void worldLoadEvent(Level level)
{
eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(level));
}
public void worldUnloadEvent()
{
eventApi.worldUnloadEvent();
}
/*
public void blockChangeEvent(BlockEventData event)
{
// we only care about certain block events
if (event.getClass() == BlockEventData.BreakEvent.class ||
event.getClass() == BlockEventData.EntityPlaceEvent.class ||
event.getClass() == BlockEventData.EntityMultiPlaceEvent.class ||
event.getClass() == BlockEventData.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEventData.PortalSpawnEvent.class)
{
IChunkWrapper chunk = new ChunkWrapper(event.getWorld().getChunk(event.getPos()));
DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType());
// recreate the LOD where the blocks were changed
eventApi.blockChangeEvent(chunk, dimType);
}
}
*/
// The debug mode keybinding, which will be registered
public static final KeyMapping DebugToggle = KeyBindingHelper.registerKeyBinding(
new KeyMapping("key.lod.DebugToggle", GLFW.GLFW_KEY_F4, "key.lod.category"));
// The draw toggle keybinding, which will be registered
public static final KeyMapping DrawToggle = KeyBindingHelper.registerKeyBinding(
new KeyMapping("key.lod.DrawToggle", GLFW.GLFW_KEY_F6, "key.lod.category"));
boolean PreDebugToggle = false;
boolean PreDrawToggle = false;
public void onKeyInput() {
if (Config.Client.AdvancedModOptions.Debugging.enableDebugKeybindings)
{
// Only activates when you press the key
if (DebugToggle.isDown() && DebugToggle.isDown() != PreDebugToggle)
Config.Client.AdvancedModOptions.Debugging.debugMode = Config.Client.AdvancedModOptions.Debugging.debugMode.getNext();
if (DrawToggle.isDown() && DrawToggle.isDown() != PreDebugToggle)
Config.Client.AdvancedModOptions.Debugging.drawLods = !Config.Client.AdvancedModOptions.Debugging.drawLods;
}
PreDebugToggle = DebugToggle.isDown();
PreDrawToggle = DrawToggle.isDown();
}
}
@@ -0,0 +1,245 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.enums.config.*;
import com.seibel.lod.core.enums.rendering.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IWorldGenerator;
import me.shedaniel.autoconfig.ConfigData;
import me.shedaniel.autoconfig.annotation.ConfigEntry;
/**
* This handles any configuration the user has access to.
* @author coolGi2007
* @version 11-23-2021
*/
@me.shedaniel.autoconfig.annotation.Config(name = ModInfo.ID)
public class Config implements ConfigData
{
// CONFIG STRUCTURE
// -> Client
// |
// |-> Graphics
// | |-> QualityOption
// | |-> FogQualityOption
// | |-> AdvancedGraphicsOption
// |
// |-> World Generation
// |
// |-> Advanced Mod Option
// |-> Threads
// |-> Buffers
// |-> Debugging
// Since the original config system uses forge stuff, that means we have to rewrite the whole config system
// TODO: Stop using autoconfig and use manual config for cloth config
@ConfigEntry.Gui.Excluded
//@ConfigEntry.Category("lod.debug")
public int ConfigVersion = 1;
@ConfigEntry.Gui.CollapsibleObject
public Client client = new Client();
public static class Client
{
@ConfigEntry.Gui.CollapsibleObject
public Graphics graphics = new Graphics();
@ConfigEntry.Gui.CollapsibleObject
public WorldGenerator worldGenerator = new WorldGenerator();
@ConfigEntry.Gui.CollapsibleObject
public AdvancedModOptions advancedModOptions = new AdvancedModOptions();
public static class Graphics
{
@ConfigEntry.Gui.CollapsibleObject
public QualityOption qualityOption = new QualityOption();
@ConfigEntry.Gui.CollapsibleObject
public FogQualityOption fogQualityOption = new FogQualityOption();
@ConfigEntry.Gui.CollapsibleObject
public AdvancedGraphicsOption advancedGraphicsOption = new AdvancedGraphicsOption();
public static class QualityOption
{
@ConfigEntry.Category("lod.Graphics.QualityOption")
@ConfigEntry.Gui.Tooltip
public static HorizontalResolution drawResolution = IQuality.DRAW_RESOLUTION_DEFAULT;
@ConfigEntry.Category("lod.Graphics.QualityOption")
@ConfigEntry.Gui.Tooltip
@ConfigEntry.BoundedDiscrete(min = 16, max = 1024)
public static int lodChunkRenderDistance = IQuality.LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX.defaultValue;
@ConfigEntry.Category("lod.Graphics.QualityOption")
@ConfigEntry.Gui.Tooltip
public static VerticalQuality verticalQuality = IQuality.VERTICAL_QUALITY_DEFAULT;
@ConfigEntry.Category("lod.Graphics.QualityOption")
@ConfigEntry.Gui.Tooltip
public static HorizontalScale horizontalScale = IQuality.HORIZONTAL_SCALE_DEFAULT;
@ConfigEntry.Category("lod.Graphics.QualityOption")
@ConfigEntry.Gui.Tooltip
public static HorizontalQuality horizontalQuality = IQuality.HORIZONTAL_QUALITY_DEFAULT;
}
public static class FogQualityOption
{
@ConfigEntry.Category("lod.Graphics.FogQualityOption")
@ConfigEntry.Gui.Tooltip
public static FogDistance fogDistance = IFogQuality.FOG_DISTANCE_DEFAULT;
@ConfigEntry.Category("lod.Graphics.FogQualityOption")
@ConfigEntry.Gui.Tooltip
public static FogDrawOverride fogDrawOverride = IFogQuality.FOG_DRAW_OVERRIDE_DEFAULT;
@ConfigEntry.Category("lod.Graphics.FogQualityOption")
@ConfigEntry.Gui.Tooltip
public static boolean disableVanillaFog = IFogQuality.DISABLE_VANILLA_FOG_DEFAULT;
}
public static class AdvancedGraphicsOption
{
@ConfigEntry.Category("lod.Graphics.AdvancedGraphicsOption")
@ConfigEntry.Gui.Tooltip
public static LodTemplate lodTemplate = IAdvancedGraphics.LOD_TEMPLATE_DEFAULT;
@ConfigEntry.Category("lod.Graphics.AdvancedGraphicsOption")
@ConfigEntry.Gui.Tooltip
public static boolean disableDirectionalCulling = IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DEFAULT;
@ConfigEntry.Category("lod.Graphics.AdvancedGraphicsOption")
@ConfigEntry.Gui.Tooltip
public static boolean alwaysDrawAtMaxQuality = IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT;
@ConfigEntry.Category("lod.Graphics.AdvancedGraphicsOption")
@ConfigEntry.Gui.Tooltip
public static VanillaOverdraw vanillaOverdraw = IAdvancedGraphics.VANILLA_OVERDRAW_DEFAULT;
@ConfigEntry.Category("lod.Graphics.AdvancedGraphicsOption")
@ConfigEntry.Gui.Tooltip
public static GpuUploadMethod gpuUploadMethod = IAdvancedGraphics.GPU_UPLOAD_METHOD_DEFAULT;
@ConfigEntry.Category("lod.Graphics.AdvancedGraphicsOption")
@ConfigEntry.Gui.Tooltip
public static boolean useExtendedNearClipPlane = IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT;
}
}
//========================//
// WorldGenerator Configs //
//========================//
public static class WorldGenerator
{
@ConfigEntry.Category("lod.WorldGenerator")
@ConfigEntry.Gui.Tooltip
public static GenerationPriority generationPriority = IWorldGenerator.GENERATION_PRIORITY_DEFAULT;
@ConfigEntry.Category("lod.WorldGenerator")
@ConfigEntry.Gui.Tooltip
public static DistanceGenerationMode distanceGenerationMode = IWorldGenerator.DISTANCE_GENERATION_MODE_DEFAULT;
@ConfigEntry.Category("lod.WorldGenerator")
@ConfigEntry.Gui.Tooltip
public static boolean allowUnstableFeatureGeneration = IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT;
@ConfigEntry.Category("lod.WorldGenerator")
@ConfigEntry.Gui.Tooltip
public static BlocksToAvoid blocksToAvoid = IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT;
/*
@ConfigEntry.Category("lod.WorldGenerator")
@ConfigEntry.Gui.Tooltip
public static boolean useExperimentalPreGenLoading = false;
*/
}
//============================//
// AdvancedModOptions Configs //
//============================//
public static class AdvancedModOptions
{
@ConfigEntry.Gui.CollapsibleObject
public Threading threading = new Threading();
@ConfigEntry.Gui.CollapsibleObject
public Debugging debugging = new Debugging();
@ConfigEntry.Gui.CollapsibleObject
public Buffers buffers = new Buffers();
public static class Threading
{
@ConfigEntry.Category("lod.AdvancedModOptions.Threading")
@ConfigEntry.Gui.Tooltip
// Find a way to set the max to a variable
@ConfigEntry.BoundedDiscrete(min = 1, max = 50)
public static int numberOfWorldGenerationThreads = IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT.defaultValue;
@ConfigEntry.Category("lod.AdvancedModOptions.Threading")
@ConfigEntry.Gui.Tooltip
// Find a way to set the max to a variable
@ConfigEntry.BoundedDiscrete(min = 1, max = 50)
public static int numberOfBufferBuilderThreads = IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX.defaultValue;
}
//===============//
// Debug Options //
//===============//
public static class Debugging
{
@ConfigEntry.Category("lod.AdvancedModOptions.Debugging")
@ConfigEntry.Gui.Tooltip
public static boolean drawLods = IDebugging.DRAW_LODS_DEFAULT;
@ConfigEntry.Category("lod.AdvancedModOptions.Debugging")
@ConfigEntry.Gui.Tooltip
public static DebugMode debugMode = IDebugging.DEBUG_MODE_DEFAULT;
@ConfigEntry.Category("lod.AdvancedModOptions.Debugging")
@ConfigEntry.Gui.Tooltip
public static boolean enableDebugKeybindings = IDebugging.DEBUG_KEYBINDINGS_ENABLED_DEFAULT;
}
public static class Buffers
{
@ConfigEntry.Category("lod.AdvancedModOptions.Buffers")
@ConfigEntry.Gui.Tooltip
public static BufferRebuildTimes rebuildTimes = IBuffers.REBUILD_TIMES_DEFAULT;
}
}
}
}
@@ -0,0 +1,80 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.fabric.wrappers.DependencySetup;
import me.shedaniel.autoconfig.AutoConfig;
import me.shedaniel.autoconfig.serializer.Toml4jConfigSerializer;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraftforge.common.WorldWorkerManager;
/**
* Initialize and setup the Mod. <br>
* If you are looking for the real start of the mod
* check out the ClientProxy.
*
* @author coolGi2007
* @author Ran
* @version 11-21-2021
*/
public class Main implements ClientModInitializer
{
// This is a client mod so it should implement ClientModInitializer and in fabric.mod.json it should have "environment": "client"
// Once it works on servers change the implement to ModInitializer and in fabric.mod.json it should be "environment": "*"
public static Main instance;
public static ClientProxy client_proxy;
public static final Config CONFIG = AutoConfig.register(Config.class, Toml4jConfigSerializer::new).getConfig();
// Do if implements ClientModInitializer
@Override
public void onInitializeClient() {
}
public static void init() {
if (instance != null) return;
instance = new Main();
DependencySetup.createInitialBindings();
ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
initializeForge();
// Check if this works
client_proxy = new ClientProxy();
client_proxy.registerEvents();
}
/**
* This method makes forge classes work instead of just sitting there
* @author Ran
*/
public static void initializeForge() {
ServerTickEvents.START_SERVER_TICK.register((server) -> WorldWorkerManager.tick(true));
ServerTickEvents.END_SERVER_TICK.register((server) -> WorldWorkerManager.tick(false));
}
}
@@ -0,0 +1,17 @@
package com.seibel.lod.fabric.mixins;
import com.seibel.lod.fabric.Main;
import net.minecraft.client.Minecraft;
import net.minecraft.client.main.GameConfig;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = Minecraft.class)
public class MixinMinecraft {
@Inject(method = "<init>", at = @At("TAIL"))
private void startMod(GameConfig gameConfig, CallbackInfo ci) {
Main.init();
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.mixins;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.LevelRenderer;
import org.lwjgl.opengl.GL15;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.fabric.wrappers.McObjectConverter;
import net.minecraft.client.renderer.RenderType;
/**
* This class is used to mix in my rendering code
* before Minecraft starts rendering blocks.
* If this wasn't done, and we used Forge's
* render last event, the LODs would render on top
* of the normal terrain.
*
* @author coolGi2007
* @author James Seibel
* @version 11-21-2021
*/
@Mixin(LevelRenderer.class)
public class MixinWorldRenderer
{
private static float previousPartialTicks = 0;
@Inject(at = @At("RETURN"), method = "renderClouds")
private void renderClouds(PoseStack poseStack, Matrix4f matrix4f, float f, double d, double e, double g, CallbackInfo ci)
// private void renderClouds(PoseStack matrixStackIn, float partialTicks, CallbackInfo callback)
{
// get the partial ticks since renderBlockLayer doesn't
// have access to them
previousPartialTicks = f;
}
@Inject(at = @At("HEAD"), method = "renderChunkLayer")
private void renderChunkLayer(RenderType renderType, PoseStack matrixStackIn, double d, double e, double f, Matrix4f matrix4f, CallbackInfo ci)
{
// only render if LODs are enabled and
// only render before solid blocks
if (renderType.equals(RenderType.solid()))
{
// get MC's current projection matrix
float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
mcProjectionMatrix.transpose();
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks);
}
}
}
@@ -0,0 +1,28 @@
package com.seibel.lod.fabric.mixins.events;
import com.seibel.lod.fabric.Main;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.Supplier;
/**
* This class is used for world loading events
* @author Ran
*/
@Mixin(ClientLevel.class)
public class MixinClientLevel {
@Inject(method = "<init>", at = @At("TAIL"))
private void loadWorldEvent(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey<Level> resourceKey, DimensionType dimensionType, int i, Supplier<ProfilerFiller> supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
Main.client_proxy.worldLoadEvent((ClientLevel) (Object) this);
}
}
@@ -0,0 +1,31 @@
package com.seibel.lod.fabric.mixins.events;
import com.seibel.lod.fabric.Main;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.multiplayer.ClientLevel;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* This class is used for world unloading events
* @author Ran
*/
@Mixin(Minecraft.class)
public class MixinMinecraft {
@Shadow @Nullable public ClientLevel level;
@Inject(method = "setLevel", at = @At("HEAD"))
private void unloadWorldEvent_sL(ClientLevel clientLevel, CallbackInfo ci) {
if (level != null) Main.client_proxy.worldUnloadEvent();
}
@Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;updateScreenAndTick(Lnet/minecraft/client/gui/screens/Screen;)V", shift = At.Shift.AFTER))
private void unloadWorldEvent_cL(Screen screen, CallbackInfo ci) {
if (this.level != null) Main.client_proxy.worldUnloadEvent();
}
}
@@ -0,0 +1,26 @@
package com.seibel.lod.fabric.mixins.events;
import com.seibel.lod.fabric.Main;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.ProgressListener;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* This class is used for world saving events
* @author Ran
*/
@Mixin(ServerLevel.class)
public class MixinServerLevel {
@Inject(method = "save", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;saveAll()V", shift = At.Shift.AFTER))
private void saveWorldEvent_sA(ProgressListener progressListener, boolean bl, boolean bl2, CallbackInfo ci) {
Main.client_proxy.worldSaveEvent();
}
@Inject(method = "save", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;autoSave()V", shift = At.Shift.AFTER))
private void saveWorldEvent_aS(ProgressListener progressListener, boolean bl, boolean bl2, CallbackInfo ci) {
Main.client_proxy.worldSaveEvent();
}
}
@@ -0,0 +1,37 @@
package com.seibel.lod.fabric.wrappers;
import com.seibel.lod.core.handlers.IReflectionHandler;
import com.seibel.lod.core.handlers.ReflectionHandler;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockColorSingletonWrapper;
import com.seibel.lod.fabric.wrappers.config.LodConfigWrapperSingleton;
import com.seibel.lod.fabric.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.lod.fabric.wrappers.minecraft.MinecraftWrapper;
/**
* Binds all necessary dependencies so we
* can access them in Core. <br>
* This needs to be called before any Core classes
* are loaded.
*
* @author James Seibel
* @version 11-20-2021
*/
public class DependencySetup
{
public static void createInitialBindings()
{
SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE);
SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions()));
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.wrappers;
import java.nio.FloatBuffer;
import com.mojang.math.Matrix4f;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.objects.math.Mat4f;
import net.minecraft.core.Direction;
/**
* This class converts to and from Minecraft objects (Ex: Matrix4f)
* and objects we created (Ex: Mat4f).
* Since we don't want to deal with a bunch of tiny changes
* every time Minecraft renames a variable in Matrix4f or something.
*
* @author James Seibel
* @version 11-11-2021
*/
public class McObjectConverter
{
public McObjectConverter()
{
}
/** 4x4 float matrix converter */
public static Mat4f Convert(Matrix4f mcMatrix)
{
FloatBuffer buffer = FloatBuffer.allocate(16);
mcMatrix.store(buffer);
Mat4f matrix = new Mat4f(buffer);
matrix.transpose();
return matrix;
}
public static Direction Convert(LodDirection lodDirection)
{
return Direction.byName(lodDirection.name());
}
}
@@ -0,0 +1,72 @@
package com.seibel.lod.fabric.wrappers;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockPosWrapper;
import com.seibel.lod.fabric.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.fabric.wrappers.worldGeneration.WorldGeneratorWrapper;
/**
* This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 11-20-2021
*/
public class WrapperFactory implements IWrapperFactory
{
public static final WrapperFactory INSTANCE = new WrapperFactory();
@Override
public AbstractBlockPosWrapper createBlockPos()
{
return new BlockPosWrapper();
}
@Override
public AbstractBlockPosWrapper createBlockPos(int x, int y, int z)
{
return new BlockPosWrapper(x,y,z);
}
@Override
public AbstractChunkPosWrapper createChunkPos()
{
return new ChunkPosWrapper();
}
@Override
public AbstractChunkPosWrapper createChunkPos(int x, int z)
{
return new ChunkPosWrapper(x, z);
}
@Override
public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos)
{
return new ChunkPosWrapper(newChunkPos);
}
@Override
public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos)
{
return new ChunkPosWrapper(blockPos);
}
@Override
public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper);
}
}
@@ -0,0 +1,18 @@
package com.seibel.lod.fabric.wrappers;
import net.minecraft.world.level.levelgen.Heightmap;
/**
* Stores any variables or code that
* may be shared between wrapper objects.
*
* @author James Seibel
* @version 11-20-2021
*/
public class WrapperUtil
{
/** If we ever need to use a heightmap for any reason, use this one. */
public static final Heightmap.Types DEFAULT_HEIGHTMAP = Heightmap.Types.WORLD_SURFACE_WG;
}
@@ -0,0 +1,25 @@
package com.seibel.lod.fabric.wrappers.block;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import net.minecraft.world.level.block.Blocks;
/**
* This class wraps the minecraft Block class
*
* @author ??
* @version 11-17-2021
*/
public class BlockColorSingletonWrapper implements IBlockColorSingletonWrapper
{
public static final BlockColorSingletonWrapper INSTANCE = new BlockColorSingletonWrapper();
/** return base color of water (grey value) */
@Override
public IBlockColorWrapper getWaterColor()
{
return BlockColorWrapper.getBlockColorWrapper(Blocks.WATER);
}
}
@@ -0,0 +1,304 @@
package com.seibel.lod.fabric.wrappers.block;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.fabric.wrappers.minecraft.MinecraftWrapper;
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.block.*;
import net.minecraft.world.level.block.state.BlockState;
/**
* This class wraps the minecraft Block color class
*
* @author ??
* @version 11-17-2021
*/
public class BlockColorWrapper implements IBlockColorWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
// public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static final BlockPos blockPos = new BlockPos(0, 0, 0);
public static final Random random = new Random(0);
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
private final Block block;
private int color;
private boolean isColored;
private boolean toTint;
private boolean foliageTint;
private boolean grassTint;
private boolean waterTint;
/**Constructor only require for the block instance we are wrapping**/
public BlockColorWrapper(Block block)
{
this.block = block;
this.color = 0;
this.isColored = true;
this.toTint = false;
this.foliageTint = false;
this.grassTint = false;
this.waterTint = false;
setupColorAndTint();
/*StringBuilder s = new StringBuilder();
s.append(block + "\n"
+ Integer.toHexString(
Minecraft.getInstance().getBlockColors().createDefault().getColor(
block.defaultBlockState(),
(World) MinecraftWrapper.INSTANCE.getWrappedServerLevel().getLevel(),
blockPosWrapper.getBlockPos())) + "\n"
);
for(Property x : Minecraft.getInstance().getBlockColors().getColoringProperties(block))
s.append(x.getName() + " " + x.getPossibleValues() + '\n');
System.out.println(s);*/
//System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint);
}
/**
* this return a wrapper of the block in input
* @param block object of the block to wrap
*/
public static IBlockColorWrapper getBlockColorWrapper(Block block)
{
//first we check if the block has already been wrapped
if (blockColorWrapperMap.containsKey(block) && blockColorWrapperMap.get(block) != null)
return blockColorWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockColorWrapper blockWrapper = new BlockColorWrapper(block);
blockColorWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint()
{
BlockState blockState = block.defaultBlockState();
BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
boolean isTinted = false;
int listSize = 0;
// first step is to check if this block has a tinted face
for (Direction direction : directions)
{
// quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
listSize = Math.max(listSize, quads.size());
for (BakedQuad bakedQuad : quads)
{
isTinted |= bakedQuad.isTinted();
}
}
//if it contains a tinted face then we store this block in the toTint set
if (isTinted)
this.toTint = true;
//now we get the first non empty face
for (Direction direction : directions)
{
// quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
if (!quads.isEmpty())
break;
}
//the quads list is not empty we extract the first one
if (!quads.isEmpty())
{
isColored = true;
texture = quads.get(0).getSprite();
}
else
{
isColored = true;
texture = mc.getModelManager().getBlockModelShaper().getParticleIcon(block.defaultBlockState());
}
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int tempColor;
int colorMultiplier;
// generate the block's color
// for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
int frameIndex = 0; // TODO
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getWidth(); u++)
{
for (int v = 0; v < texture.getHeight(); v++)
{
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v);
if (ColorUtil.getAlpha(TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v)) == 0)
continue;
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(tempColor) > (ColorUtil.getBlue(tempColor) + 30)) || !(ColorUtil.getGreen(tempColor) > (ColorUtil.getRed(tempColor) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(tempColor) * colorMultiplier;
red += ColorUtil.getBlue(tempColor) * colorMultiplier;
green += ColorUtil.getGreen(tempColor) * colorMultiplier;
blue += ColorUtil.getRed(tempColor) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = 0;
else
{
// determine the average color
alpha /= count;
red /= count;
green /= count;
blue /= count;
tempColor = ColorUtil.rgbToInt(alpha, red, green, blue);
}
// determine if this block should use the biome color tint
if ((grassInstance() || leavesInstance() || waterIstance()) && (float) numberOfGreyPixel / count > 0.75f)
this.toTint = true;
// we check which kind of tint we need to apply
this.grassTint = grassInstance() && toTint;
this.foliageTint = leavesInstance() && toTint;
this.waterTint = waterIstance() && toTint;
color = tempColor;
}
/** determine if the given block should use the biome's grass color */
private boolean grassInstance()
{
return block instanceof GrassBlock
|| block instanceof BushBlock
|| block instanceof GrowingPlantBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean leavesInstance()
{
return block instanceof LeavesBlock
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's foliage color */
private boolean waterIstance()
{
return block == Blocks.WATER;
}
//--------------//
//Colors getters//
//--------------//
@Override
public boolean hasColor()
{
return isColored;
}
@Override
public int getColor()
{
return color;
}
//------------//
//Tint getters//
//------------//
@Override
public boolean hasTint()
{
return toTint;
}
@Override
public boolean hasGrassTint()
{
return grassTint;
}
@Override
public boolean hasFolliageTint()
{
return foliageTint;
}
@Override
public boolean hasWaterTint()
{
return waterTint;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockColorWrapper))
return false;
BlockColorWrapper that = (BlockColorWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,73 @@
package com.seibel.lod.fabric.wrappers.block;
import java.util.Objects;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import net.minecraft.core.BlockPos;
public class BlockPosWrapper extends AbstractBlockPosWrapper
{
private final BlockPos.MutableBlockPos blockPos;
public BlockPosWrapper()
{
this.blockPos = new BlockPos.MutableBlockPos(0, 0, 0);
}
public BlockPosWrapper(int x, int y, int z)
{
this.blockPos = new BlockPos.MutableBlockPos(x, y, z);
}
public void set(int x, int y, int z)
{
blockPos.set(x, y, z);
}
public int getX()
{
return blockPos.getX();
}
public int getY()
{
return blockPos.getY();
}
public int getZ()
{
return blockPos.getZ();
}
public int get(LodDirection.Axis axis)
{
return axis.choose(getX(), getY(), getZ());
}
public BlockPos.MutableBlockPos getBlockPos()
{
return blockPos;
}
@Override
public boolean equals(Object o)
{
return blockPos.equals(o);
}
@Override
public int hashCode()
{
return Objects.hash(blockPos);
}
public BlockPosWrapper offset(int x, int y, int z)
{
blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z);
return this;
}
}
@@ -0,0 +1,162 @@
package com.seibel.lod.fabric.wrappers.block;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.fabric.wrappers.chunk.ChunkWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
/**
* This class wraps Minecraft's Block class
*
* @author ??
* @version 11-18-2021
*/
public class BlockShapeWrapper implements IBlockShapeWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockShapeWrapper> blockShapeWrapperMap = new ConcurrentHashMap<>();
public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper();
private final Block block;
private boolean toAvoid;
private boolean nonFull;
private boolean noCollision;
/**Constructor only require for the block instance we are wrapping**/
public BlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper)
{
this.block = block;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = ofBlockToAvoid();
setupShapes((ChunkWrapper) chunkWrapper, (BlockPosWrapper) blockPosWrapper);
//System.out.println(block + " non full " + nonFull + " no collision " + noCollision + " to avoid " + toAvoid);
}
private BlockShapeWrapper()
{
this.block = Blocks.WATER;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = false;
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
*/
static public BlockShapeWrapper getBlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper)
{
//first we check if the block has already been wrapped
if (blockShapeWrapperMap.containsKey(block) && blockShapeWrapperMap.get(block) != null)
return blockShapeWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockShapeWrapper blockWrapper = new BlockShapeWrapper(block, chunkWrapper, blockPosWrapper);
blockShapeWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
private void setupShapes(ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper)
{
ChunkAccess chunk = chunkWrapper.getChunk();
BlockPos blockPos = blockPosWrapper.getBlockPos();
boolean noCollisionSetted = false;
boolean nonFullSetted = false;
if (!block.defaultBlockState().getFluidState().isEmpty()) // || block instanceof SixWayBlock)
{
noCollisionSetted = true;
nonFullSetted = true;
noCollision = false;
nonFull = false;
}
if (!nonFullSetted)
{
VoxelShape voxelShape = block.defaultBlockState().getShape(chunk, blockPos);
if (!voxelShape.isEmpty())
{
AABB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
nonFull = xWidth < 1 && zWidth < 1 && yWidth < 1;
}
else
{
nonFull = false;
}
}
if (!noCollisionSetted)
{
VoxelShape collisionShape = block.defaultBlockState().getCollisionShape(chunk, blockPos);
noCollision = collisionShape.isEmpty();
}
}
@Override
public boolean ofBlockToAvoid()
{
return block.equals(Blocks.AIR)
|| block.equals(Blocks.CAVE_AIR)
|| block.equals(Blocks.BARRIER)
|| block.equals(Blocks.VOID_AIR);
}
//-----------------//
//Avoidance getters//
//-----------------//
@Override
public boolean isNonFull()
{
return nonFull;
}
@Override
public boolean hasNoCollision()
{
return noCollision;
}
@Override
public boolean isToAvoid()
{
return toAvoid;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockShapeWrapper))
return false;
BlockShapeWrapper that = (BlockShapeWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,28 @@
package com.seibel.lod.fabric.wrappers.block;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
/**
* For wrapping/utilizing around TextureAtlasSprite
* @author Ran
*/
public class TextureAtlasSpriteWrapper {
/**
* This code is from Minecraft Forge
* Which is licensed under the terms of GNU Lesser General Public License
* as published by the Free Software Foundation version 2.1
* of the License.
*
* The code has been modified to use TextureAtlasSprite
*/
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y) {
if (sprite.animatedTexture != null) {
x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width;
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
}
return sprite.mainImage[0].getPixelRGBA(x, y);
}
}
@@ -0,0 +1,118 @@
package com.seibel.lod.fabric.wrappers.chunk;
import java.util.Objects;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockPosWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
/**
* This class wraps minecraft's ChunkPos class
*
* @author James Seibel
* @version 11-18-2021
*/
public class ChunkPosWrapper extends AbstractChunkPosWrapper
{
private final ChunkPos chunkPos;
public ChunkPosWrapper(ChunkPos newChunkPos)
{
this.chunkPos = newChunkPos;
}
public ChunkPosWrapper(BlockPos blockPos)
{
this.chunkPos = new ChunkPos(blockPos);
}
public ChunkPosWrapper(AbstractChunkPosWrapper newChunkPos)
{
this.chunkPos = ((ChunkPosWrapper) newChunkPos).chunkPos;
}
public ChunkPosWrapper(AbstractBlockPosWrapper blockPos)
{
this.chunkPos = new ChunkPos(((BlockPosWrapper) blockPos).getBlockPos());
}
public ChunkPosWrapper(int chunkX, int chunkZ)
{
this.chunkPos = new ChunkPos(chunkX, chunkZ);
}
public ChunkPosWrapper()
{
this.chunkPos = new ChunkPos(0, 0);
}
@Override
public int getX()
{
return chunkPos.x;
}
@Override
public int getZ()
{
return chunkPos.z;
}
@Override
public int getMinBlockX()
{
return chunkPos.getMinBlockX();
}
@Override
public int getMinBlockZ()
{
return chunkPos.getMinBlockZ();
}
@Override
public int getRegionX()
{
return chunkPos.getRegionX();
}
@Override
public int getRegionZ()
{
return chunkPos.getRegionZ();
}
public ChunkPos getChunkPos()
{
return chunkPos;
}
@Override
public boolean equals(Object o)
{
return chunkPos.equals(o);
}
@Override
public int hashCode()
{
return Objects.hash(chunkPos);
}
@Override
public BlockPosWrapper getWorldPosition()
{
BlockPos blockPos = chunkPos.getWorldPosition();
return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
}
@@ -0,0 +1,106 @@
package com.seibel.lod.fabric.wrappers.chunk;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.fabric.wrappers.WrapperUtil;
import com.seibel.lod.fabric.wrappers.block.BlockColorWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockPosWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockShapeWrapper;
import com.seibel.lod.fabric.wrappers.world.BiomeWrapper;
import net.minecraft.world.level.block.LiquidBlockContainer;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.chunk.ChunkAccess;
/**
*
* @author ??
* @version 11-17-2021
*/
public class ChunkWrapper implements IChunkWrapper
{
private final ChunkAccess chunk;
private final ChunkPosWrapper chunkPos;
@Override
public int getHeight()
{
return chunk.getMaxBuildHeight();
}
@Override
public boolean isPositionInWater(AbstractBlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos());
//This type of block is always in water
return ((blockState.getBlock() instanceof LiquidBlockContainer) && !(blockState.getBlock() instanceof SimpleWaterloggedBlock))
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
@Override
public int getHeightMapValue(int xRel, int zRel)
{
return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
}
@Override
public BiomeWrapper getBiome(int xRel, int yAbs, int zRel)
{
return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2));
}
@Override
public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos)
{
return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock());
}
@Override
public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos)
{
return BlockShapeWrapper.getBlockShapeWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock(), this, blockPos);
}
public ChunkWrapper(ChunkAccess chunk)
{
this.chunk = chunk;
this.chunkPos = new ChunkPosWrapper(chunk.getPos());
}
public ChunkAccess getChunk()
{
return chunk;
}
@Override
public ChunkPosWrapper getPos()
{
return chunkPos;
}
@Override
public boolean isLightCorrect()
{
return chunk.isLightCorrect();
}
@Override
public boolean isWaterLogged(AbstractBlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(((BlockPosWrapper)blockPos).getBlockPos());
//This type of block is always in water
return ((blockState.getBlock() instanceof LiquidBlockContainer) && !(blockState.getBlock() instanceof SimpleWaterloggedBlock))
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
@Override
public int getEmittedBrightness(AbstractBlockPosWrapper blockPos)
{
return chunk.getLightEmission(((BlockPosWrapper)blockPos).getBlockPos());
}
}
@@ -0,0 +1,463 @@
package com.seibel.lod.fabric.wrappers.config;
import com.seibel.lod.core.enums.config.*;
import com.seibel.lod.core.enums.rendering.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.fabric.Config;
/**
* This holds the config defaults and setters/getters
* that should be hooked into the host mod loader (Fabric, Forge, etc.).
*
* @author James Seibel
* @version 11-16-2021
*/
public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
{
public static final LodConfigWrapperSingleton INSTANCE = new LodConfigWrapperSingleton();
private static final Client client = new Client();
@Override
public IClient client()
{
return client;
}
public static class Client implements IClient
{
public final IGraphics graphics;
public final IWorldGenerator worldGenerator;
public final IAdvanced advanced;
@Override
public IGraphics graphics()
{
return graphics;
}
@Override
public IWorldGenerator worldGenerator()
{
return worldGenerator;
}
@Override
public IAdvanced advanced()
{
return advanced;
}
//================//
// Client Configs //
//================//
public Client()
{
graphics = new Graphics();
worldGenerator = new WorldGenerator();
advanced = new Advanced();
}
//==================//
// Graphics Configs //
//==================//
public static class Graphics implements IGraphics
{
public final IQuality quality;
public final IFogQuality fogQuality;
public final IAdvancedGraphics advancedGraphics;
@Override
public IQuality quality()
{
return quality;
}
@Override
public IFogQuality fogQuality()
{
return fogQuality;
}
@Override
public IAdvancedGraphics advancedGraphics()
{
return advancedGraphics;
}
Graphics()
{
quality = new Quality();
advancedGraphics = new AdvancedGraphics();
fogQuality = new FogQuality();
}
public static class Quality implements IQuality
{
@Override
public HorizontalResolution getDrawResolution()
{
return Config.Client.Graphics.QualityOption.drawResolution;
}
@Override
public void setDrawResolution(HorizontalResolution newHorizontalResolution)
{
Config.Client.Graphics.QualityOption.drawResolution = newHorizontalResolution;
}
@Override
public int getLodChunkRenderDistance()
{
return Config.Client.Graphics.QualityOption.lodChunkRenderDistance;
}
@Override
public void setLodChunkRenderDistance(int newLodChunkRenderDistance)
{
Config.Client.Graphics.QualityOption.lodChunkRenderDistance = newLodChunkRenderDistance;
}
@Override
public VerticalQuality getVerticalQuality()
{
return Config.Client.Graphics.QualityOption.verticalQuality;
}
@Override
public void setVerticalQuality(VerticalQuality newVerticalQuality)
{
Config.Client.Graphics.QualityOption.verticalQuality = newVerticalQuality;
}
@Override
public HorizontalScale getHorizontalScale()
{
return Config.Client.Graphics.QualityOption.horizontalScale;
}
@Override
public void setHorizontalScale(HorizontalScale newHorizontalScale)
{
Config.Client.Graphics.QualityOption.horizontalScale = newHorizontalScale;
}
@Override
public HorizontalQuality getHorizontalQuality()
{
return Config.Client.Graphics.QualityOption.horizontalQuality;
}
@Override
public void setHorizontalQuality(HorizontalQuality newHorizontalQuality)
{
Config.Client.Graphics.QualityOption.horizontalQuality = newHorizontalQuality;
}
}
public static class FogQuality implements IFogQuality
{
@Override
public FogDistance getFogDistance()
{
return Config.Client.Graphics.FogQualityOption.fogDistance;
}
@Override
public void setFogDistance(FogDistance newFogDistance)
{
Config.Client.Graphics.FogQualityOption.fogDistance = newFogDistance;
}
@Override
public FogDrawOverride getFogDrawOverride()
{
return Config.Client.Graphics.FogQualityOption.fogDrawOverride;
}
@Override
public void setFogDrawOverride(FogDrawOverride newFogDrawOverride)
{
Config.Client.Graphics.FogQualityOption.fogDrawOverride = newFogDrawOverride;
}
@Override
public boolean getDisableVanillaFog()
{
return Config.Client.Graphics.FogQualityOption.disableVanillaFog;
}
@Override
public void setDisableVanillaFog(boolean newDisableVanillaFog)
{
Config.Client.Graphics.FogQualityOption.disableVanillaFog = newDisableVanillaFog;
}
}
public static class AdvancedGraphics implements IAdvancedGraphics
{
@Override
public LodTemplate getLodTemplate()
{
return Config.Client.Graphics.AdvancedGraphicsOption.lodTemplate;
}
@Override
public void setLodTemplate(LodTemplate newLodTemplate)
{
Config.Client.Graphics.AdvancedGraphicsOption.lodTemplate = newLodTemplate;
}
@Override
public boolean getDisableDirectionalCulling()
{
return Config.Client.Graphics.AdvancedGraphicsOption.disableDirectionalCulling;
}
@Override
public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling)
{
Config.Client.Graphics.AdvancedGraphicsOption.disableDirectionalCulling = newDisableDirectionalCulling;
}
@Override
public boolean getAlwaysDrawAtMaxQuality()
{
return Config.Client.Graphics.AdvancedGraphicsOption.alwaysDrawAtMaxQuality;
}
@Override
public void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality)
{
Config.Client.Graphics.AdvancedGraphicsOption.alwaysDrawAtMaxQuality = newAlwaysDrawAtMaxQuality;
}
@Override
public VanillaOverdraw getVanillaOverdraw()
{
return Config.Client.Graphics.AdvancedGraphicsOption.vanillaOverdraw;
}
@Override
public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw)
{
Config.Client.Graphics.AdvancedGraphicsOption.vanillaOverdraw = newVanillaOverdraw;
}
@Override
public GpuUploadMethod getGpuUploadMethod()
{
return Config.Client.Graphics.AdvancedGraphicsOption.gpuUploadMethod;
}
@Override
public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog)
{
Config.Client.Graphics.AdvancedGraphicsOption.gpuUploadMethod = newDisableVanillaFog;
}
@Override
public boolean getUseExtendedNearClipPlane()
{
return Config.Client.Graphics.AdvancedGraphicsOption.useExtendedNearClipPlane;
}
@Override
public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane)
{
Config.Client.Graphics.AdvancedGraphicsOption.useExtendedNearClipPlane = newUseExtendedNearClipPlane;
}
}
}
//========================//
// WorldGenerator Configs //
//========================//
public static class WorldGenerator implements IWorldGenerator
{
@Override
public GenerationPriority getGenerationPriority()
{
return Config.Client.WorldGenerator.generationPriority;
}
@Override
public void setGenerationPriority(GenerationPriority newGenerationPriority)
{
Config.Client.WorldGenerator.generationPriority = newGenerationPriority;
}
@Override
public DistanceGenerationMode getDistanceGenerationMode()
{
return Config.Client.WorldGenerator.distanceGenerationMode;
}
@Override
public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode)
{
Config.Client.WorldGenerator.distanceGenerationMode = newDistanceGenerationMode;
}
@Override
public boolean getAllowUnstableFeatureGeneration()
{
return Config.Client.WorldGenerator.allowUnstableFeatureGeneration;
}
@Override
public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration)
{
Config.Client.WorldGenerator.allowUnstableFeatureGeneration = newAllowUnstableFeatureGeneration;
}
@Override
public BlocksToAvoid getBlocksToAvoid()
{
return Config.Client.WorldGenerator.blocksToAvoid;
}
@Override
public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid)
{
Config.Client.WorldGenerator.blocksToAvoid = newBlockToAvoid;
}
}
//============================//
// AdvancedModOptions Configs //
//============================//
public static class Advanced implements IAdvanced
{
public final IThreading threading;
public final IDebugging debugging;
public final IBuffers buffers;
@Override
public IThreading threading()
{
return threading;
}
@Override
public IDebugging debugging()
{
return debugging;
}
@Override
public IBuffers buffers()
{
return buffers;
}
public Advanced()
{
threading = new Threading();
debugging = new Debugging();
buffers = new Buffers();
}
public static class Threading implements IThreading
{
@Override
public int getNumberOfWorldGenerationThreads()
{
return Config.Client.AdvancedModOptions.Threading.numberOfWorldGenerationThreads;
}
@Override
public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads)
{
Config.Client.AdvancedModOptions.Threading.numberOfWorldGenerationThreads = newNumberOfWorldGenerationThreads;
}
@Override
public int getNumberOfBufferBuilderThreads()
{
return Config.Client.AdvancedModOptions.Threading.numberOfBufferBuilderThreads;
}
@Override
public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads)
{
Config.Client.AdvancedModOptions.Threading.numberOfBufferBuilderThreads = newNumberOfWorldBuilderThreads;
}
}
//===============//
// Debug Options //
//===============//
public static class Debugging implements IDebugging
{
@Override
public boolean getDrawLods()
{
return Config.Client.AdvancedModOptions.Debugging.drawLods;
}
@Override
public void setDrawLods(boolean newDrawLods)
{
Config.Client.AdvancedModOptions.Debugging.drawLods = newDrawLods;
}
@Override
public DebugMode getDebugMode()
{
return Config.Client.AdvancedModOptions.Debugging.debugMode;
}
@Override
public void setDebugMode(DebugMode newDebugMode)
{
Config.Client.AdvancedModOptions.Debugging.debugMode = newDebugMode;
}
@Override
public boolean getDebugKeybindingsEnabled()
{
return Config.Client.AdvancedModOptions.Debugging.enableDebugKeybindings;
}
@Override
public void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings)
{
Config.Client.AdvancedModOptions.Debugging.enableDebugKeybindings = newEnableDebugKeybindings;
}
}
public static class Buffers implements IBuffers
{
@Override
public BufferRebuildTimes getRebuildTimes()
{
return Config.Client.AdvancedModOptions.Buffers.rebuildTimes;
}
@Override
public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes)
{
Config.Client.AdvancedModOptions.Buffers.rebuildTimes = newBufferRebuildTimes;
}
}
}
}
}
@@ -0,0 +1,19 @@
package com.seibel.lod.fabric.wrappers.config;
import com.seibel.lod.fabric.Config;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import me.shedaniel.autoconfig.AutoConfig;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/**
* For making the config show up in modmenu
*/
@Environment(EnvType.CLIENT)
public class ModMenuIntegration implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return parent -> AutoConfig.getConfigScreen(Config.class, parent).get();
}
}
@@ -0,0 +1,141 @@
package com.seibel.lod.fabric.wrappers.minecraft;
import java.util.HashSet;
import com.mojang.math.Vector3d;
import com.mojang.math.Vector3f;
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.objects.math.Vec3d;
import com.seibel.lod.core.objects.math.Vec3f;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.fabric.wrappers.McObjectConverter;
import com.seibel.lod.fabric.wrappers.block.BlockPosWrapper;
import com.seibel.lod.fabric.wrappers.chunk.ChunkPosWrapper;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk;
import net.minecraft.core.BlockPos;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.phys.Vec3;
/**
* A singleton that contains everything
* related to rendering in Minecraft.
*
* @author James Seibel
* @version 11-18-2021
*/
public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
private final GameRenderer gameRenderer = Minecraft.getInstance().gameRenderer;
private final static Minecraft mc = Minecraft.getInstance();
@Override
public Vec3f getLookAtVector()
{
Camera camera = gameRenderer.getMainCamera();
Vector3f cameraDir = camera.getLookVector();
return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z());
}
@Override
public AbstractBlockPosWrapper getCameraBlockPosition()
{
Camera camera = gameRenderer.getMainCamera();
BlockPos blockPos = camera.getBlockPosition();
return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
@Override
public boolean playerHasBlindnessEffect()
{
return mc.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null;
}
@Override
public Vec3d getCameraExactPosition()
{
Camera camera = gameRenderer.getMainCamera();
Vec3 projectedView = camera.getPosition();
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
}
@Override
public Mat4f getDefaultProjectionMatrix(float partialTicks)
{
// return McObjectConverter.Convert(gameRenderer.getProjectionMatrix(gameRenderer.getMainCamera(), partialTicks, true));
return McObjectConverter.Convert(gameRenderer.getProjectionMatrix(partialTicks));
}
@Override
public double getGamma()
{
return mc.options.gamma;
}
@Override
public double getFov(float partialTicks)
{
return gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true);
}
/** Measured in chunks */
@Override
public int getRenderDistance()
{
return mc.options.renderDistance;
}
@Override
public int getScreenWidth()
{
return mc.getWindow().getWidth();
}
@Override
public int getScreenHeight()
{
return mc.getWindow().getHeight();
}
/**
* This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br>
* <p>
* Note: This isn't perfect. It will return some chunks that are outside
* the clipping plane. (For example, if you are high above the ground some chunks
* will be incorrectly added, even though they are outside render range).
*/
@Override
public HashSet<AbstractChunkPosWrapper> getRenderedChunks()
{
HashSet<AbstractChunkPosWrapper> loadedPos = new HashSet<>();
// Wow, those are some long names!
// go through every RenderInfo to get the compiled chunks
LevelRenderer renderer = mc.levelRenderer;
for (LevelRenderer.RenderChunkInfo worldRenderer$LocalRenderInformationContainer : renderer.renderChunks)
{
CompiledChunk compiledChunk = worldRenderer$LocalRenderInformationContainer.chunk.getCompiledChunk();
if (!compiledChunk.hasNoRenderableLayers())
{
// add the ChunkPos for every rendered chunk
BlockPos bpos = worldRenderer$LocalRenderInformationContainer.chunk.getOrigin();
loadedPos.add(new ChunkPosWrapper(bpos));
}
}
return loadedPos;
}
}
@@ -0,0 +1,394 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.wrappers.minecraft;
import java.awt.Color;
import java.io.File;
import java.util.ArrayList;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.platform.Window;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.fabric.wrappers.McObjectConverter;
import com.seibel.lod.fabric.wrappers.block.BlockPosWrapper;
import com.seibel.lod.fabric.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.fabric.wrappers.misc.LightMapWrapper;
import com.seibel.lod.fabric.wrappers.world.DimensionTypeWrapper;
import com.seibel.lod.fabric.wrappers.world.WorldWrapper;
import net.minecraft.CrashReport;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.server.IntegratedServer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.dimension.DimensionType;
/**
* A singleton that wraps the Minecraft class
* to allow for easier movement between Minecraft versions.
*
* @author James Seibel
* @version 9-16-2021
*/
public class MinecraftWrapper implements IMinecraftWrapper
{
public static final MinecraftWrapper INSTANCE = new MinecraftWrapper();
private final Minecraft mc = Minecraft.getInstance();
/**
* The lightmap for the current:
* Time, dimension, brightness setting, etc.
*/
private NativeImage lightMap = null;
private ProfilerWrapper profilerWrapper;
private MinecraftWrapper()
{
}
//================//
// helper methods //
//================//
/**
* This should be called at the beginning of every frame to
* clear any Minecraft data that becomes out of date after a frame. <br> <br>
* <p>
* LightMaps and other time sensitive objects fall in this category. <br> <br>
* <p>
* This doesn't affect OpenGL objects in any way.
*/
@Override
public void clearFrameObjectCache()
{
lightMap = null;
}
//=================//
// method wrappers //
//=================//
@Override
public float getShade(LodDirection lodDirection)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return mc.level.getShade(mcDir, true);
}
@Override
public boolean hasSinglePlayerServer()
{
return mc.hasSingleplayerServer();
}
@Override
public String getCurrentServerName()
{
return mc.getCurrentServer().name;
}
@Override
public String getCurrentServerIp()
{
return mc.getCurrentServer().ip;
}
@Override
public String getCurrentServerVersion()
{
return mc.getCurrentServer().version.getString();
}
/** Returns the dimension the player is currently in */
@Override
public IDimensionTypeWrapper getCurrentDimension()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(mc.player.level.dimensionType());
}
@Override
public String getCurrentDimensionId()
{
return LodUtil.getDimensionIDFromWorld(WorldWrapper.getWorldWrapper(mc.level));
}
/** This texture changes every frame */
@Override
public ILightMapWrapper getCurrentLightMap()
{
// get the current lightMap if the cache is empty
if (lightMap == null)
{
LightTexture tex = mc.gameRenderer.lightTexture();
lightMap = tex.lightPixels;
}
return new LightMapWrapper(lightMap);
}
/**
* Returns the color int at the given pixel coordinates
* from the current lightmap.
* @param u x location in texture space
* @param v z location in texture space
*/
@Override
public int getColorIntFromLightMap(int u, int v)
{
if (lightMap == null)
{
// make sure the lightMap is up-to-date
getCurrentLightMap();
}
return lightMap.getPixelRGBA(u, v);
}
/**
* Returns the Color at the given pixel coordinates
* from the current lightmap.
* @param u x location in texture space
* @param v z location in texture space
*/
@Override
public Color getColorFromLightMap(int u, int v)
{
return LodUtil.intToColor(lightMap.getPixelRGBA(u, v));
}
//=============//
// Simple gets //
//=============//
public LocalPlayer getPlayer()
{
return mc.player;
}
@Override
public boolean playerExists()
{
return mc.player != null;
}
@Override
public BlockPosWrapper getPlayerBlockPos()
{
BlockPos playerPos = getPlayer().blockPosition();
return new BlockPosWrapper(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
@Override
public ChunkPosWrapper getPlayerChunkPos()
{
return new ChunkPosWrapper(getPlayer().chunkPosition().x, getPlayer().chunkPosition().z);
}
public Options getOptions()
{
return mc.options;
}
public ModelManager getModelManager()
{
return mc.getModelManager();
}
public ClientLevel getClientWorld()
{
return mc.level;
}
/**
* Attempts to get the ServerWorld for the dimension
* the user is currently in.
* @returns null if no ServerWorld is available
*/
@Override
public IWorldWrapper getWrappedServerWorld()
{
if (mc.level == null)
return null;
DimensionType dimension = mc.level.dimensionType();
IntegratedServer server = mc.getSingleplayerServer();
if (server == null)
return null;
ServerLevel serverWorld = null;
Iterable<ServerLevel> worlds = server.getAllLevels();
for (ServerLevel world : worlds)
{
if (world.dimensionType() == dimension)
{
serverWorld = world;
break;
}
}
return WorldWrapper.getWorldWrapper(serverWorld);
}
@Override
public IWorldWrapper getWrappedClientWorld()
{
return WorldWrapper.getWorldWrapper(mc.level);
}
@Override
public File getGameDirectory()
{
return mc.gameDirectory;
}
@Override
public IProfilerWrapper getProfiler()
{
if (profilerWrapper == null)
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
else if (mc.getProfiler() != profilerWrapper.profiler)
profilerWrapper.profiler = mc.getProfiler();
return profilerWrapper;
}
public ClientPacketListener getConnection()
{
return mc.getConnection();
}
public GameRenderer getGameRenderer()
{
return mc.gameRenderer;
}
public Entity getCameraEntity()
{
return mc.cameraEntity;
}
public Window getWindow()
{
return mc.getWindow();
}
@Override
public float getSkyDarken(float partialTicks)
{
return mc.level.getSkyDarken(partialTicks);
}
public IntegratedServer getSinglePlayerServer()
{
return mc.getSingleplayerServer();
}
@Override
public boolean connectedToServer()
{
return mc.getCurrentServer() != null;
}
public ServerData getCurrentServer()
{
return mc.getCurrentServer();
}
public LevelRenderer getLevelRenderer()
{
return mc.levelRenderer;
}
/** Returns all worlds available to the server */
@Override
public ArrayList<IWorldWrapper> getAllServerWorlds()
{
ArrayList<IWorldWrapper> worlds = new ArrayList<IWorldWrapper>();
Iterable<ServerLevel> serverWorlds = mc.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds)
{
worlds.add(WorldWrapper.getWorldWrapper(world));
}
return worlds;
}
@Override
public void sendChatMessage(String string)
{
getPlayer().sendMessage(new TextComponent(string), getPlayer().getUUID());
}
/**
* Crashes Minecraft, displaying the given errorMessage <br> <br>
* In the following format: <br>
*
* The game crashed whilst <strong>errorMessage</strong> <br>
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
* Exit Code: -1 <br>
*/
@Override
public void crashMinecraft(String errorMessage, Throwable exception)
{
ClientApi.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report);
}
}
@@ -0,0 +1,43 @@
package com.seibel.lod.fabric.wrappers.minecraft;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import net.minecraft.util.profiling.ProfilerFiller;
/**
*
*
* @author James Seibel
* @version 11-20-2021
*/
public class ProfilerWrapper implements IProfilerWrapper
{
public ProfilerFiller profiler;
public ProfilerWrapper(ProfilerFiller newProfiler)
{
profiler = newProfiler;
}
/** starts a new section inside the currently running section */
@Override
public void push(String newSection)
{
profiler.push(newSection);
}
/** ends the currently running section and starts a new one */
@Override
public void popPush(String newSection)
{
profiler.popPush(newSection);
}
/** ends the currently running section */
@Override
public void pop()
{
profiler.pop();
}
}
@@ -0,0 +1,31 @@
package com.seibel.lod.fabric.wrappers.misc;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
/**
*
* @author Leonardo Amato
* @version 11-13-2021
*/
public class LightMapWrapper implements ILightMapWrapper
{
private NativeImage lightMap = null;
public LightMapWrapper(NativeImage newlightMap)
{
lightMap = newlightMap;
}
public void setLightMap(NativeImage newlightMap)
{
lightMap = newlightMap;
}
@Override
public int getLightValue(int skyLight, int blockLight)
{
return lightMap.getPixelRGBA(skyLight, blockLight);
}
}
@@ -0,0 +1,61 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.wrappers.world;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeColorWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockPosWrapper;
import net.minecraft.client.renderer.BiomeColors;
/**
*
* @author Cola?
* @version 11-15-2021
*/
public class BiomeColorWrapperSingleton implements IBiomeColorWrapperSingleton
{
private static final BiomeColorWrapperSingleton instance = new BiomeColorWrapperSingleton();
@Override
public IBiomeColorWrapperSingleton getInstance()
{
return instance;
}
@Override
public int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageGrassColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageWaterColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageFoliageColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
}
@@ -0,0 +1,157 @@
package com.seibel.lod.fabric.wrappers.world;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockColorSingletonWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockColorWrapper;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Blocks;
/**
* This class wraps the minecraft BlockPos.Mutable (and BlockPos) class
*
* @author James Seibel
* @version 11-15-2021
*/
public class BiomeWrapper implements IBiomeWrapper
{
public static final ConcurrentMap<Biome, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
private final Biome biome;
public BiomeWrapper(Biome biome)
{
this.biome = biome;
}
static public BiomeWrapper getBiomeWrapper(Biome biome)
{
//first we check if the biome has already been wrapped
if(biomeWrapperMap.containsKey(biome) && biomeWrapperMap.get(biome) != null)
return biomeWrapperMap.get(biome);
//if it hasn't been created yet, we create it and save it in the map
BiomeWrapper biomeWrapper = new BiomeWrapper(biome);
biomeWrapperMap.put(biome, biomeWrapper);
//we return the newly created wrapper
return biomeWrapper;
}
/** Returns a color int for the given biome. */
@Override
public int getColorForBiome(int x, int z)
{
int colorInt;
int tintValue = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.NETHERRACK).getColor();
break;
case THEEND:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.END_STONE).getColor();
break;
case BEACH:
case DESERT:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SAND).getColor();
break;
case EXTREME_HILLS:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.STONE).getColor();
break;
case MUSHROOM:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.MYCELIUM).getColor();
break;
case ICY:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SNOW).getColor();
break;
case MESA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.RED_SAND).getColor();
break;
case OCEAN:
case RIVER:
colorInt = BlockColorSingletonWrapper.INSTANCE.getWaterColor().getColor();
tintValue = biome.getWaterColor();
break;
case PLAINS:
case SAVANNA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.GRASS_BLOCK).getColor();
tintValue = biome.getGrassColor(x, z);
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case TAIGA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SPRUCE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case JUNGLE:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.JUNGLE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case NONE:
default:
case SWAMP:
case FOREST:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.OAK_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
}
return colorInt;
}
@Override
public int getGrassTint(int x, int z)
{
return biome.getGrassColor(x, z);
}
@Override
public int getFolliageTint()
{
return biome.getFoliageColor();
}
@Override
public int getWaterTint()
{
return biome.getWaterColor();
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BiomeWrapper))
return false;
BiomeWrapper that = (BiomeWrapper) o;
return Objects.equals(biome, that.biome);
}
@Override public int hashCode()
{
return Objects.hash(biome);
}
}
@@ -0,0 +1,56 @@
package com.seibel.lod.fabric.wrappers.world;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import net.minecraft.world.level.dimension.DimensionType;
/**
*
* @author ??
* @version 11-15-2021
*/
public class DimensionTypeWrapper implements IDimensionTypeWrapper
{
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>();
private final DimensionType dimensionType;
public DimensionTypeWrapper(DimensionType dimensionType)
{
this.dimensionType = dimensionType;
}
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
{
//first we check if the biome has already been wrapped
if(dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
return dimensionTypeWrapperMap.get(dimensionType);
//if it hasn't been created yet, we create it and save it in the map
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper;
}
@Override
public String getDimensionName()
{
return dimensionType.effectsLocation().getPath();
}
@Override
public boolean hasCeiling()
{
return dimensionType.hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return dimensionType.hasSkyLight();
}
}
@@ -0,0 +1,152 @@
package com.seibel.lod.fabric.wrappers.world;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.enums.WorldType;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.fabric.wrappers.block.BlockPosWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
/**
*
* @author James Seibel
* @author ??
* @version 11-15-2021
*/
public class WorldWrapper implements IWorldWrapper
{
private static final ConcurrentMap<Level, WorldWrapper> worldWrapperMap = new ConcurrentHashMap<>();
private final Level world;
public final WorldType worldType;
public WorldWrapper(Level newWorld)
{
world = newWorld;
if (world.getClass() == ServerLevel.class)
worldType = WorldType.ServerWorld;
else if (world.getClass() == ClientLevel.class)
worldType = WorldType.ClientWorld;
else
worldType = WorldType.Unknown;
}
public static WorldWrapper getWorldWrapper(Level world)
{
if (world == null) return null;
//first we check if the biome has already been wrapped
if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null)
return worldWrapperMap.get(world);
//if it hasn't been created yet, we create it and save it in the map
WorldWrapper worldWrapper = new WorldWrapper(world);
worldWrapperMap.put(world, worldWrapper);
//we return the newly created wrapper
return worldWrapper;
}
public static void clearMap()
{
worldWrapperMap.clear();
}
@Override
public WorldType getWorldType() {
return worldType;
}
@Override
public DimensionTypeWrapper getDimensionType()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType());
}
@Override
public int getBlockLight(AbstractBlockPosWrapper blockPos)
{
return world.getLightEngine().blockEngine.getLightValue(((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getSkyLight(AbstractBlockPosWrapper blockPos)
{
return world.getLightEngine().skyEngine.getLightValue(((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public BiomeWrapper getBiome(AbstractBlockPosWrapper blockPos)
{
return BiomeWrapper.getBiomeWrapper(world.getBiome(((BlockPosWrapper) blockPos).getBlockPos()));
}
public Level getWorld()
{
return world;
}
@Override
public boolean hasCeiling()
{
return world.dimensionType().hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return world.dimensionType().hasSkyLight();
}
@Override
public boolean isEmpty()
{
return world == null;
}
@Override
public int getHeight()
{
return world.getHeight();
}
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
@Override
public File getSaveFolder() throws UnsupportedOperationException
{
if (worldType != WorldType.ServerWorld)
throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds.");
ServerChunkCache chunkSource = ((ServerLevel) world).getChunkSource();
return chunkSource.dataStorage.dataFolder;
}
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
public ServerLevel getServerWorld() throws UnsupportedOperationException
{
if (worldType != WorldType.ServerWorld)
throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds.");
return (ServerLevel) world;
}
@Override
public int getSeaLevel()
{
// TODO this is depreciated, what should we use instead?
return world.getSeaLevel();
}
}
@@ -0,0 +1,358 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.wrappers.worldGeneration;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.fabric.wrappers.WrapperUtil;
import com.sun.jna.Structure;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.SectionPos;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.EmptyTickList;
import net.minecraft.world.level.TickList;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.Nullable;
/**
* This is a fake ServerWorld used when generating features.
* It allows us to keep each LodChunk generation independent
* of the actual ServerWorld, allowing
* multithread generation.
*
* @author James Seibel
* @version 7-26-2021
*/
public class LodServerWorld implements WorldGenLevel
{
public HashMap<Heightmap.Types, Heightmap> heightmaps = new HashMap<>();
public final ChunkAccess chunk;
public final ServerLevel serverWorld;
public LodServerWorld(ServerLevel newServerWorld, ChunkAccess newChunk)
{
chunk = newChunk;
serverWorld = newServerWorld;
}
@Override
public int getHeight(Heightmap.Types heightmapType, int x, int z)
{
// make sure the block position is set relative to the chunk
x = x % LodUtil.CHUNK_WIDTH;
x = (x < 0) ? x + 16 : x;
z = z % LodUtil.CHUNK_WIDTH;
z = (z < 0) ? z + 16 : z;
return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z);
}
@Override
public Biome getBiome(BlockPos pos)
{
return chunk.getBiomes().getNoiseBiome(pos.getX() >> 2, pos.getY() >> 2, pos.getZ() >> 2);
}
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft)
{
return chunk.setBlockState(pos, state, false) == state;
}
@Override
public BlockState getBlockState(BlockPos pos)
{
return chunk.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos pos)
{
return chunk.getFluidState(pos);
}
@Override
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> state)
{
return state.test(chunk.getBlockState(pos));
}
@Override
public boolean isFluidAtPosition(BlockPos pos, Predicate<FluidState> state) {
return state.test(chunk.getFluidState(pos));
}
@Override
public TickList<Block> getBlockTicks()
{
return EmptyTickList.empty();
}
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull)
{
return chunk;
}
@Override
public Stream<? extends StructureStart<?>> startsForFeature(SectionPos p_241827_1_, StructureFeature<?> p_241827_2_)
{
return serverWorld.startsForFeature(p_241827_1_, p_241827_2_);
}
@Override
public TickList getLiquidTicks()
{
return EmptyTickList.empty();
}
@Override
public LevelLightEngine getLightEngine()
{
return new LevelLightEngine(null, false, false);
}
@Override
public long getSeed()
{
return serverWorld.getSeed();
}
@Override
public RegistryAccess registryAccess()
{
return serverWorld.registryAccess();
}
/**
* All methods below shouldn't be needed
* and thus have been left unimplemented.
*/
@Override
public Random getRandom()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void playSound(Player player, BlockPos pos, SoundEvent soundIn, SoundSource category, float volume,
float pitch)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void addParticle(ParticleOptions particleData, double x, double y, double z, double xSpeed, double ySpeed,
double zSpeed)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public BiomeManager getBiomeManager()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSeaLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public float getShade(Direction p_230487_1_, boolean p_230487_2_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public WorldBorder getWorldBorder()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean removeBlock(BlockPos pos, boolean isMoving)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public ServerLevel getLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public ChunkSource getChunkSource()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Nullable
@Override
public MinecraftServer getServer() {
return serverWorld.getServer();
}
@Override
public LevelData getLevelData()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void levelEvent(Player arg0, int arg1, BlockPos arg2, int arg3)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void gameEvent(@Nullable Entity entity, GameEvent gameEvent, BlockPos blockPos) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<Entity> getEntities(Entity arg0, AABB arg1, Predicate<? super Entity> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public <T extends Entity> List<T> getEntities(EntityTypeTest<Entity, T> entityTypeTest, AABB aABB, Predicate<? super T> predicate) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public <T extends Entity> List<T> getEntitiesOfClass(Class<T> arg0, AABB arg1,
Predicate<? super T> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<? extends Player> players()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSkyDarken()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean isClientSide()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DimensionType dimensionType()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public BlockEntity getBlockEntity(BlockPos p_175625_1_)
{
throw new UnsupportedOperationException("Not Implemented");
}
}
@@ -0,0 +1,393 @@
package com.seibel.lod.fabric.wrappers.worldGeneration;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
import com.seibel.lod.fabric.wrappers.WrapperUtil;
import com.seibel.lod.fabric.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.fabric.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.fabric.wrappers.world.WorldWrapper;
import net.minecraft.core.Registry;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.chunk.*;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
/**
* This is used for generating chunks
* in a variety of detail and threading levels.
*
* @author coolGi2007
* @author James Seibel
* @version 11-23-2021
*/
public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper
{
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
/**
* If a configured feature fails for whatever reason,
* add it to this list. This will hopefully remove any
* features that could cause issues down the line.
*/
private static final ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> FEATURES_TO_AVOID = new ConcurrentHashMap<>();
public final ServerLevel serverWorld;
public final LodDimension lodDim;
public final LodBuilder lodBuilder;
public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
super(newLodBuilder, newLodDimension, worldWrapper);
lodBuilder = newLodBuilder;
lodDim = newLodDimension;
serverWorld = ((WorldWrapper) worldWrapper).getServerWorld();
}
/** takes about 2-5 ms */
@Override
public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode)
{
/*
List<ChunkAccess> chunkList = new LinkedList<>();
ProtoChunk chunk = new ProtoChunk((ChunkPosWrapper) pos, UpgradeData.EMPTY, serverWorld);
chunkList.add(chunk);
ServerChunkCache chunkSource = serverWorld.getChunkSource();
ChunkGenerator chunkGen = chunkSource.getGenerator();
// generate the terrain (this is thread safe)
// ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ThreadedLevelLightEngine) serverWorld.getLightEngine(), null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
// generate fake height data for this LOD
int seaLevel = serverWorld.getSeaLevel();
boolean simulateHeight = generationMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
boolean inTheEnd = false;
// add fake heightmap data so our LODs aren't at height 0
Heightmap heightmap = new Heightmap(chunk, WrapperUtil.DEFAULT_HEIGHTMAP);
for (int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++)
{
if (simulateHeight)
{
// these heights are of course aren't super accurate,
// they are just to simulate height data where there isn't any
switch (chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory())
{
case NETHER:
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
break;
case EXTREME_HILLS:
heightmap.setHeight(x, z, seaLevel + 30);
break;
case MESA:
case JUNGLE:
heightmap.setHeight(x, z, seaLevel + 20);
break;
case BEACH:
heightmap.setHeight(x, z, seaLevel + 5);
break;
case NONE:
heightmap.setHeight(x, z, 0);
break;
case OCEAN:
case RIVER:
heightmap.setHeight(x, z, seaLevel);
break;
case THEEND:
inTheEnd = true;
break;
// DESERT
// FOREST
// ICY
// MUSHROOM
// SAVANNA
// SWAMP
// TAIGA
// PLAINS
default:
heightmap.setHeight(x, z, seaLevel + 10);
break;
}// heightmap switch
}
else
{
// we aren't simulating height
// always use sea level
heightmap.setHeight(x, z, seaLevel);
}
}// z
}// x
chunk.setHeightmap(WrapperUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData());
if (!inTheEnd)
{
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
else
{
// if we are in the end, don't generate any chunks.
// Since we don't know where the islands are, everything
// generates the same, and it looks awful.
//TODO it appears that 'if' can be collapsed, but comment says that it should not be a case
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
// long startTime = System.currentTimeMillis();
// long endTime = System.currentTimeMillis();
// System.out.println(endTime - startTime);
*/
}
/** takes about 10 - 20 ms */
@Override
public void generateSurface(AbstractChunkPosWrapper pos)
{
/*
List<ChunkAccess> chunkList = new LinkedList<>();
ProtoChunk chunk = new ProtoChunk(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkCache chunkSource = serverWorld.getChunkSource();
ThreadedLevelLightEngine lightEngine = (ThreadedLevelLightEngine) serverWorld.getLightEngine();
StructureManager templateManager = serverWorld.getStructureManager();
ChunkGenerator chunkGen = chunkSource.getGenerator();
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// this feature has been proven to be thread safe,
// so we will add it
IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC);
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.SURFACE));
/*TODO if we want to use Biome utils and terrain utils for overworld
* lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/
}
/**
* takes about 15 - 20 ms
* <p>
* Causes concurrentModification Exceptions,
* which could cause instability or world generation bugs
*/
@Override
public void generateFeatures(AbstractChunkPosWrapper pos)
{
/*
List<ChunkAccess> chunkList = new LinkedList<>();
ProtoChunk chunk = new ProtoChunk(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine();
TemplateManager templateManager = serverWorld.getStructureManager();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// get all the biomes in the chunk
HashSet<Biome> biomes = new HashSet<>();
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2);
// Issue #35
// For some reason Jungle biomes cause incredible lag
// the features here must be interacting with each other
// in unpredictable ways (specifically tree feature generation).
// When generating Features my CPU usage generally hovers around 30 - 40%
// when generating Jungles it spikes to 100%.
if (biome.getBiomeCategory() != Biome.BiomeCategory.JUNGLE)
{
// should probably use the heightmap here instead of seaLevel,
// but this seems to get the job done well enough
biomes.add(biome);
}
}
}
boolean allowUnstableFeatures = CONFIG.client().worldGenerator().getAllowUnstableFeatureGeneration();
// generate all the features related to this chunk.
// this may or may not be thread safe
for (Biome biome : biomes)
{
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.generationSettings.features();
for (List<Supplier<ConfiguredFeature<?, ?>>> suppliers : featuresForState)
{
for (Supplier<ConfiguredFeature<?, ?>> featureSupplier : suppliers)
{
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
if (!allowUnstableFeatures &&
FEATURES_TO_AVOID.containsKey(configuredFeature.hashCode()))
continue;
try
{
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
}
catch (ConcurrentModificationException | UnsupportedOperationException e)
{
// This will happen. I'm not sure what to do about it
// except pray that it doesn't affect the normal world generation
// in any harmful way.
// Update: this can cause crashes and high CPU usage.
// Issue #35
// I tried cloning the config for each feature, but that
// path was blocked since I can't clone lambda methods.
// I tried using a deep cloning library and discovered
// the problem there.
// ( https://github.com/kostaskougios/cloning
// and
// https://github.com/EsotericSoftware/kryo )
if (!allowUnstableFeatures)
FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
// This will happen when the LodServerWorld
// isn't able to return something that a feature
// generator needs
catch (Exception e)
{
// I'm not sure what happened, print to the log
e.printStackTrace();
if (!allowUnstableFeatures)
FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
}
}
}
// generate a Lod like normal
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.FEATURES));
*/
}
/**
* Generates using MC's ServerWorld.
* <p>
* on pre generated chunks 0 - 1 ms <br>
* on un generated chunks 0 - 50 ms <br>
* with the median seeming to hover around 15 - 30 ms <br>
* and outliers in the 100 - 200 ms range <br>
* <p>
* Note this should not be multithreaded and does cause server/simulation lag
* (Higher lag for generating than loading)
*/
@Override
public void generateFull(AbstractChunkPosWrapper pos)
{
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.FULL));
}
/*
* performance/generation tests related to
* serverWorld.getChunk(x, z, ChunkStatus. *** )
true/false is whether they generated blocks or not
the time is how long it took to generate
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
ChunkStatus.BIOMES 1 - 10 ms false (no height)
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
ChunkStatus.FEATURES 7 - 25 ms true
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
ChunkStatus.LIGHT 20 - 40 ms true
ChunkStatus.FULL 30 - 50 ms true
ChunkStatus.SPAWN 50 - 80 ms true
At this point I would suggest using FEATURES, as it generates snow and trees
(and any other object that are needed to make biomes distinct)
Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
*/
}
@@ -0,0 +1,97 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common;
import java.util.ArrayList;
import java.util.List;
public class WorldWorkerManager
{
private static List<IWorker> workers = new ArrayList<IWorker>();
private static long startTime = -1;
private static int index = 0;
public static void tick(boolean start)
{
if (start)
{
startTime = System.currentTimeMillis();
return;
}
index = 0;
IWorker task = getNext();
if (task == null)
return;
long time = 50 - (System.currentTimeMillis() - startTime);
if (time < 10)
time = 10; //If ticks are lagging, give us at least 10ms to do something.
time += System.currentTimeMillis();
while (System.currentTimeMillis() < time && task != null)
{
boolean again = task.doWork();
if (!task.hasWork())
{
remove(task);
task = getNext();
}
else if (!again)
{
task = getNext();
}
}
}
public static synchronized void addWorker(IWorker worker)
{
workers.add(worker);
}
private static synchronized IWorker getNext()
{
return workers.size() > index ? workers.get(index++) : null;
}
private static synchronized void remove(IWorker worker)
{
workers.remove(worker);
index--;
}
//Internal only, used to clear everything when the server shuts down.
public static synchronized void clear()
{
workers.clear();
}
public static interface IWorker
{
boolean hasWork();
/**
* Perform a task, returning true from this will have the manager call this function again this tick if there is time left.
* Returning false will skip calling this worker until next tick.
*/
boolean doWork();
}
}
@@ -0,0 +1,66 @@
{
"lod.title": "Distant Horizons mod",
"text.autoconfig.lod.title": "Distant Horizons config",
"text.autoconfig.lod.option.client": "Client",
"text.autoconfig.lod.option.client.graphics": "Graphics",
"text.autoconfig.lod.option.client.graphics.qualityOption": "Quality option",
"text.autoconfig.lod.option.client.graphics.qualityOption.drawResolution": "Draw resolution",
"text.autoconfig.lod.option.client.graphics.qualityOption.drawResolution.@Tooltip": "What is the maximum detail fake chunks should be drawn at?",
"text.autoconfig.lod.option.client.graphics.qualityOption.lodChunkRenderDistance": "Chunk render distance",
"text.autoconfig.lod.option.client.graphics.qualityOption.lodChunkRenderDistance.@Tooltip": "The mod's render distance, measured in chunks",
"text.autoconfig.lod.option.client.graphics.qualityOption.verticalQuality": "Vertical quality",
"text.autoconfig.lod.option.client.graphics.qualityOption.verticalQuality.@Tooltip": "This indicates how detailed fake chunks will represent overhangs, caves, floating islands, ect. \nHigher options will use more memory and increase GPU usage",
"text.autoconfig.lod.option.client.graphics.qualityOption.horizontalScale": "Horizontal scale",
"text.autoconfig.lod.option.client.graphics.qualityOption.horizontalScale.@Tooltip": "This indicates how quickly fake chunks drop off in quality",
"text.autoconfig.lod.option.client.graphics.qualityOption.horizontalQuality": "Horizontal quality",
"text.autoconfig.lod.option.client.graphics.qualityOption.horizontalQuality.@Tooltip": "This indicates the exponential base of the quadratic drop-off",
"text.autoconfig.lod.option.client.graphics.fogQualityOption": "Fog quality option",
"text.autoconfig.lod.option.client.graphics.fogQualityOption.fogDistance": "Fog distance",
"text.autoconfig.lod.option.client.graphics.fogQualityOption.fogDistance.@Tooltip": "At what distance should Fog be drawn on the fake chunks?",
"text.autoconfig.lod.option.client.graphics.fogQualityOption.fogDrawOverride": "Fog draw override",
"text.autoconfig.lod.option.client.graphics.fogQualityOption.fogDrawOverride.@Tooltip": "When should fog be drawn?",
"text.autoconfig.lod.option.client.graphics.fogQualityOption.disableVanillaFog": "Disable vanilla fog",
"text.autoconfig.lod.option.client.graphics.fogQualityOption.disableVanillaFog.@Tooltip": "If true disable Minecraft's fog. \nMay cause issues with other mods that edit fog. \nMay cause errors with other fog editing mods",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption": "Advanced quality option",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.lodTemplate": "LOD template",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.lodTemplate.@Tooltip": "How should the LODs be drawn? \nNOTE: Currently only CUBIC is implemented!",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.disableDirectionalCulling": "Disable directional culling",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.disableDirectionalCulling.@Tooltip": "If false fake chunks behind the player's camera aren't drawn, increasing performance. \nIf true all LODs are drawn, even those behind the player's camera, decreasing performance",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality": "Always draw at max quality",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.@Tooltip": "Disable quality falloff, all fake chunks will be drawn at the highest available detail level",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.vanillaOverdraw": "Vanilla overdraw",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.vanillaOverdraw.@Tooltip": "How often should LODs be drawn on top of regular chunks? \nHALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves.",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.gpuUploadMethod": "GPU upload method",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.gpuUploadMethod.@Tooltip": "What method should be used to upload geometry to the GPU?",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.useExtendedNearClipPlane": "Use extended near clip plane",
"text.autoconfig.lod.option.client.graphics.advancedGraphicsOption.useExtendedNearClipPlane.@Tooltip": "Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly especially when in/near an ocean",
"text.autoconfig.lod.option.client.worldGenerator": "World generator",
"text.autoconfig.lod.option.client.worldGenerator.generationPriority": "Generation priority",
"text.autoconfig.lod.option.client.worldGenerator.generationPriority.@Tooltip": "What is the priority of the chunks being generated around the player",
"text.autoconfig.lod.option.client.worldGenerator.distanceGenerationMode": "Distance generation mode",
"text.autoconfig.lod.option.client.worldGenerator.distanceGenerationMode.@Tooltip": "How much of the generation should be used when generating fake chunks",
"text.autoconfig.lod.option.client.worldGenerator.allowUnstableFeatureGeneration": "Allow unstable feature generation",
"text.autoconfig.lod.option.client.worldGenerator.allowUnstableFeatureGeneration.@Tooltip": "Some features may not be thread safe. \nCould cause instability and crashes",
"text.autoconfig.lod.option.client.worldGenerator.blockToAvoid": "Block to avoid",
"text.autoconfig.lod.option.client.worldGenerator.blockToAvoid.@Tooltip": "What block to avoid when generating fake chunks",
"text.autoconfig.lod.option.client.advancedModOptions": "Advance mod options",
"text.autoconfig.lod.option.client.advancedModOptions.threading": "Threading",
"text.autoconfig.lod.option.client.advancedModOptions.threading.numberOfWorldGenerationThreads": "NO. of world generation threads",
"text.autoconfig.lod.option.client.advancedModOptions.threading.numberOfWorldGenerationThreads.@Tooltip": "This is how many threads are used when generating LODs outside the normal render distance. \nIf you experience stuttering when generating distant LODs, decrease this number. If you want to increase LOD generation speed, increase this number \nCan only be between 1 and your current number of threads",
"text.autoconfig.lod.option.client.advancedModOptions.threading.numberOfBufferBuilderThreads": "NO. of buffer builder threads",
"text.autoconfig.lod.option.client.advancedModOptions.threading.numberOfBufferBuilderThreads.@Tooltip": "This is how many threads are used when building vertex buffers (The things sent to your GPU to draw the fake chunks) \nCan only be between 1 and your current number of threads",
"text.autoconfig.lod.option.client.advancedModOptions.buffers": "Buffers",
"text.autoconfig.lod.option.client.advancedModOptions.buffers.rebuildTimes": "Rebuild times",
"text.autoconfig.lod.option.client.advancedModOptions.buffers.rebuildTimes.@Tooltip": "Rebuild times",
"text.autoconfig.lod.option.client.debug": "Debug",
"text.autoconfig.lod.option.client.debug.drawLods": "Draw LOD's",
"text.autoconfig.lod.option.client.debug.drawLods.@Tooltip": "If true, the mod is enabled and fake chunks will be drawn.",
"text.autoconfig.lod.option.client.debug.debugMode": "Debug mode",
"text.autoconfig.lod.option.client.debug.debugMode.@Tooltip": "What type of debug mode do you want",
"text.autoconfig.lod.option.client.debug.enableDebugKeybindings": "Enable debug keybindings",
"text.autoconfig.lod.option.client.debug.enableDebugKeybindings.@Tooltip": "Enable debug keybindings to change the Debug mode on the fly in game",
"toast.lod.title": "Distant Horizons",
"key.lod.category": "Distant Horizons",
"key.lod.DebugToggle": "Debug toggle",
"key.lod.DrawToggle": "Draw toggle"
}
+57
View File
@@ -0,0 +1,57 @@
{
"schemaVersion": 1,
"id": "lod",
"version": "${version}",
"name": "Distant Horizons Fabric",
"description": "This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost",
"authors": [
"coolGi2007",
"Ran",
"Original LOD Creators"
],
"contact": {
"homepage": "https://www.curseforge.com/minecraft/mc-mods/lod-level-of-detail",
"sources": "https://gitlab.com/jeseibel/minecraft-lod-mod/-/tree/1.17.1_fabric",
"issues": "https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues"
},
"license": "CC0-1.0",
"icon": "icon.png",
"environment": "client",
"entrypoints": {
"client": [
"com.seibel.lod.fabric.Main"
],
"modmenu": [
"com.seibel.lod.fabric.wrappers.config.ModMenuIntegration"
]
},
"mixins": [
"lod.mixins.json"
],
"accessWidener" : "lod.accesswidener",
"depends": {
"fabricloader": ">=0.11.3",
"fabric": "*",
"minecraft": "1.17.x",
"java": ">=16",
"modmenu": ">=2.0.14",
"cloth-config2": ">=5.0.38"
},
"suggests": {
"another-mod": "*"
},
"custom": {
"modmenu": {
"links": {
"modmenu.discord": "https://discord.gg/xAB8G4cENx"
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"required": true,
"minVersion": "0.8",
"package": "com.seibel.lod.fabric.mixins",
"compatibilityLevel": "JAVA_16",
"mixins": [],
"client": [
"MixinMinecraft",
"MixinWorldRenderer",
"events.MixinClientLevel",
"events.MixinMinecraft",
"events.MixinServerLevel"
],
"server": [],
"injectors": {
"defaultRequire": 1
}
}