Compare commits

...

65 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
James Seibel 91f9ef3f4b remove dev from the version number 2026-04-24 06:51:09 -05:00
James Seibel d52a3abb14 Add a rough build all parallel batch script 2026-04-23 20:38:51 -05:00
James Seibel 16370b0b6e ignore parallel build folders 2026-04-23 20:38:23 -05:00
James Seibel bfa60b48cf Fix iris transparent blending 2026-04-23 17:54:41 -05:00
James Seibel 50518bfe21 Fix "fog" rendering when underwater with Iris 2026-04-23 17:39:46 -05:00
James Seibel 80e4467829 Fix near clip plane to close with shaders 2026-04-23 17:09:27 -05:00
James Seibel 396315bd05 Fix GC rarely deleting in use GL buffers 2026-04-23 16:57:17 -05:00
James Seibel 7a0fec2c2f Maybe fix a buffer deletion issue? 2026-04-23 07:44:33 -05:00
James Seibel 4afaaa7b12 up api version 6.0.0 -> 6.1.0 2026-04-23 07:42:31 -05:00
James Seibel b057041467 Add more locking and volatile buffer ID checks 2026-04-23 07:16:31 -05:00
James Seibel 33e6ce6376 potential nvidia null VBO pointer crash fix 2026-04-22 22:33:17 -05:00
James Seibel 118ef39c30 Fix flashing when moving over root node boundaries 2026-04-22 18:36:13 -05:00
James Seibel 1013e1c824 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2026-04-22 17:15:09 -05:00
James Seibel b0e924c7fe Fix nvidia driver crash 2026-04-22 17:14:50 -05:00
James Seibel 1777acd1d4 remove unused var in ThreadWorldGenParams 2026-04-22 16:54:41 -05:00
s809 8276a862f8 Clean up received payload buffer check a bit 2026-04-23 00:26:37 +05:00
James Seibel 4329acf91d Try fixing rare null pointer in render 2026-04-22 07:51:11 -05:00
James Seibel 72f83b40f7 Fix quad tree unit tests 2026-04-22 07:41:56 -05:00
James Seibel a33eb30a53 fix rare race condition preventing world gen 2026-04-21 22:20:14 -05:00
James Seibel d3e96f50a8 Maybe fix native GL crash due to buffer free 2 2026-04-21 21:40:33 -05:00
James Seibel 3aefeb98b4 Maybe fix native GL crash due to buffer free 2026-04-21 21:37:14 -05:00
James Seibel 3553ff8e60 handle a weird double start issue 2026-04-21 21:25:44 -05:00
James Seibel 945a2c0c5a Fix garbage collector warning not using config 2026-04-21 19:59:23 -05:00
James Seibel 8c7974e216 Improve node out-of-bound logic
This fixes some overlapping rendering issues, fixes LOD generating outside of render distance, and fixes low-detail LODs flashing when moving into previously-explored LODs
2026-04-21 19:50:30 -05:00
James Seibel 37756cd759 Try fixing LOD flashing/stuck low details 2026-04-21 07:48:46 -05:00
James Seibel c60cc4f013 Merge branch 'main' into 'main'
Remove broken TerraFirmaCraft compatibility code

See merge request distant-horizons-team/distant-horizons!86
2026-04-21 11:59:12 +00:00
James Seibel 87cce2e33c Fix LODs loading outside render distance
Fixes !1233
2026-04-19 21:48:33 -05:00
James Seibel 40ada9c186 fix debug wireframe on blaze3D 2026-04-19 16:34:44 -05:00
James Seibel 55fb458266 Maybe reduce memory when using internal world gen 2026-04-19 16:29:27 -05:00
James Seibel 79d2466fa2 Revert "Auto-change rendering backend when Iris is present"
This reverts commit d750e489df.
2026-04-19 15:09:16 -05:00
s809 d750e489df Auto-change rendering backend when Iris is present 2026-04-19 12:57:23 +05:00
James Seibel a206e49b2b up version number 3.0.1 -> 3.0.2 2026-04-18 21:44:33 -05:00
Daniel e5536de44f Remove broken TerraFirmaCraft compatibility code 2025-12-23 19:27:48 -08:00
43 changed files with 1028 additions and 538 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
+2
View File
@@ -25,6 +25,8 @@ hs_err_pid*
Merged/ Merged/
# Folder created by the buildAll scripts # Folder created by the buildAll scripts
buildAllJars/ buildAllJars/
_buildAllJars/
_buildWorkers/
relocate_natives/.venv/ relocate_natives/.venv/
relocate_natives/__pycache__/ relocate_natives/__pycache__/
-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
View File
@@ -0,0 +1,35 @@
@echo off & setlocal enabledelayedexpansion
echo ==================== Getting versions to build... ====================
mkdir _buildAllJars 2>nul
del _buildAllJars\* /Q 2>nul
set "ROOT=%~dp0"
set "WORK_DIR=%ROOT%_buildWorkers"
mkdir "%WORK_DIR%" 2>nul
REM get the number of versions to compile
set count=0
for %%f in (versionProperties\*) do set /a count+=1
echo ==================== Found %count% versions to build in parallel ====================
REM Launch a parallel job for each version
for %%f in (%ROOT%versionProperties\*) do (
set version=%%~nf
echo starting [!version!]...
start "Build !version!" cmd /c ""%ROOT%build_worker.bat" "!version!" "%ROOT%" "%WORK_DIR%" ""..\..\_buildAllJars"""
REM Minor timeout between launches so we can stop the build early if we only want
REM to test part of the script and to reduce startup load
timeout /t 3 /nobreak
REM 2>nul to supress a harmless warning that the for loop
REM "cannot find the drive specified"
) 2>nul
echo ==================== All builds started... Completed Jars will be in _buildAllJars ====================
endlocal
+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")
}
} }
} }
} }
+41
View File
@@ -0,0 +1,41 @@
@echo off & setlocal enabledelayedexpansion
set "VERSION=%~1"
set "ROOT=%~2"
set "WORK_DIR=%~3"
set "WORKER=%WORK_DIR%\%VERSION%"
set "JAR_OUTPUT_DIR=%~4"
REM remove the ending "\" from the root folder, otherwise the final quote
REM in the robocopy command will be escaped and it won't run
if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%"
set "WORKER=%~3\%~1"
set "BUILT_JAR_DIR=%WORKER%\build\forgix"
echo ==================== [%VERSION%] Copying workspace ====================
mkdir "%WORKER%"
robocopy "%ROOT%" "%WORKER%" /E /XD "%WORKER%" "_buildWorkers" "buildAllJars" ".gradle" "build" ".git" ".idea" ".gitlab" "run" "testScripts" /NFL /NDL
echo ==================== [%VERSION%] Cleaning ====================
cd /d "%WORKER%"
call .\gradlew.bat clean
REM optional arg that can be added if we want to log the result to a file
REM >"%WORK_DIR%\build_%VERSION%.log" 2>&1
echo ==================== [%VERSION%] Assembling ====================
call .\gradlew.bat assemble -PmcVer="%VERSION%"
REM optional arg that can be added if we want to log the result to a file
REM >>"%WORK_DIR%\build_%VERSION%.log" 2>&1
echo ==================== [%VERSION%] Exporting ====================
mkdir "%JAR_OUTPUT_DIR%"
robocopy "%BUILT_JAR_DIR%" "%JAR_OUTPUT_DIR%" /NFL /NDL
echo ==================== [%VERSION%] Done ====================
endlocal
REM can be uncommented for debugging
REM pause
@@ -8,8 +8,8 @@ import com.seibel.distanthorizons.common.commands.CommandInitializer;
import com.seibel.distanthorizons.common.wrappers.DependencySetup; import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.common.wrappers.gui.DhDebugScreenEntry; import com.seibel.distanthorizons.common.wrappers.gui.DhDebugScreenEntry;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
import com.seibel.distanthorizons.core.Initializer;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigHandler; import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
@@ -95,7 +95,8 @@ public abstract class AbstractModInitializer
// Client uses config for auto-updater, so it's initialized here instead of post-init stage // Client uses config for auto-updater, so it's initialized here instead of post-init stage
this.initConfig(); this.initConfig();
logModIncompatibilityWarnings(); // needs to be called after config loading logIncompatibilityWarnings(); // needs to be called after config loading
Initializer.postConfigInit();
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized."); LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
@@ -137,6 +138,7 @@ public abstract class AbstractModInitializer
MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server; MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
this.initConfig(); this.initConfig();
Initializer.postConfigInit();
this.postInit(); this.postInit();
this.postServerInit(); this.postServerInit();
this.commandInitializer.onServerReady(); this.commandInitializer.onServerReady();
@@ -159,7 +161,7 @@ public abstract class AbstractModInitializer
private void startup() private void startup()
{ {
DependencySetup.createSharedBindings(); DependencySetup.createSharedBindings();
SharedApi.init(); Initializer.preConfigInit();
this.createInitialSharedBindings(); this.createInitialSharedBindings();
} }
@@ -261,9 +263,9 @@ public abstract class AbstractModInitializer
//==================================// //======================//
// mod partial compatibility checks // // compatibility checks //
//==================================// //======================//
//region //region
/** /**
@@ -272,7 +274,7 @@ public abstract class AbstractModInitializer
* This method will log (and display to chat if enabled) * This method will log (and display to chat if enabled)
* these warnings and potential fixes. * these warnings and potential fixes.
*/ */
private static void logModIncompatibilityWarnings() private static void logIncompatibilityWarnings()
{ {
boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get(); boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get();
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class); IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
@@ -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;
}
}
@@ -299,27 +299,27 @@ public class BlazeDebugWireframeRenderer extends AbstractDebugWireframeRenderer
// render // // render //
//try (RenderPass renderPass = commandEncoder.createRenderPass( try (RenderPass renderPass = commandEncoder.createRenderPass(
// this::getRenderPassName, this::getRenderPassName,
// BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView,
// /*optionalClearColorAsInt*/ OptionalInt.empty(), /*optionalClearColorAsInt*/ OptionalInt.empty(),
// BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView,
// /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) /*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
//{ {
// // Bind instance data // // Bind instance data //
// renderPass.setUniform("uniformBlock", this.uniformBuffer); renderPass.setUniform("uniformBlock", this.uniformBuffer);
//
// renderPass.setPipeline(this.pipeline); renderPass.setPipeline(this.pipeline);
// renderPass.setIndexBuffer(this.boxIndexBuffer, VertexFormat.IndexType.INT); renderPass.setIndexBuffer(this.boxIndexBuffer, VertexFormat.IndexType.INT);
//
// renderPass.setVertexBuffer(0, this.boxVertexBuffer); renderPass.setVertexBuffer(0, this.boxVertexBuffer);
//
// renderPass.drawIndexed( renderPass.drawIndexed(
// /*indexStart*/ 0, /*indexStart*/ 0,
// /*firstIndex*/0, /*firstIndex*/0,
// /*indexCount*/BOX_OUTLINE_INDICES.length, /*indexCount*/BOX_OUTLINE_INDICES.length,
// /*instanceCount*/1); /*instanceCount*/1);
//} }
} }
private String getRenderPassName() { return "distantHorizons:McDebugRenderer"; } private String getRenderPassName() { return "distantHorizons:McDebugRenderer"; }
@@ -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
@@ -19,6 +19,7 @@ import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeBufferRenderEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeBufferRenderEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderPassEvent;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
@@ -267,6 +268,8 @@ public class BlazeDhTerrainRenderer implements IDhTerrainRenderer
{ {
profiler.popPush("rendering"); profiler.popPush("rendering");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
// create a render pass // create a render pass
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName, this::getRenderPassName,
@@ -10,7 +10,6 @@ import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.render.openGl.glObject.GlDhFramebuffer; import com.seibel.distanthorizons.common.render.openGl.glObject.GlDhFramebuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.texture.*; import com.seibel.distanthorizons.common.render.openGl.glObject.texture.*;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader; import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader;
import com.seibel.distanthorizons.common.render.openGl.terrain.GlDhTerrainShaderProgram;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
@@ -22,7 +21,6 @@ import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.RenderParams; import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
@@ -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
} }
@@ -21,14 +21,19 @@ package com.seibel.distanthorizons.common.render.openGl.glObject.buffer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; 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.render.openGl.glObject.GLState;
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;
@@ -36,9 +41,12 @@ 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;
import java.util.concurrent.locks.StampedLock;
public class GLBuffer implements AutoCloseable public class GLBuffer implements AutoCloseable
{ {
@@ -50,26 +58,57 @@ 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");
protected int id; protected volatile int id = 0;
public final int getId() { return this.id; } public final int getId() { return this.id; }
protected int size = 0; protected int size = 0;
public int getSize() { return this.size; } public int getSize() { return this.size; }
protected boolean bufferStorage; protected boolean bufferStorage;
public final boolean isBufferStorage() { return this.bufferStorage; }
protected boolean isMapped = false; protected boolean isMapped = false;
/**
* Locking on the render thread isn't great, but is needed due to an inconsistent
* race condition where VBOs can be marked as deleted outside the render thread. <br><br>
*
* But, due to being a read-write lock the chance of freezing
* the render thread is very low
* and since this is a stamped lock, the optimistic read time is basically zero.
* (The optimistic lock time doesn't even appear in the profiler).
*/
public final StampedLock renderStampLock = new StampedLock();
//==============// //==============//
@@ -112,72 +151,119 @@ public class GLBuffer implements AutoCloseable
LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread."); LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread.");
} }
// destroy the old buffer if one is present
if (this.id != 0) // lock to prevent the render thread from accessing the buffer's ID
// while we are removing it
long writeStamp = renderStampLock.writeLock();
try
{ {
destroyBufferIdNow(this.id); final int oldId = this.id;
this.id = GLMC.glGenBuffers();
//LOGGER.info("created [" + newId + "].");
// destroy the old buffer
// after the new one has been created
// to hopefully prevent a rare race conditions where the old ID
// is still used somewhere
if (oldId != 0)
{
// this ID doesn't need to be tracked anymore
tryRemoveBufferIdFromPhantom(oldId);
destroyBufferIdNow(oldId, "destroyOldAndCreate");
}
this.bufferStorage = asBufferStorage;
bufferCount.getAndIncrement();
PhantomReference<GLBuffer> phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE);
PHANTOM_TO_BUFFER_ID.put(phantom, this.id);
BUFFER_ID_TO_PHANTOM.put(this.id, phantom);
this.updateAllocationStackTrace();
}
finally
{
renderStampLock.unlock(writeStamp);
} }
this.id = GLMC.glGenBuffers();
this.bufferStorage = asBufferStorage;
bufferCount.getAndIncrement();
PhantomReference<GLBuffer> phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE);
PHANTOM_TO_BUFFER_ID.put(phantom, this.id);
BUFFER_ID_TO_PHANTOM.put(this.id, phantom);
} }
protected void destroyAsync() protected void destroyAsync()
{ {
if (this.id == 0) // lock to prevent the render thread from accessing the buffer's ID
// while we are removing it
long writeStamp = renderStampLock.writeLock();
try
{ {
// the buffer has already been closed if (this.id == 0)
return; {
// the buffer has already been closed
return;
}
final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda
// remove the phantom tracking now so the phantom doesn't have the chance to
// get garbage collected before the render thread task runs
// (this can happen if MC is running at extremely low framerates like 1 fps via mods)
tryRemoveBufferIdFromPhantom(idToDelete);
// mark the old data is invalid before deleting to prevent a rare race condition
// where the queued on render thread task runs before the ID is cleared
this.id = 0;
this.size = 0;
//LOGGER.info("async destroy [" + idToDelete + "].");
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete, "destroyAsync"); });
}
finally
{
renderStampLock.unlock(writeStamp);
} }
final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete); });
this.id = 0;
this.size = 0;
} }
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;
} }
// remove and clear the phantom reference if present bufferCount.decrementAndGet();
if (BUFFER_ID_TO_PHANTOM.containsKey(id)) GLMC.glDeleteBuffers(id);
if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
{
LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "], cause: ["+cause+"].");
}
}
/** should be called before {@link GLBuffer#destroyBufferIdNow} */
private static void tryRemoveBufferIdFromPhantom(int 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)
}
bufferCount.decrementAndGet();
// destroy the buffer if it exists,
// the buffer may not exist if the destroy method is called twice
if (GL32.glIsBuffer(id))
{
GLMC.glDeleteBuffers(id);
if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
{ {
LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]"); LOGGER.warn("No Phantom->ID binding stored for ID ["+id+"]");
} }
} }
else
{
LOGGER.warn("Unable to remove phantom GLBuffer with ID ["+id+"], buffer may have already been deleted.");
}
} }
//endregion //endregion
@@ -263,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)
@@ -272,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
@@ -321,7 +440,6 @@ public class GLBuffer implements AutoCloseable
{ {
// recreate if the buffer storage type changed // recreate if the buffer storage type changed
this.bind(); this.bind();
destroyBufferIdNow(this.id);
this.destroyOldAndCreate(uploadMethod.useBufferStorage); this.destroyOldAndCreate(uploadMethod.useBufferStorage);
this.bind(); this.bind();
} }
@@ -338,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
@@ -349,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
@@ -359,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()
@@ -289,6 +289,9 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS
GLMC.disableBlend(); GLMC.disableBlend();
} }
// needs to be triggered after DH attempts to set the GL state so that Iris
// can override it as needed
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
@@ -311,6 +314,12 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS
for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++)
{ {
LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); LodBufferContainer bufferContainer = bufferContainers.get(lodIndex);
if (!bufferContainer.buffersUploaded)
{
// make sure we don't accidentally try
// rendering a buffer that is (or is going to be) freed
continue;
}
// set uniforms and fire events // set uniforms and fire events
{ {
@@ -335,25 +344,45 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS
continue; continue;
} }
if (vbo.getVertexCount() == 0)
// for lock information please view the lock's javadocs
long vboReadStamp = vbo.renderStampLock.readLock();
long iboReadStamp = vbo.getQuadIBO().renderStampLock.readLock();
try
{ {
continue; // don't render empty sections
if (vbo.getVertexCount() == 0)
{
continue;
}
// don't render deleted VBOs (this will crash the driver/game)
if (vbo.getId() == 0
|| vbo.getQuadIBO().getId() == 0)
{
continue;
}
// 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5
int indexCount = (int) (vbo.getVertexCount() * 1.5);
vbo.bind();
vbo.getQuadIBO().bind();
GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId());
GL32.glDrawElements(
GL32.GL_TRIANGLES,
indexCount,
vbo.getQuadIBO().getGlType(), 0);
vbo.unbind();
vbo.getQuadIBO().unbind();
}
finally
{
vbo.renderStampLock.unlock(vboReadStamp);
vbo.getQuadIBO().renderStampLock.unlock(iboReadStamp);
} }
// 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5
int indexCount = (int)(vbo.getVertexCount() * 1.5);
vbo.bind();
vbo.getQuadIBO().bind();
GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId());
GL32.glDrawElements(
GL32.GL_TRIANGLES,
indexCount,
vbo.getQuadIBO().getGlType(), 0);
vbo.unbind();
vbo.getQuadIBO().unbind();
} }
} }
} }
@@ -80,9 +80,20 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE); SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
} }
private static boolean renderingApiBindingsSet = false;
/** will be called from a DH thread, not the render thread */ /** will be called from a DH thread, not the render thread */
public static void setRenderingApiBindings() public synchronized static void setRenderingApiBindings()
{ {
// shouldn't happen, but there was a single report that this method was triggered twice
if (renderingApiBindingsSet)
{
LOGGER.warn("Rendering bindings already set, skipping. How did this happen?");
return;
}
renderingApiBindingsSet = true;
EDhApiRenderApi renderingApiEnum = Config.Client.Advanced.Graphics.Experimental.renderingApi.get(); EDhApiRenderApi renderingApiEnum = Config.Client.Advanced.Graphics.Experimental.renderingApi.get();
if (renderingApiEnum == EDhApiRenderApi.AUTO) if (renderingApiEnum == EDhApiRenderApi.AUTO)
{ {
@@ -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)
{ {
@@ -154,22 +154,12 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
static static
{ {
boolean isTerraFirmaCraftPresent = false;
try
{
Class.forName("net.dries007.tfc.world.TFCChunkGenerator");
isTerraFirmaCraftPresent = true;
LOGGER.info("TerraFirmaCraft detected.");
}
catch (ClassNotFoundException ignore) { }
ImmutableMap.Builder<EDhApiWorldGenerationStep, Integer> builder = ImmutableMap.builder(); ImmutableMap.Builder<EDhApiWorldGenerationStep, Integer> builder = ImmutableMap.builder();
builder.put(EDhApiWorldGenerationStep.EMPTY, 1); builder.put(EDhApiWorldGenerationStep.EMPTY, 1);
builder.put(EDhApiWorldGenerationStep.STRUCTURE_START, 0); builder.put(EDhApiWorldGenerationStep.STRUCTURE_START, 0);
builder.put(EDhApiWorldGenerationStep.STRUCTURE_REFERENCE, 0); builder.put(EDhApiWorldGenerationStep.STRUCTURE_REFERENCE, 0);
builder.put(EDhApiWorldGenerationStep.BIOMES, isTerraFirmaCraftPresent ? 1 : 0); builder.put(EDhApiWorldGenerationStep.BIOMES, 0);
builder.put(EDhApiWorldGenerationStep.NOISE, isTerraFirmaCraftPresent ? 1 : 0); builder.put(EDhApiWorldGenerationStep.NOISE, 0);
builder.put(EDhApiWorldGenerationStep.SURFACE, 0); builder.put(EDhApiWorldGenerationStep.SURFACE, 0);
builder.put(EDhApiWorldGenerationStep.CARVERS, 0); builder.put(EDhApiWorldGenerationStep.CARVERS, 0);
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0); builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
@@ -205,11 +195,6 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
LOGGER.info("TerraForge Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it."); LOGGER.info("TerraForge Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "]."); LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
} }
else if (generator.getClass().toString().equals("class net.dries007.tfc.world.TFCChunkGenerator"))
{
LOGGER.info("TerraFirmaCraft Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
}
else else
{ {
LOGGER.warn("Unknown Chunk Generator detected: [" + generator.getClass() + "], Distant Generation May Fail!"); LOGGER.warn("Unknown Chunk Generator detected: [" + generator.getClass() + "], Distant Generation May Fail!");
@@ -12,11 +12,9 @@ import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat; import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.generation.DhLightingEngine; import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.DhServerLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel; import com.seibel.distanthorizons.core.level.IDhServerLevel;
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.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.ExceptionUtil; import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.TimerUtil;
@@ -195,12 +193,22 @@ public class InternalServerGenerator
} }
finally finally
{ {
ArrayList<CompletableFuture<Void>> releaseFutures = new ArrayList<>();
// release all chunks from the server to prevent out of memory issues // release all chunks from the server to prevent out of memory issues
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0); Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
while (chunkPosIterator.hasNext()) while (chunkPosIterator.hasNext())
{ {
ChunkPos chunkPos = chunkPosIterator.next(); ChunkPos chunkPos = chunkPosIterator.next();
this.releaseChunkFromServer(this.params.mcServerLevel, this.params.dhServerLevel, chunkPos); releaseFutures.add(this.releaseChunkFromServerAsync(this.params.mcServerLevel, chunkPos));
}
// wait for all release futures to finish to prevent an issue where DH queues
// tickets faster than MC can clear them out
for (int i = 0; i < releaseFutures.size(); i++)
{
CompletableFuture<Void> releaseFuture = releaseFutures.get(i);
releaseFuture.join();
} }
} }
} }
@@ -286,8 +294,10 @@ public class InternalServerGenerator
* mitigates out of memory issues in the vanilla chunk system. <br> * mitigates out of memory issues in the vanilla chunk system. <br>
* See: https://github.com/pop4959/Chunky/pull/383 * See: https://github.com/pop4959/Chunky/pull/383
*/ */
private void releaseChunkFromServer(ServerLevel level, IDhServerLevel dhLevel, ChunkPos chunkPos) private CompletableFuture<Void> releaseChunkFromServerAsync(ServerLevel level, ChunkPos chunkPos)
{ {
CompletableFuture<Void> removeTicketFuture = new CompletableFuture<>();
level.getChunkSource().chunkMap.mainThreadExecutor.execute(() -> level.getChunkSource().chunkMap.mainThreadExecutor.execute(() ->
{ {
try try
@@ -323,9 +333,15 @@ public class InternalServerGenerator
} }
catch (Exception e) catch (Exception e)
{ {
LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e); LOGGER.warn("Failed to release chunk ["+chunkPos+"] back to internal server. Error: ["+e.getMessage()+"]", e);
}
finally
{
removeTicketFuture.complete(null);
} }
}); });
return removeTicketFuture;
} }
@@ -40,8 +40,6 @@ public final class ThreadWorldGenParams
public StructureCheck structCheck; public StructureCheck structCheck;
#endif #endif
boolean isValid = true;
// used for some older MC versions // used for some older MC versions
private static GlobalWorldGenParams previousGlobalWorldGenParams = null; private static GlobalWorldGenParams previousGlobalWorldGenParams = null;
@@ -55,7 +53,6 @@ public final class ThreadWorldGenParams
{ {
ThreadWorldGenParams threadParam = LOCAL_PARAM_REF.get(); ThreadWorldGenParams threadParam = LOCAL_PARAM_REF.get();
if (threadParam != null if (threadParam != null
&& threadParam.isValid
&& threadParam.level == globalParams.mcServerLevel) && threadParam.level == globalParams.mcServerLevel)
{ {
return threadParam; return threadParam;
-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.1-b mod_version=3.0.3-b
api_version=6.0.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=