Compare commits

...

32 Commits

Author SHA1 Message Date
James Seibel fc516a20d5 remove dev from version number 2026-05-03 20:47:00 -05:00
James Seibel 791c2c3426 up api version 6.1.0 -> 6.1.1 2026-05-03 20:47:00 -05:00
James Seibel b00079897a fix blaze generic obj render close on wrong thread 2026-05-03 20:47:00 -05:00
James Seibel 37b73e1d5c Improve stack/GC tracking for GL buffers 2026-05-03 16:45:40 -05:00
James Seibel 4bd1136713 Fix generic buffer cleanup 2026-05-03 16:36:53 -05:00
James Seibel ef98dbd5fd color override API tests 2026-05-02 21:26:33 -05:00
James Seibel 5df0a60b06 add extra optional GLBuffer phantom logging 2026-05-02 21:18:24 -05:00
James Seibel 9feb20eff8 remove unnecessary warning in GLBuffer 2026-05-02 21:18:01 -05:00
James Seibel c9267d61a8 allow toggling tracy via gradle.properties 2026-05-02 15:21:44 -05:00
James Seibel a29e225a80 Fix LOD shading applying incorrectly with Iris 2026-05-02 15:14:33 -05:00
James Seibel ae0f3c2b3b Add oculus api implementation 2026-05-02 12:43:43 -05:00
James Seibel 852ea75449 Update coreSubProjects 2026-05-02 11:45:54 -05:00
James Seibel ab6a5dad2b Revert "Attempt to merge CI changes"
This reverts commit 1a1eaca280.
2026-05-02 11:45:29 -05:00
James Seibel 1a1eaca280 Attempt to merge CI changes 2026-05-02 11:44:09 -05:00
James Seibel 0272f8c57f remove unused manual build scripts 2026-05-02 11:43:13 -05:00
James Seibel cf09358710 fix fog common mixin compiling 2026-05-02 11:04:10 -05:00
James Seibel b5d833fa3d simplify mixin fog common code 2026-05-02 10:57:37 -05:00
James Seibel 329dbe9585 Fix block wrapper null pointer 2026-05-02 10:43:41 -05:00
James Seibel c7ae7f155e move fog common mixin code to common 2026-05-02 10:42:05 -05:00
James Seibel 5f7dbb8662 Fix neoforge debugging 2026-05-02 08:00:14 -05:00
James Seibel ee21548151 comment on why sponge maven can't have fabric 2026-05-01 07:42:53 -05:00
Ran-Mewo ca00125960 exclude FabricMC groups from Sponge repository in dh-loader.gradle 2026-04-30 23:28:29 +10:00
James Seibel 4067264e72 Merge branch 'distant-horizons-fix' 2026-04-29 07:45:29 -05:00
James Seibel 9c2d243ad4 Add position finder debug config 2026-04-29 07:35:24 -05:00
James Seibel de9d8b0d2e Optimize BlockStateWrapper getter 2026-04-28 21:15:07 -05:00
James Seibel 67f4615b34 add backup to texture color getting 2026-04-28 07:46:53 -05:00
James Seibel cd7a130ee4 use camera pos for detail calculations 2026-04-28 07:09:26 -05:00
James Seibel 4d17f7aecf fix null pointer on dedicated server shutdown 2026-04-27 07:48:11 -05:00
James Seibel a59e7500ab Fix wyncraft getting stuck at low LOD quality 2026-04-27 07:27:07 -05:00
James Seibel 40040294e7 up fabric api 1.21.1 version 2026-04-25 15:09:48 -05:00
James Seibel 783e61ec3d Update the readme 2026-04-24 07:47:41 -05:00
James Seibel e09db5d7df up version number 3.0.2 -> 3.0.3 2026-04-24 06:51:44 -05:00
31 changed files with 757 additions and 444 deletions
-19
View File
@@ -1,19 +0,0 @@
**/.git
**/.gitlab
**/.cache
buildAllJars
**/_Misc Files
*.bat
*.md
*.sh
*.txt
coreSubProjects/*.md
coreSubProjects/*.txt
**/.gitignore
**/.gitattributes
**/.gitlab-cy.yml
**/.gitmodules
-12
View File
@@ -1,12 +0,0 @@
FROM eclipse-temurin:25-jdk
WORKDIR /home/build/
COPY ./gradlew .
RUN chmod +x ./gradlew
CMD echo "\r========== [CLEAN: $MC_VER] ==========" && \
./gradlew clean -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [BUILD: $MC_VER] ==========" && \
./gradlew build -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [MERGE: $MC_VER] ==========" && \
./gradlew mergeJars -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [DONE: $MC_VER] =========="
+29 -127
View File
@@ -14,89 +14,11 @@ Below is a video demonstrating the system:
<br> <br>
## Minecraft and Library Versions
### This branch supports the following versions of Minecraft:
#### 1.20.4, 1.20.3 (Default)
Fabric: 0.15.1\
Fabric API: 0.91.2+1.20.4\
Forge: 49.0.30\
NeoForge: 118-beta\
Parchment: 1.20.2:2023.12.10\
Modmenu: 9.0.0-pre.1
#### 1.20.2
Fabric: 0.14.24\
Fabric API: 0.90.4+1.20.2\
Forge: 48.0.13\
Parchment: 1.20.1:2023.09.03\
Modmenu: 8.0.0
#### 1.20.1, 1.20
Fabric: 0.14.24\
Fabric API: 0.90.4+1.20.1\
Forge: 47.2.1\
Parchment: 1.20.1:2023.09.03\
Modmenu: 7.2.2
#### 1.19.4
Fabric: 0.14.24\
Fabric API: 0.87.1+1.19.4\
Forge: 45.2.4\
Parchment: 1.19.4:2023.06.26\
Modmenu: 6.3.1
#### 1.19.2
Fabric: 0.14.24\
Fabric API: 0.76.1+1.19.2\
Forge: 43.3.2\
Parchment: 1.19.2:2022.11.27\
Modmenu: 4.2.0-beta.2
#### 1.18.2
Fabric: 0.14.24\
Fabric API: 0.76.0+1.18.2\
Forge: 40.2.10\
Parchment: 1.18.2:2022.11.06\
Modmenu: 3.2.5
#### 1.17.1, 1.17
Fabric: 0.14.24\
Fabric API: 0.46.1+1.17\
Forge: 37.1.1\
Parchment: 1.17.1:2021.12.12\
Modmenu: 2.0.14
#### 1.16.5, 1.16.4
Fabric: 0.14.24\
Fabric API: 0.42.0+1.16\
Forge: 36.2.39\
Parchment: 1.16.5:2022.03.06\
Modmenu: 1.16.22
### Versions no longer supported
- 1.18.1, 1.18
- 1.19.1, 1.19
- 1.19.3
<br>
### Plugin and Library versions
Gradle: 8.5\
Fabric loom: 1.4-SNAPSHOT\
Architectury loom (Forge gradle replacement): 1.4-SNAPSHOT\
Sponge vanilla gradle: 0.2.1-SNAPSHOT\
Java Preprocessor plugin: Manifold Preprocessor
<br>
## Source Code Installation ## Source Code Installation
### Prerequisites ### Prerequisites
* A Java Development Kit (JDK) for Java 17 (recommended) or newer. <br> * A Java Development Kit (JDK) for Java 25 (recommended) or newer. <br>
Visit https://www.oracle.com/java/technologies/downloads/ for installers. Visit https://www.oracle.com/java/technologies/downloads/ for installers.
* Git or someway to clone git projects. <br> * Git or someway to clone git projects. <br>
Visit https://git-scm.com/ for installers. Visit https://git-scm.com/ for installers.
@@ -104,15 +26,10 @@ Java Preprocessor plugin: Manifold Preprocessor
**If using IntelliJ:** **If using IntelliJ:**
1. Install the Manifold plugin 1. Install the Manifold plugin
- https://plugins.jetbrains.com/plugin/10057-manifold-ij
2. Open IDEA and import the build.gradle 2. Open IDEA and import the build.gradle
3. Refresh the Gradle project in IDEA if required 3. Refresh the Gradle project in IDEA if required
**If using Eclipse: (Note that Eclipse doesn't support Manifold's preprocessor!)**
1. Run the command: `./gradlew geneclipseruns`
2. Run the command: `./gradlew eclipse`
3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft)
4. Import the project into eclipse
<br> <br>
## Switching Versions ## Switching Versions
@@ -127,72 +44,42 @@ In IntelliJ, you will also need to do a gradle sync if it didn't happen automati
## Compiling ## Compiling
Prerequisites: Prerequisites:
- JDK 17 or newer - JDK 25 or newer
From the File Explorer:
1. Download and extract the project zip
2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows)
5. Merge the jars with `./gradlew mergeJars`
6. The compiled jar file will be in the folder `Merged`
From the command line: From the command line:
1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git` 1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git`
2. `cd distant-horizons` 2. `cd distant-horizons`
3. `./gradlew assemble` 3. `./gradlew assemble`
4. `./gradlew mergeJars` 5. The compiled jar file will be in the folder `\build\libs`
5. The compiled jar file will be in the folder `Merged`
Run tests with: `./gradlew test` From the File Explorer:
1. Download and extract the project zip
2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
3. Open command prompt/terminal in the project folder
4. Run the commands: `./gradlew assemble`
6. The compiled jar file will be in the folder `\build\libs`
>Note: You can add the argument `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file.\ >Note: You can add the argument `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file.\
> For example: `./gradlew assemble -PmcVer=1.18.2` > For example: `./gradlew assemble -PmcVer=1.18.2`
<br> <br>
## Compiling with Docker
`./compile <version>`
You can also locally compile the DH jars without a Java environment by using Docker. Where `<version>` is the version of Minecraft to compile for (ie `1.20.1`), or the keyword `all`. See [Versions](#minecraft-and-library-versions) for a list of all supported values.
<br>
## Other commands ## Other commands
`./gradlew --refresh-dependencies` to refresh local dependencies.
`./gradlew clean` to delete any compiled code.
<br>
## Note to self
The Minecraft source code is NOT added to your workspace in an editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only.
Source code uses Mojang mappings & [Parchment](https://parchmentmc.org/) mappings.
To generate the source code run `./gradlew genSources` <br>
If your IDE fails to auto-detect the source jars when browsing Minecraft classes; manually select the JAR file ending with -sources.jar when prompted by your IDE. <br>
(In IntelliJ it's at the top where it says "choose sources" when browsing a Minecraft class)
<br>
## Other Useful commands
Run the standalone jar: `./gradlew run` <br> Run the standalone jar: `./gradlew run` <br>
Build the standalone jar: `./gradlew core:build` <br> Build the standalone jar: `./gradlew core:build` <br>
Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build` <br> Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build` <br>
Only build Forge: `./gradlew forge:assemble` or `./gradlew forge:build` <br> Only build Forge: `./gradlew forge:assemble` or `./gradlew forge:build` <br>
Run the Fabric client (for debugging): `./gradlew fabric:runClient` <br> Run the Fabric client (for debugging): `./gradlew fabric:runClient` <br>
Run the Forge client (for debugging): `./gradlew forge:runClient` <br> Run the Forge client (for debugging): `./gradlew forge:runClient` <br>
Delete all compiled code: `./gradlew clean` <br>
Refresh local dependencies: `./gradlew --refresh-dependencies`
To build all versions: `./buildAll` (all builds will end up in the `Merged` folder) To build all versions: `./buildAll`
<br> <br>
## Open Source Acknowledgements ## Open Source Libraries
Forgix (To merge multiple mod versions into one jar) [_Formerly_ [_DHJarMerger_](https://github.com/Ran-helo/DHJarMerger)]\ Forgix (To merge multiple mod versions into one jar) [_Formerly_ [_DHJarMerger_](https://github.com/Ran-helo/DHJarMerger)]\
https://github.com/PacifistMC/Forgix https://github.com/PacifistMC/Forgix
@@ -208,3 +95,18 @@ https://github.com/blackears/svgSalamander
sqlite-jdbc\ sqlite-jdbc\
https://github.com/xerial/sqlite-jdbc https://github.com/xerial/sqlite-jdbc
## Acknowledgements
Distant Horizons has been graciously provided an open source license for <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>.
> <img src="https://www.yourkit.com/images/yklogo.png">
>
> YourKit supports open source projects with innovative and intelligent tools
for monitoring and profiling Java and .NET applications.
YourKit is the creator of <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>,
<a href="https://www.yourkit.com/dotnet-profiler/">YourKit .NET Profiler</a>,
and <a href="https://www.yourkit.com/youmonitor/">YourKit YouMonitor</a>.
+35 -4
View File
@@ -54,7 +54,12 @@ repositories {
url "https://www.cursemaven.com" url "https://www.cursemaven.com"
content { includeGroup "curse.maven" } content { includeGroup "curse.maven" }
} }
maven { url "https://repo.spongepowered.org/maven/" } maven {
url "https://repo.spongepowered.org/maven/"
// exclusion is needed since sponge has a deprecated version of fabric
// that gradle would prefer to get over the up-to-date version modrinth provides
content { excludeGroupByRegex "net\\.fabricmc(\\..*)?" }
}
maven { url "https://maven.terraformersmc.com/" } maven { url "https://maven.terraformersmc.com/" }
maven { url "https://maven.neoforged.net/releases/" } maven { url "https://maven.neoforged.net/releases/" }
flatDir { flatDir {
@@ -361,6 +366,23 @@ if (isNotCommonProject) {
!arg.startsWith("-XX:MaxGCPauseMillis") !arg.startsWith("-XX:MaxGCPauseMillis")
} }
runTask.jvmArgs = filteredArgs runTask.jvmArgs = filteredArgs
// fix (Neo)forge debug running
doFirst {
def modsDir = rootProject.file("run/${isClient ? 'client' : 'server'}/mods")
modsDir.mkdirs()
// Remove any stale DH jars before copying the fresh one
modsDir.listFiles()?.each { file ->
if (file.name.startsWith(rootProject.mod_name)) file.delete()
}
// Copy shadow jar into mods folder so (Neo)Forge discovers it properly
copy {
from shadowJar.archiveFile
into modsDir
}
}
// JVM args // JVM args
runTask.jvmArgs( runTask.jvmArgs(
@@ -371,7 +393,8 @@ if (isNotCommonProject) {
//"-XX:+ZGenerational", //"-XX:+ZGenerational",
rootProject.minecraftMemoryJavaArg, rootProject.minecraftMemoryJavaArg,
) )
if (isClient) { if (isClient)
{
runTask.jvmArgs( runTask.jvmArgs(
"-Dminecraft.api.auth.host=https://nope.invalid", "-Dminecraft.api.auth.host=https://nope.invalid",
"-Dminecraft.api.account.host=https://nope.invalid", "-Dminecraft.api.account.host=https://nope.invalid",
@@ -382,9 +405,17 @@ if (isNotCommonProject) {
// use a consistent username for easier debugging in a given world (vs randomly teleporting to a new user each time the game boots) // use a consistent username for easier debugging in a given world (vs randomly teleporting to a new user each time the game boots)
"--username", "Dev", "--username", "Dev",
// "--renderDebugLabels" is a Mojang command to show render names in RenderDoc // "--renderDebugLabels" is a Mojang command to show render names in RenderDoc
"--renderDebugLabels", "--renderDebugLabels"
)
// enabling tracy causes constant memory growth so it isn't always desired
if (rootProject.minecraftEnableTracy == "true")
{
// "--tracy" is a Mojang command to allow individual frames to be debugged using Tracy https://github.com/wolfpld/tracy/releases/tag/v0.13.1 // "--tracy" is a Mojang command to allow individual frames to be debugged using Tracy https://github.com/wolfpld/tracy/releases/tag/v0.13.1
"--tracy") runTask.args("--tracy")
}
} }
} }
} }
@@ -0,0 +1,110 @@
package com.seibel.distanthorizons.common.commonMixins;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Camera;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.client.Camera;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_3
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_6
import net.minecraft.world.level.material.FogType;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.blaze3d.shaders.FogShape;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.client.renderer.FogParameters;
import org.joml.Vector4f;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#else
import net.minecraft.world.level.material.FogType;
#endif
public class MixinVanillaFogCommon
{
#if MC_VER < MC_1_21_6
public static boolean cancelFog(Camera camera, FogRenderer.FogMode fogMode)
#else
public static boolean cancelFog()
#endif
{
#if MC_VER < MC_1_21_6
Entity entity = camera.getEntity();
#elif MC_VER <= MC_1_21_10
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.getEntity();
#else
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.entity();
#endif
boolean cameraNotInFluid = cameraNotInFluid(camera);
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
boolean cancelFog = !isSpecialFog;
cancelFog = cancelFog && cameraNotInFluid;
#if MC_VER < MC_1_21_6
cancelFog = cancelFog && (fogMode == FogRenderer.FogMode.FOG_TERRAIN);
#endif
cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial();
cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get();
return cancelFog;
}
private static boolean cameraNotInFluid(Camera camera)
{
#if MC_VER < MC_1_17_1
FluidState fluidState = camera.getFluidInCamera();
boolean cameraNotInFluid = fluidState.isEmpty();
#else
FogType fogTypes = camera.getFluidInCamera();
boolean cameraNotInFluid = fogTypes == FogType.NONE;
#endif
return cameraNotInFluid;
}
}
@@ -57,6 +57,7 @@ import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.render.RenderParams; import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory; import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup; import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
@@ -615,5 +616,27 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
// close is called outside the render thread and buffer closing must be done on the render thread
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("Generic Obj Cleanup", () ->
{
if (this.vertUniformBuffer != null)
{
this.vertUniformBuffer.close();
}
});
}
//endregion
} }
#endif #endif
@@ -748,4 +748,27 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
if (this.boxVertexBuffer != null)
{
this.boxVertexBuffer.close();
}
if (this.boxIndexBuffer != null)
{
this.boxIndexBuffer.close();
}
}
//endregion
} }
@@ -23,11 +23,17 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomLoggingHelper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL44; import org.lwjgl.opengl.GL44;
@@ -35,6 +41,8 @@ import java.lang.ref.PhantomReference;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -50,14 +58,35 @@ public class GLBuffer implements AutoCloseable
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
// TODO move to a shared location that can be handled by both GL and Blaze3D buffers
/** if enabled the number of GC'ed buffers will be logged */
private static final boolean LOG_PHANTOM_RECOVERY = ModInfo.IS_DEV_BUILD;
/** If enabled buffers allocation/upload stacks will be logged when GC'ed. */
private static final boolean LOG_PHANTOM_ALLOCATION_STACKS = false; // disabled by default due to the increased GC load
public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3; public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3;
public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER; public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER;
/**
* On macOS the legacy OpenGL -> Metal bridge crashes with SIGBUS in
* {@code _platform_memmove} when {@code glBufferData} or {@code glBufferSubData}
* are called with a large ByteBuffer in one shot. To work around it, we split
* the upload into smaller sub-data calls that each fit comfortably inside the
* driver's internal staging path. <br>
* 256 KiB tuned empirically against macOS 26.5 — a 1 MiB chunk still
* tripped the same {@code _platform_memmove} crash inside
* {@code glBufferSubData_Exec}, but 256 KiB consistently survives.
*/
public static final int MAC_UPLOAD_CHUNK_BYTES = 256 * 1024;
/** Threshold above which the chunked path kicks in on macOS. */
public static final int MAC_UPLOAD_CHUNK_THRESHOLD = MAC_UPLOAD_CHUNK_BYTES;
/** the number of active buffers, can be used for debugging */ /** the number of active buffers, can be used for debugging */
public static AtomicInteger bufferCount = new AtomicInteger(0); public static AtomicInteger bufferCount = new AtomicInteger(0);
private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000; private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000;
private static final ConcurrentHashMap<PhantomReference<? extends GLBuffer>, Integer> PHANTOM_TO_BUFFER_ID = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<PhantomReference<? extends GLBuffer>, Integer> PHANTOM_TO_BUFFER_ID = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, PhantomReference<? extends GLBuffer>> BUFFER_ID_TO_PHANTOM = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<Integer, PhantomReference<? extends GLBuffer>> BUFFER_ID_TO_PHANTOM = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> BUFFER_ID_TO_ALLOCATION_STRING = new ConcurrentHashMap<>();
private static final ReferenceQueue<GLBuffer> PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>(); private static final ReferenceQueue<GLBuffer> PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>();
private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("GLBuffer Cleanup"); private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("GLBuffer Cleanup");
@@ -128,8 +157,9 @@ public class GLBuffer implements AutoCloseable
long writeStamp = renderStampLock.writeLock(); long writeStamp = renderStampLock.writeLock();
try try
{ {
int oldId = this.id; final int oldId = this.id;
this.id = GLMC.glGenBuffers(); this.id = GLMC.glGenBuffers();
//LOGGER.info("created [" + newId + "].");
// destroy the old buffer // destroy the old buffer
// after the new one has been created // after the new one has been created
@@ -139,7 +169,7 @@ public class GLBuffer implements AutoCloseable
{ {
// this ID doesn't need to be tracked anymore // this ID doesn't need to be tracked anymore
tryRemoveBufferIdFromPhantom(oldId); tryRemoveBufferIdFromPhantom(oldId);
destroyBufferIdNow(oldId); destroyBufferIdNow(oldId, "destroyOldAndCreate");
} }
@@ -149,6 +179,8 @@ public class GLBuffer implements AutoCloseable
PhantomReference<GLBuffer> phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE); PhantomReference<GLBuffer> phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE);
PHANTOM_TO_BUFFER_ID.put(phantom, this.id); PHANTOM_TO_BUFFER_ID.put(phantom, this.id);
BUFFER_ID_TO_PHANTOM.put(this.id, phantom); BUFFER_ID_TO_PHANTOM.put(this.id, phantom);
this.updateAllocationStackTrace();
} }
finally finally
{ {
@@ -181,7 +213,8 @@ public class GLBuffer implements AutoCloseable
this.id = 0; this.id = 0;
this.size = 0; this.size = 0;
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete); }); //LOGGER.info("async destroy [" + idToDelete + "].");
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete, "destroyAsync"); });
} }
finally finally
{ {
@@ -189,49 +222,43 @@ public class GLBuffer implements AutoCloseable
} }
} }
private static void destroyBufferIdNow(int id) private static void destroyBufferIdNow(int id, String cause)
{ {
// only delete valid buffers // only delete valid buffers
if (id == 0) if (id == 0)
{ {
LOGGER.warn("Attempted to destroy a buffer with ID 0, VRAM memory leaks may occur."); LOGGER.warn("Attempted to destroy a buffer with ID 0, VRAM memory leaks may occur, cause: ["+cause+"].");
return; return;
} }
bufferCount.decrementAndGet(); bufferCount.decrementAndGet();
GLMC.glDeleteBuffers(id);
// destroy the buffer if it exists, if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
// the buffer may not exist if the destroy method is called twice
if (GL32.glIsBuffer(id))
{ {
GLMC.glDeleteBuffers(id); LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "], cause: ["+cause+"].");
if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
{
LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]");
}
}
else
{
// shouldn't happen, but just in case
LOGGER.warn("Attempted to destroy a non buffer object with ID ["+id+"].");
} }
} }
/** should be called before {@link GLBuffer#destroyBufferIdNow} */ /** should be called before {@link GLBuffer#destroyBufferIdNow} */
private static void tryRemoveBufferIdFromPhantom(int id) private static void tryRemoveBufferIdFromPhantom(int id)
{ {
if (BUFFER_ID_TO_PHANTOM.containsKey(id)) // will contain nothing if stack tracking isn't enabled
BUFFER_ID_TO_ALLOCATION_STRING.remove(id);
PhantomReference<? extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.remove(id);
if (phantom != null)
{ {
Reference<? extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.get(id);
// if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again // if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again
// this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA // this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA
// to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it // to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it
phantom.clear(); phantom.clear();
PHANTOM_TO_BUFFER_ID.remove(phantom); Integer phantomId = PHANTOM_TO_BUFFER_ID.remove(phantom);
BUFFER_ID_TO_PHANTOM.remove(id); if (phantomId == null)
{
LOGGER.warn("No Phantom->ID binding stored for ID ["+id+"]");
}
} }
else else
{ {
@@ -322,8 +349,25 @@ public class GLBuffer implements AutoCloseable
LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use bufferData upload method!"); LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use bufferData upload method!");
int bbSize = bb.limit() - bb.position(); int bbSize = bb.limit() - bb.position();
GL32.glBufferData(this.getBufferBindingTarget(), bb, bufferDataHint); int target = this.getBufferBindingTarget();
if (shouldUploadToGpuInChunks(bbSize))
{
// Two-step path used on macOS to dodge the Apple OpenGL -> Metal
// memmove SIGBUS triggered by uploading a large ByteBuffer in one
// glBufferData call:
// 1) allocate-only with the size overload (no memcpy)
// 2) fill the buffer through chunked glBufferSubData calls
GL32.glBufferData(target, (long) bbSize, bufferDataHint);
subDataUploadInChunks(target, 0, bb, MAC_UPLOAD_CHUNK_BYTES);
}
else
{
GL32.glBufferData(target, bb, bufferDataHint);
}
this.size = bbSize; this.size = bbSize;
this.updateAllocationStackTrace();
} }
/** Requires the buffer to be bound */ /** Requires the buffer to be bound */
protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint) protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint)
@@ -331,14 +375,30 @@ public class GLBuffer implements AutoCloseable
LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use subData upload method!"); LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use subData upload method!");
int bbSize = bb.limit() - bb.position(); int bbSize = bb.limit() - bb.position();
int target = this.getBufferBindingTarget();
if (this.size < bbSize || this.size > bbSize * BUFFER_SHRINK_TRIGGER) if (this.size < bbSize || this.size > bbSize * BUFFER_SHRINK_TRIGGER)
{ {
int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER); int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER);
if (newSize > maxExpansionSize) newSize = maxExpansionSize; if (newSize > maxExpansionSize)
GL32.glBufferData(this.getBufferBindingTarget(), newSize, bufferDataHint); {
newSize = maxExpansionSize;
}
// allocate-only — no memcpy, safe on macOS regardless of size
GL32.glBufferData(target, (long) newSize, bufferDataHint);
this.size = newSize; this.size = newSize;
} }
GL32.glBufferSubData(this.getBufferBindingTarget(), 0, bb);
if (shouldUploadToGpuInChunks(bbSize))
{
subDataUploadInChunks(target, 0, bb, MAC_UPLOAD_CHUNK_BYTES);
}
else
{
GL32.glBufferSubData(target, 0, bb);
}
this.updateAllocationStackTrace();
} }
//endregion //endregion
@@ -396,6 +456,91 @@ public class GLBuffer implements AutoCloseable
} }
} }
/**
* macOS-only mitigation for the SIGBUS in
* {@code libsystem_platform.dylib _platform_memmove} that happens when the
* Apple OpenGL -> Metal translation layer copies a single large ByteBuffer
* out of LWJGL into driver memory. Splitting the copy into
* {@link #MAC_UPLOAD_CHUNK_BYTES} slices keeps every memmove inside a size
* the bridge handles reliably.
*/
private static boolean shouldUploadToGpuInChunks(int byteCount)
{
return EPlatform.get() == EPlatform.MACOS
&& byteCount > MAC_UPLOAD_CHUNK_THRESHOLD;
}
/**
* Uploads {@code bb} into the currently bound buffer at {@code baseOffset}
* using a sequence of {@link GL32#glBufferSubData(int, long, ByteBuffer)}
* calls of at most {@code chunkBytes} each. The buffer's position/limit are
* restored before returning.
*/
private static void subDataUploadInChunks(int target, int baseOffset, ByteBuffer bb, int chunkBytes)
{
final int origPos = bb.position();
final int origLimit = bb.limit();
try
{
final int total = origLimit - origPos;
int uploaded = 0;
while (uploaded < total)
{
int chunk = Math.min(chunkBytes, total - uploaded);
bb.position(origPos + uploaded);
bb.limit(origPos + uploaded + chunk);
GL32.glBufferSubData(target, (long) (baseOffset + uploaded), bb);
uploaded += chunk;
// Force the driver to drain its command queue between chunks
// so the OpenGL -> Metal bridge processes (and frees) each
// staging copy before the next sub-data call piles another
// memmove on top of it.
if (uploaded < total)
{
GL32.glFlush();
}
}
}
finally
{
bb.limit(origLimit);
bb.position(origPos);
}
}
/**
* used to help track down leaks where the buffer isn't properly closed
* Note: this probably needs extending to accept a stack trace from outside where it's being called
* since it's often called on the render thread in an un-helpful location.
*/
public void updateAllocationStackTrace()
{
if (LOG_PHANTOM_ALLOCATION_STACKS)
{
String stack;
RenderThreadTaskHandler.QueuedRunnable parentQueuedRunnable;
// if this is running on the render thread, try getting the render task's stack trace instead
// since it's a lot more helpful than wherever the render thread tasks themselves are being run from
if (RenderThreadTaskHandler.INSTANCE.isCurrentThread()
&& (parentQueuedRunnable = RenderThreadTaskHandler.INSTANCE.getCurrentlyRunningTask()) != null
&& parentQueuedRunnable.stackTrace != null)
{
// trim off the getStacktrace() and queueRunningOnRenderThread() methods
StackTraceElement[] trimmedElements = Arrays.copyOfRange(parentQueuedRunnable.stackTrace, 2, parentQueuedRunnable.stackTrace.length);
stack = StringUtil.join("\n", trimmedElements).intern();
}
else
{
// not running on the render thread, use the normal stack trace
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
stack = StringUtil.join("\n", stackTraceElements).intern();
}
BUFFER_ID_TO_ALLOCATION_STRING.put(this.id, stack);
}
}
//endregion //endregion
@@ -407,8 +552,13 @@ public class GLBuffer implements AutoCloseable
private static void runPhantomReferenceCleanupLoop() private static void runPhantomReferenceCleanupLoop()
{ {
// these arrays are stored here so they don't have to be re-allocated each loop
ArrayList<Pair<String, AtomicInteger>> allocationStackTraceCountPairList = new ArrayList<>();
while (true) while (true)
{ {
allocationStackTraceCountPairList.clear();
try try
{ {
try try
@@ -417,20 +567,53 @@ public class GLBuffer implements AutoCloseable
} }
catch (InterruptedException ignore) { } catch (InterruptedException ignore) { }
int collectedCount = 0;
Reference<? extends GLBuffer> phantomRef = PHANTOM_REFERENCE_QUEUE.poll(); Reference<? extends GLBuffer> phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
while (phantomRef != null) while (phantomRef != null)
{ {
// destroy the buffer if it hasn't been cleared yet // destroy the buffer if it hasn't been cleared yet
if (PHANTOM_TO_BUFFER_ID.containsKey(phantomRef)) Integer idRef = PHANTOM_TO_BUFFER_ID.remove((PhantomReference<? extends GLBuffer>)phantomRef); // cast to make IntelliJ happy
if (idRef != null)
{ {
int id = PHANTOM_TO_BUFFER_ID.get(phantomRef); BUFFER_ID_TO_PHANTOM.remove(idRef);
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer phantom destroy", () -> { destroyBufferIdNow(id); }); final int id = idRef;
//LOGGER.warn("Buffer Phantom collected, ID: ["+id+"]"); RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer phantom destroy", () -> { destroyBufferIdNow(id, "runPhantomReferenceCleanupLoop"); });
//LOGGER.info("Buffer Phantom collected, ID: ["+id+"]");
if (LOG_PHANTOM_ALLOCATION_STACKS) // stack trace shouldn't be null, but just in case
{
String stack = BUFFER_ID_TO_ALLOCATION_STRING.get(idRef);
PhantomLoggingHelper.putAndIncrementTrackingString(stack, allocationStackTraceCountPairList);
}
}
else
{
LOGGER.warn("Failed to find Buffer ID for phantom reference: ["+phantomRef+"]");
} }
collectedCount++;
phantomRef = PHANTOM_REFERENCE_QUEUE.poll(); phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
} }
if (LOG_PHANTOM_RECOVERY)
{
// we only want to log when something has been returned
if (collectedCount != 0)
{
LOGGER.warn("GLBuffer phantom recovered: ["+ F3Screen.NUMBER_FORMAT.format(collectedCount)+"].");
// log stack traces if present
if (LOG_PHANTOM_ALLOCATION_STACKS)
{
PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, allocationStackTraceCountPairList);
}
}
}
} }
catch (Exception e) catch (Exception e)
{ {
@@ -37,10 +37,11 @@ public class GLIndexBuffer extends GLBuffer
protected int glType = GL32.GL_UNSIGNED_INT; protected int glType = GL32.GL_UNSIGNED_INT;
public int getGlType() { return this.glType; } public int getGlType() { return this.glType; }
public GLIndexBuffer(boolean isBufferStorage)
{
super(isBufferStorage); public GLIndexBuffer(boolean isBufferStorage) { super(isBufferStorage); }
}
@Override @Override
public void destroyAsync() public void destroyAsync()
@@ -167,20 +167,46 @@ public class BlockStateWrapper implements IBlockStateWrapper
return AIR; return AIR;
} }
// create a wrapper specifically for the API event to use // pooling wrappers significantly improves chunk->LOD processing speed
BlockStateWrapper apiWrapper = new BlockStateWrapper(blockState, levelWrapper, null); // and also reduces GC pressure
DhApiBlockStateWrapperCreatedEvent.EventParam eventParam = new DhApiBlockStateWrapperCreatedEvent.EventParam(apiWrapper); BlockStateWrapper existingWrapper = WRAPPER_BY_BLOCK_STATE.get(blockState);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBlockStateWrapperCreatedEvent.class, eventParam); if (existingWrapper != null)
if (!eventParam.getOverridesSet())
{ {
// no changes needed, use the existing object return existingWrapper;
return apiWrapper;
} }
// create a new wrapper using whatever overrides the API user set
BlockStateWrapper returnWrapper = new BlockStateWrapper(blockState, levelWrapper, eventParam);
return returnWrapper; // synchronized so the API event only fires once per block
synchronized (WRAPPER_BY_BLOCK_STATE)
{
// if another thread already finished this block, use that wrapper
existingWrapper = WRAPPER_BY_BLOCK_STATE.get(blockState);
if (existingWrapper != null)
{
return existingWrapper;
}
// create a wrapper specifically for the API event to use
BlockStateWrapper apiWrapper = new BlockStateWrapper(blockState, levelWrapper, null);
DhApiBlockStateWrapperCreatedEvent.EventParam eventParam = new DhApiBlockStateWrapperCreatedEvent.EventParam(apiWrapper);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBlockStateWrapperCreatedEvent.class, eventParam);
if (!eventParam.getOverridesSet())
{
// no API changes needed, use the existing object
WRAPPER_BY_BLOCK_STATE.putIfAbsent(blockState, apiWrapper);
return apiWrapper;
}
else
{
// create a new wrapper using whatever overrides the API user set
BlockStateWrapper returnWrapper = new BlockStateWrapper(blockState, levelWrapper, eventParam);
WRAPPER_BY_BLOCK_STATE.putIfAbsent(blockState, returnWrapper);
return returnWrapper;
}
}
} }
private BlockStateWrapper( private BlockStateWrapper(
@Nullable BlockState blockState, ILevelWrapper levelWrapper, @Nullable BlockState blockState, ILevelWrapper levelWrapper,
@@ -1000,6 +1026,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
// put if absent in case two threads deserialize at the same time // put if absent in case two threads deserialize at the same time
// unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions // unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper); WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
if (foundWrapper != AIR)
{
WRAPPER_BY_BLOCK_STATE.putIfAbsent(foundWrapper.blockState, foundWrapper);
}
} }
} }
@@ -260,39 +260,55 @@ public class ClientBlockStateColorCache
&& !quads.isEmpty() && !quads.isEmpty()
&& quads.get(0) != null) && quads.get(0) != null)
{ {
BakedQuad firstQuad = quads.get(0); try
{
BakedQuad firstQuad = quads.get(0);
#if MC_VER <= MC_1_21_11 #if MC_VER <= MC_1_21_11
this.needPostTinting = firstQuad.isTinted(); this.needPostTinting = firstQuad.isTinted();
#else #else
this.needPostTinting = firstQuad.materialInfo().isTinted(); this.needPostTinting = firstQuad.materialInfo().isTinted();
#endif #endif
#if MC_VER <= MC_1_21_4 #if MC_VER <= MC_1_21_4
this.tintIndex = firstQuad.getTintIndex(); this.tintIndex = firstQuad.getTintIndex();
#elif MC_VER <= MC_1_21_11 #elif MC_VER <= MC_1_21_11
this.tintIndex = firstQuad.tintIndex(); this.tintIndex = firstQuad.tintIndex();
#else #else
this.tintIndex = firstQuad.materialInfo().tintIndex(); this.tintIndex = firstQuad.materialInfo().tintIndex();
#endif #endif
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.sprite, firstQuad.sprite,
EColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#elif MC_VER < MC_1_21_5 #elif MC_VER < MC_1_21_5
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.getSprite(), firstQuad.getSprite(),
EColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#elif MC_VER <= MC_1_21_11 #elif MC_VER <= MC_1_21_11
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.sprite(), firstQuad.sprite(),
EColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#else #else
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.materialInfo().sprite(), firstQuad.materialInfo().sprite(),
EColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#endif #endif
}
catch (Exception e)
{
// Shouldn't normally happen, but there was at least
// one report of MC's texture being un-loaded
// which prevented us from getting the texture.
// So we should have some basic backup logic.
LOGGER.warn("Failed to get texture color for block ["+this.blockStateWrapper.getSerialString()+"] due to: ["+e.getMessage()+"], falling back to particle color.");
this.needPostTinting = false;
this.tintIndex = 0;
this.baseColor = this.getParticleIconColor();
}
} }
else else
{ {
@@ -399,22 +415,14 @@ public class ClientBlockStateColorCache
int scale = 1; int scale = 1;
if (colorMode == EColorMode.Leaves) if (colorMode == EColorMode.Leaves)
{ {
//switch (//FIXME add config option) if (a == 0)
// case BLACK: {
// a = 255; //simulate black background of fast leaves continue; //same long grass
// break; }
// case IGNORE: else
if (a == 0) { {
continue; //same long grass a = 255; //just in case there are semi transparent pixels
} }
else
{
a = 255; //just in case there are semi transparent pixels
}
// break;
// case TRANSPARENT:
// break; //do nothing, let it count towards transparency
} }
else if (a == 0 && colorMode != EColorMode.Glass) else if (a == 0 && colorMode != EColorMode.Glass)
{ {
-31
View File
@@ -1,31 +0,0 @@
#!/bin/sh
publish_version()
{
if [[ "$2" == "all" || "$1" == "$2" ]]
then
docker run --name=dh-build-$1 --rm -v /${PWD}:/home/build -e MC_VER=$1 dh-eclipse-temurin
cp ./fabric/build/libs/*$1.jar ./buildAllJars/fabric/
cp ./forge/build/libs/*$1.jar ./buildAllJars/forge/
cp ./Merged/*.jar ./buildAllJars/merged/
fi
}
if [ -z "$1" ]
then
echo "Build target is undefined! [all] [1.20.1] [1.19.4] [1.19.2] [1.18.2] [1.17.1] [1.16.5]"
exit 1
fi
docker build --tag=dh-eclipse-temurin -q .
mkdir -p buildAllJars/fabric
mkdir -p buildAllJars/forge
mkdir -p buildAllJars/merged
publish_version 1.20.1 $1
publish_version 1.19.4 $1
publish_version 1.19.2 $1
publish_version 1.18.2 $1
publish_version 1.17.1 $1
publish_version 1.16.5 $1
@@ -1,6 +1,8 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockStateWrapperCreatedEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent;
import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister; import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
@@ -16,7 +18,9 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.fabric.testing.TestBlockWrapperCreatedEvent;
import com.seibel.distanthorizons.fabric.testing.TestChunkInputReplacerEvent; import com.seibel.distanthorizons.fabric.testing.TestChunkInputReplacerEvent;
import com.seibel.distanthorizons.fabric.testing.TestCustomColorEvent;
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent; import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
@@ -87,11 +91,21 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
/* Register the mod needed event callbacks */ /* Register the mod needed event callbacks */
// can be enabled to test overrides/events without having to build a separate API project // can be enabled to test overrides/events without having to build a separate API project
if (false)
{ {
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent()); // test custom world gen
DhApi.events.bind(DhApiChunkProcessingEvent.class, new TestChunkInputReplacerEvent()); if (false)
{
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent());
DhApi.events.bind(DhApiChunkProcessingEvent.class, new TestChunkInputReplacerEvent());
}
// test custom colors
if (false)
{
DhApi.events.bind(DhApiBlockColorOverrideEvent.class, new TestCustomColorEvent());
DhApi.events.bind(DhApiBlockStateWrapperCreatedEvent.class, new TestBlockWrapperCreatedEvent());
}
} }
@@ -19,19 +19,13 @@
package com.seibel.distanthorizons.fabric.mixins.client; package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.common.commonMixins.MixinVanillaFogCommon;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
@@ -42,7 +36,6 @@ import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_3 #elif MC_VER < MC_1_21_3
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode; import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
@@ -96,11 +89,11 @@ public class MixinFogRenderer
#endif #endif
{ {
#if MC_VER < MC_1_21_6 #if MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera, fogMode); boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera, fogMode);
#elif MC_VER < MC_1_21_6 #elif MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera); boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera);
#else #else
boolean cancelFog = cancelFog(); boolean cancelFog = MixinVanillaFogCommon.cancelFog();
#endif #endif
if (cancelFog) if (cancelFog)
@@ -142,7 +135,7 @@ public class MixinFogRenderer
) )
private void onSetRenderDistanceEnd(FogData instance, float value, Operation<Void> original) private void onSetRenderDistanceEnd(FogData instance, float value, Operation<Void> original)
{ {
if (cancelFog()) if (MixinVanillaFogCommon.cancelFog())
{ {
instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE; instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE;
instance.environmentalEnd = A_EVEN_LARGER_VALUE; instance.environmentalEnd = A_EVEN_LARGER_VALUE;
@@ -164,52 +157,5 @@ public class MixinFogRenderer
#endif #endif
@Unique
#if MC_VER < MC_1_21_6
private static boolean cancelFog(Camera camera, FogMode fogMode)
#else
private static boolean cancelFog()
#endif
{
#if MC_VER < MC_1_21_6
Entity entity = camera.getEntity();
#elif MC_VER <= MC_1_21_10
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.getEntity();
#else
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.entity();
#endif
boolean cameraNotInFluid = cameraNotInFluid(camera);
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
boolean cancelFog = !isSpecialFog;
cancelFog = cancelFog && cameraNotInFluid;
#if MC_VER < MC_1_21_6
cancelFog = cancelFog && (fogMode == FogMode.FOG_TERRAIN);
#endif
cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial();
cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get();
return cancelFog;
}
@Unique
private static boolean cameraNotInFluid(Camera camera)
{
#if MC_VER < MC_1_17_1
FluidState fluidState = camera.getFluidInCamera();
boolean cameraNotInFluid = fluidState.isEmpty();
#else
FogType fogTypes = camera.getFluidInCamera();
boolean cameraNotInFluid = fogTypes == FogType.NONE;
#endif
return cameraNotInFluid;
}
} }
@@ -0,0 +1,43 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockStateWrapperCreatedEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import java.util.Random;
/**
* @see TestCustomColorEvent
*/
public class TestBlockWrapperCreatedEvent extends DhApiBlockStateWrapperCreatedEvent
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Override
public void blockStateWrapperCreated(DhApiEventParam<EventParam> event)
{
EventParam eventParam = event.value;
// can be enabled to flip the opacity of transparent/opaque blocks
if (false)
{
if (eventParam.getBlockStateWrapper().getOpacity() == LodUtil.BLOCK_FULLY_OPAQUE)
{
eventParam.setOpacity(LodUtil.BLOCK_FULLY_TRANSPARENT);
}
else
{
eventParam.setOpacity(LodUtil.BLOCK_FULLY_OPAQUE);
}
}
// needed for TestCustomColorEvent
eventParam.setAllowApiColorOverride(true);
}
}
@@ -0,0 +1,86 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import java.awt.*;
/**
* @see TestBlockWrapperCreatedEvent
*/
public class TestCustomColorEvent extends DhApiBlockColorOverrideEvent
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Override
public void blockStateWrapperCreated(DhApiEventParam<EventParam> event)
{
EventParam eventParam = event.value;
//randomDatapointColors(eventParam);
//randomPerBlockColors(eventParam);
//blackWhitePositionStripe(eventParam);
positionRainbow(eventParam);
}
/** each datapoint has a random color */
private void randomDatapointColors(EventParam eventParam)
{
// random colors for each datapoint
int a = eventParam.getAlpha();
int r = eventParam.getRed();
int g = eventParam.getGreen();
int b = eventParam.getBlue();
if (eventParam.getBlockStateWrapper().getOpacity() == LodUtil.BLOCK_FULLY_OPAQUE)
{
eventParam.setColor(255,r,g,b);
}
else
{
eventParam.setColor(60,r,g,b);
}
}
/** each block has a different color */
private void randomPerBlockColors(EventParam eventParam)
{
// random colors per block
int r = Math.abs(eventParam.getBlockStateWrapper().hashCode() % 255);
int g = Math.abs((eventParam.getBlockStateWrapper().hashCode() << 4) % 255);
int b = Math.abs((eventParam.getBlockStateWrapper().hashCode() << 8) % 255);
eventParam.setColor(r,g,b);
}
private void blackWhitePositionStripe(EventParam eventParam)
{
// black-white stripes
int r = Math.abs(eventParam.getBlockPosX() % 255);
int g = r;
int b = r;
eventParam.setColor(r,g,b);
}
/** rainbow along the X axis repeating every 255 blocks */
private void positionRainbow(EventParam eventParam)
{
float[] ahsv = ColorUtil.argbToAhsv(ColorUtil.RED);
float a = ahsv[0];
int xModPos = Math.abs(eventParam.getBlockPosX() % 510);
float h = xModPos < 255 ? xModPos : 510 - xModPos;
float s = ahsv[2];
float v = ahsv[3];
int colorInt = ColorUtil.ahsvToArgb(a,h,s,v);
eventParam.setColor(ColorUtil.getRed(colorInt),ColorUtil.getGreen(colorInt),ColorUtil.getBlue(colorInt));
}
}
+5 -2
View File
@@ -18,10 +18,13 @@ def addMod(path, enabled) {
dependencies { dependencies {
// TerraForged // TerraForged
addMod("curse.maven:TerraForged-363820:${rootProject.terraforged_version}", rootProject.enable_terraforged) addMod("curse.maven:TerraForged-363820:${rootProject.terraforged_version}", rootProject.enable_terraforged)
// TerraFirmaCraft // TerraFirmaCraft
addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft)
// Oculus (Iris port)
addMod("maven.modrinth:oculus:${rootProject.oculus_version}", rootProject.enable_oculus)
// TODO: Check if this is still needed and if so ensure this code works for MC 26.1+ // TODO: Check if this is still needed and if so ensure this code works for MC 26.1+
// (potential) hack fix for MC 1.20.6 and later, force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+ // (potential) hack fix for MC 1.20.6 and later, force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+
def mcParts = rootProject.minecraft_version.split("\\.") def mcParts = rootProject.minecraft_version.split("\\.")
@@ -19,10 +19,8 @@
package com.seibel.distanthorizons.forge.mixins.client; package com.seibel.distanthorizons.forge.mixins.client;
import com.seibel.distanthorizons.common.commonMixins.MixinVanillaFogCommon;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -33,14 +31,6 @@ import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode; import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState;
#else
import net.minecraft.world.level.material.FogType;
#endif
@Mixin(FogRenderer.class) @Mixin(FogRenderer.class)
public class MixinFogRenderer public class MixinFogRenderer
@@ -55,29 +45,25 @@ public class MixinFogRenderer
remap = #if MC_VER == MC_1_17_1 || MC_VER == MC_1_18_2 false #else true #endif ) // Remap messiness due to this being weird in forge remap = #if MC_VER == MC_1_17_1 || MC_VER == MC_1_18_2 false #else true #endif ) // Remap messiness due to this being weird in forge
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float partTick, CallbackInfo callback) private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float partTick, CallbackInfo callback)
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_21_6
FluidState fluidState = camera.getFluidInCamera(); boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera, fogMode);
boolean cameraNotInFluid = fluidState.isEmpty(); #elif MC_VER < MC_1_21_6
boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera);
#else #else
FogType fogTypes = camera.getFluidInCamera(); boolean cancelFog = MixinVanillaFogCommon.cancelFog();
boolean cameraNotInFluid = fogTypes == FogType.NONE;
#endif #endif
if (cancelFog)
Entity entity = camera.getEntity();
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
if (!isSpecialFog
&& cameraNotInFluid
&& fogMode == FogMode.FOG_TERRAIN
&& !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial()
&& !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get())
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE); RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.fogEnd(A_EVEN_LARGER_VALUE); RenderSystem.fogEnd(A_EVEN_LARGER_VALUE);
#else #elif MC_VER < MC_1_21_3
RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE); RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE); RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE);
#elif MC_VER < MC_1_21_6
callback.setReturnValue(FogParameters.NO_FOG);
#else
#endif #endif
ClientApi.RENDER_STATE.vanillaFogEnabled = false; ClientApi.RENDER_STATE.vanillaFogEnabled = false;
@@ -86,6 +72,7 @@ public class MixinFogRenderer
{ {
ClientApi.RENDER_STATE.vanillaFogEnabled = true; ClientApi.RENDER_STATE.vanillaFogEnabled = true;
} }
} }
} }
@@ -23,35 +23,50 @@ import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
#if MC_VER == MC_1_20_1
import net.irisshaders.iris.Iris;
import net.irisshaders.iris.api.v0.IrisApi;
#else
#endif
public class OculusAccessor implements IIrisAccessor public class OculusAccessor implements IIrisAccessor
{ {
protected static final DhLogger LOGGER = new DhLoggerBuilder().build(); protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
public OculusAccessor() public OculusAccessor()
{ { LOGGER.warn("Partial Oculus support enabled. Some DH features may be disabled or behave strangely, use Iris instead if possible."); }
LOGGER.warn("Partial Oculus support enabled. Some DH features may be disabled or behave strangely, use Iris instead if possible.");
}
@Override @Override
public String getModName() public String getModName()
{ {
return "oculus"; #if MC_VER == MC_1_20_1
return Iris.MODID;
#else
return "iris"; // Oculus doesn't support this MC version
#endif
} }
@Override @Override
public boolean isShaderPackInUse() public boolean isShaderPackInUse()
{ {
// assume shaders are always active #if MC_VER == MC_1_20_1
return true; return IrisApi.getInstance().isShaderPackInUse();
#else
return true; // Oculus doesn't support this MC version
#endif
} }
@Override @Override
public boolean isRenderingShadowPass() public boolean isRenderingShadowPass()
{ {
return false; #if MC_VER == MC_1_20_1
return IrisApi.getInstance().isRenderingShadowPass();
#else
return false; // Oculus doesn't support this MC version
#endif
} }
} }
+5 -2
View File
@@ -6,8 +6,8 @@ org.gradle.caching=true
# Mod Info # Mod Info
mod_name=DistantHorizons mod_name=DistantHorizons
api_name=DistantHorizonsApi api_name=DistantHorizonsApi
mod_version=3.0.2-b mod_version=3.0.3-b
api_version=6.1.0 api_version=6.1.1
maven_group=com.seibel.distanthorizons maven_group=com.seibel.distanthorizons
mod_readable_name=Distant Horizons mod_readable_name=Distant Horizons
mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow. mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow.
@@ -51,3 +51,6 @@ mcVer=26.1.2
# Defines the maximum amount of memory Minecraft is allowed when run in a development environment # Defines the maximum amount of memory Minecraft is allowed when run in a development environment
minecraftMemoryJavaArg=-Xmx6G minecraftMemoryJavaArg=-Xmx6G
# can be enabled for use with the Tracy profiler, disabled by default since it causes constant memory growth when running
minecraftEnableTracy=false
@@ -19,11 +19,8 @@
package com.seibel.distanthorizons.neoforge.mixins.client; package com.seibel.distanthorizons.neoforge.mixins.client;
import com.seibel.distanthorizons.common.commonMixins.MixinVanillaFogCommon;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -96,11 +93,11 @@ public class MixinFogRenderer
#endif #endif
{ {
#if MC_VER < MC_1_21_6 #if MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera, fogMode); boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera, fogMode);
#elif MC_VER < MC_1_21_6 #elif MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera); boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera);
#else #else
boolean cancelFog = cancelFog(); boolean cancelFog = MixinVanillaFogCommon.cancelFog();
#endif #endif
if (cancelFog) if (cancelFog)
@@ -142,7 +139,7 @@ public class MixinFogRenderer
) )
private void onSetRenderDistanceEnd(FogData instance, float value, Operation<Void> original) private void onSetRenderDistanceEnd(FogData instance, float value, Operation<Void> original)
{ {
if (cancelFog()) if (MixinVanillaFogCommon.cancelFog())
{ {
instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE; instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE;
instance.environmentalEnd = A_EVEN_LARGER_VALUE; instance.environmentalEnd = A_EVEN_LARGER_VALUE;
@@ -164,52 +161,5 @@ public class MixinFogRenderer
#endif #endif
@Unique
#if MC_VER < MC_1_21_6
private static boolean cancelFog(Camera camera, FogMode fogMode)
#else
private static boolean cancelFog()
#endif
{
#if MC_VER < MC_1_21_6
Entity entity = camera.getEntity();
#elif MC_VER <= MC_1_21_10
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.getEntity();
#else
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.entity();
#endif
boolean cameraNotInFluid = cameraNotInFluid(camera);
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
boolean cancelFog = !isSpecialFog;
cancelFog = cancelFog && cameraNotInFluid;
#if MC_VER < MC_1_21_6
cancelFog = cancelFog && (fogMode == FogMode.FOG_TERRAIN);
#endif
cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial();
cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get();
return cancelFog;
}
@Unique
private static boolean cameraNotInFluid(Camera camera)
{
#if MC_VER < MC_1_17_1
FluidState fluidState = camera.getFluidInCamera();
boolean cameraNotInFluid = fluidState.isEmpty();
#else
FogType fogTypes = camera.getFluidInCamera();
boolean cameraNotInFluid = fogTypes == FogType.NONE;
#endif
return cameraNotInFluid;
}
} }
+2
View File
@@ -54,6 +54,7 @@ forge_version=36.2.39
# Forge mod versions # Forge mod versions
terraforged_version=4044290 terraforged_version=4044290
oculus_version=
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -62,3 +63,4 @@ forge_version=36.2.39
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=1 enable_terraforged=1
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=0
+2
View File
@@ -53,6 +53,7 @@ forge_version=37.1.1
# Forge mod versions # Forge mod versions
terraforged_version= terraforged_version=
oculus_version=
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -61,3 +62,4 @@ forge_version=37.1.1
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=0 enable_terraforged=0
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=0
+3 -1
View File
@@ -62,6 +62,7 @@ forge_version=40.2.10
# Forge mod versions # Forge mod versions
terraforged_version= terraforged_version=
oculus_version=
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -69,4 +70,5 @@ forge_version=40.2.10
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=0 enable_terraforged=0
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=0
+2
View File
@@ -52,6 +52,7 @@ forge_version=43.3.2
# Forge mod versions # Forge mod versions
terraforged_version= terraforged_version=
oculus_version=
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -60,3 +61,4 @@ forge_version=43.3.2
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=0 enable_terraforged=0
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=0
+2
View File
@@ -51,6 +51,7 @@ forge_version=45.2.4
# Forge mod versions # Forge mod versions
terraforged_version= terraforged_version=
oculus_version=
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -59,3 +60,4 @@ forge_version=45.2.4
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=0 enable_terraforged=0
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=0
+2
View File
@@ -51,6 +51,7 @@ forge_version=47.2.1
# Forge mod versions # Forge mod versions
terraforged_version= terraforged_version=
oculus_version=1.20.1-1.8.0
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -59,3 +60,4 @@ forge_version=47.2.1
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=0 enable_terraforged=0
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=1
+2
View File
@@ -51,6 +51,7 @@ forge_version=48.0.13
# Forge mod versions # Forge mod versions
terraforged_version= terraforged_version=
oculus_version=
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -59,3 +60,4 @@ forge_version=48.0.13
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=0 enable_terraforged=0
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=0
+2
View File
@@ -52,6 +52,7 @@ neoforge_version=
# Forge mod versions # Forge mod versions
terraforged_version= terraforged_version=
oculus_version=
# Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
@@ -60,3 +61,4 @@ neoforge_version=
enable_starlight_forge=0 enable_starlight_forge=0
enable_terraforged=0 enable_terraforged=0
enable_terrafirmacraft=0 enable_terrafirmacraft=0
enable_oculus=0
+1 -1
View File
@@ -15,7 +15,7 @@ lwjgl_version=3.3.3
# Fabric loader # Fabric loader
fabric_loader_version=0.16.9 fabric_loader_version=0.16.9
fabric_api_version=0.115.0+1.21.1 fabric_api_version=0.116.11+1.21.1
# Fabric mod versions # Fabric mod versions
modmenu_version=11.0.0-beta.1 modmenu_version=11.0.0-beta.1
starlight_version_fabric= starlight_version_fabric=