Compare commits

..

95 Commits

Author SHA1 Message Date
James Seibel 17e375bc61 Return IBlockDetailWrappers 2022-03-19 20:04:11 -05:00
James Seibel a2c8c90a5e Merge branch '1.18.2' into preprocessor_test 2022-03-19 19:56:04 -05:00
James Seibel b376014df8 Update the manifold version 2022-03-19 18:20:28 -05:00
James Seibel b9ee54f6ad add multi-dim and brightness/saturation 2022-03-19 12:39:09 -05:00
James Seibel 6c7e1900a3 Merge in fog changes 2022-03-19 11:20:44 -05:00
tom lee 6f929f40f9 Update core 2022-03-19 22:34:11 +08:00
James Seibel b6a8930855 Add a config for the MultiDimension Similarity 2022-03-18 23:57:32 -05:00
coolGi2007 7a91b258de Updated mods, readme and fixed 1 thing in the config gui 2022-03-17 17:52:19 +10:30
TomTheFurry 35bef76aeb Fix 1.18.2 WorldRenderer renderSky() mixin falling 2022-03-16 19:57:27 +08:00
TomTheFurry 780d0ad9fb Complete the push 2022-03-16 18:53:16 +08:00
TomTheFurry c0d5dd6dee ADVANCED FOGGGGGGGGGGGGGGGG~~~~~~~~~~~~~~~~ 2022-03-16 18:52:56 +08:00
TomTheFurry 3e87e625ba Pushed the stuff leetom did on my macbook 2022-03-15 21:17:07 +10:30
TomTheFurry 56f9403859 Add proper multi FrameBuffer support(And fix MacOS?) 2022-03-15 16:38:39 +08:00
TomTheFurry 8259c79e9c Fixup MixinUtilBackgroudThread+more render log 2022-03-15 18:48:27 +10:30
TomTheFurry be1e2fe7e6 Merge remote-tracking branch 'origin/preprocessor_test' into preprocessor_test 2022-03-15 12:38:10 +08:00
TomTheFurry 609ee5c70d Improve GLMessage Parser 2022-03-15 12:37:51 +08:00
James Seibel 207cab9a0f Clean up the build script 2022-03-15 04:05:12 +00:00
James Seibel c80b025ac1 Clean up the build script 2022-03-15 03:52:25 +00:00
James Seibel 8610917b86 Update .gitlab-ci.yml file 2022-03-15 03:41:17 +00:00
James Seibel fae4bee871 Update .gitlab-ci.yml file 2022-03-15 03:34:52 +00:00
James Seibel b9608498a2 Check if Gradle setup is necessary + fix artifacts not being populated 2022-03-15 03:34:29 +00:00
James Seibel fe59c6b0c9 Check if Gradle setup is necessary 2022-03-15 03:25:09 +00:00
James Seibel b082b048d6 Update .gitlab-ci.yml file 2022-03-15 03:21:59 +00:00
James Seibel 91712cee2a Rough artifact test 2022-03-15 03:21:44 +00:00
James Seibel 5adfbb2dee Update .gitlab-ci.yml 2022-03-15 03:04:50 +00:00
James Seibel ec32d09468 Try separating the archiving into a separate step
This will make adding/removing MC versions to compile easier
2022-03-15 02:51:39 +00:00
James Seibel 919990820e Fix exporting duplicate jars 2022-03-15 02:43:12 +00:00
James Seibel 5cc31efa12 Attempt to add 1.18.1 and 1.18.2 auto building 2022-03-15 02:16:04 +00:00
TomTheFurry 5f48b41693 Add Advanced Fog config entries. Actual impl is a todo. 2022-03-15 00:00:36 +08:00
TomTheFurry 9530eea287 Update core and fix serverRenderDistanceMax not being checked 2022-03-14 16:23:04 +08:00
TomTheFurry ebbf304c94 Updated core 2022-03-14 15:10:20 +08:00
TomTheFurry 7c8ac0c2ff Fix mistakes + update core 2022-03-13 22:57:42 +08:00
TomTheFurry eefad5e052 Improved getMaximumRenderedChunk and fix GLMessage on forge 2022-03-13 22:57:07 +08:00
TomTheFurry 9a1e6e29d4 Update Readme.md on switching versions 2022-03-13 16:45:41 +08:00
TomTheFurry 62ab450d98 Update Core 2022-03-13 16:16:29 +08:00
TomTheFurry b4a5da4a74 Update Config defaults 2022-03-13 16:15:29 +08:00
TomTheFurry ed82480c89 Update .gitmodules properly this time 2022-03-13 07:41:53 +00:00
TomTheFurry 82449c5edc Update and fix core branch tracking(I think?) 2022-03-13 15:40:08 +08:00
TomTheFurry b165726bc9 Add new Config: BiomeBlending 2022-03-13 15:26:24 +08:00
TomTheFurry 11910d0f28 Fix IDE not reconising preprocessor 2022-03-10 17:29:27 +08:00
TomTheFurry f66e27b077 First version where 1.18.1 & 1.18.2 is merged 2022-03-10 17:05:14 +08:00
James Seibel 835beb607d Close #207 (re-add brightness/contrast configs) 2022-03-09 23:06:34 -06:00
TomTheFurry c4e2d3fb0f Remove unused stuff 2022-03-09 16:21:44 +08:00
TomTheFurry eb3d8d9da5 Add Manifold Preprocessor plugin to gradle 2022-03-09 16:14:16 +08:00
TomTheFurry 151d548099 Update core + Fix biome blending 2022-03-08 23:27:14 +08:00
TomTheFurry c5cdc2760f Update core 2022-03-08 16:05:30 +08:00
coolGi2007 a663bf9f19 Added the multiplayer folder thing from 1.18.X to 1.18.2 and fixed building 2022-03-08 17:43:10 +10:30
coolGi2007 7ddd48d132 Updated what james did from 1.18.X to 1.18.2 2022-03-07 16:56:35 +10:30
Ran ce10a43cc6 Fix accesswidener for 1.18.2 2022-03-03 14:34:47 +06:00
tom lee 5476af5bb3 Fixed chunkloader to work with 1.18.2 2022-03-02 19:28:16 +08:00
coolGi2007 25d2cfd7b8 Quick 1.18.2 branch (**DISABLE GENERATION TO WORK**) 2022-03-02 17:43:46 +10:30
James Seibel 1b754387e0 Refactor the dependency injectors 2022-03-01 21:20:00 -06:00
James Seibel bcad40069f Merge branch '1.18.X' of gitlab.com:jeseibel/minecraft-lod-mod into 1.18.X 2022-02-28 17:57:01 -06:00
James Seibel 5aa9061006 remove the discord notification experiment
Using this changed how the messages look in a way I don't like; removing the committer's icon and commit message.
2022-02-28 17:54:41 -06:00
James Seibel 873e8cec57 Update .gitlab-ci.yml file 2022-02-28 23:37:29 +00:00
James Seibel f6f96c3aea Test pipeline discord notifications 2022-02-28 17:18:02 -06:00
tom lee aeed672295 Update core 2022-02-27 17:02:06 +08:00
tom lee 832fbe6d15 Update core 2022-02-26 16:16:00 +08:00
tom lee 03deddf666 Optimized the McObjectConverter for Direction 2022-02-26 14:04:49 +08:00
tom lee d06413d1b4 Update core 2022-02-25 15:15:51 +08:00
tom lee 8afa50d585 Update core + improved GenEvent Terminate speed 2022-02-24 22:28:59 +08:00
Morippi 9f28f1f812 Added comments 2022-02-23 18:26:35 +01:00
Morippi 985388fd90 Removed border from details circles and regions 2022-02-23 16:38:19 +01:00
tom lee e27e10082a Update core 2022-02-22 23:12:59 +08:00
tom lee 4e510d96fc Update core + fix colors. See commit in core 2022-02-22 23:10:58 +08:00
coolGi2007 c0ec02f062 forgot this like 2022-02-22 07:22:33 +00:00
coolGi2007 bd27a96bde Updated the readme and updated the DHJarMerger (now marks the jar as an executable so linux users have an easier time when runnung the jar) 2022-02-22 17:49:23 +10:30
tom lee d6aeaf8e9d Update Core 2022-02-22 11:37:22 +08:00
Ran 2b9bba4aa7 Update core 2022-02-21 17:36:08 +06:00
Ran 58b0991891 Put logger in ApiShared.java 2022-02-21 17:34:52 +06:00
Ran 59cbbef327 Merge remote-tracking branch 'origin/1.18.X' into 1.18.X 2022-02-21 17:29:41 +06:00
Ran 7047c81a2b Put logger in ApiShared.java 2022-02-21 17:27:54 +06:00
tom lee 6e2ab47359 Rework Block color / shape 2022-02-21 19:23:30 +08:00
Ran f879332e1d Client 2 Server packets 2022-02-21 13:40:42 +06:00
coolGi2007 6fcafecc61 Updated fabric, modmenu and readme 2022-02-21 16:36:10 +10:30
coolGi2007 a5af93aef1 Renamed the thing to JarMain 2022-02-20 22:26:01 +00:00
coolGi2007 4b84fd2a67 Added a way to run jar (dosnt do anything but could be used to check opengl and move mod to mods folder) 2022-02-20 22:16:21 +10:30
cola98765 924efc0cbd update core 2022-02-20 12:34:09 +01:00
tom lee 288457b5bd Update core 2022-02-20 17:35:11 +08:00
James Seibel d29fa86de6 Merge branch 'jeffthejeffthejeff-1.18.X-patch-51031' into '1.18.X'
Added an image and improved start of Readme.md

See merge request jeseibel/minecraft-lod-mod!17
2022-02-20 05:13:39 +00:00
James Seibel 97be227e3d Add a missing "s" 2022-02-20 05:12:16 +00:00
James Seibel 909d6a54c3 Slightly reword the blurb 2022-02-20 05:10:35 +00:00
Ran 228b6a80dc Merge branch '1.18.X' of https://gitlab.com/jeseibel/minecraft-lod-mod into 1.18.X 2022-02-20 09:32:44 +06:00
Ran 6936a5f96f I forgor 💀 2022-02-20 09:31:55 +06:00
cola98765 740560a50a actually fix tint on custom water textures. 2022-02-19 22:32:53 +01:00
cola98765 47c28f0f37 actually fix tint on custom water textures. 2022-02-19 22:28:35 +01:00
Ran 1c859cd7da Oops was registering channels to wrong environments 2022-02-20 00:15:33 +06:00
tom lee e4a97dd76d Update core 2022-02-20 01:01:59 +08:00
Ran 4fd1e5ea06 Update core 2022-02-19 22:58:05 +06:00
Ran 6039aeabde Server client communication! maybe 2022-02-19 22:55:53 +06:00
tom lee 17a384f074 Update core 2022-02-19 22:15:04 +08:00
coolGi2007 25e8f5ec6a Forgot to push this file 2022-02-19 08:30:06 +00:00
coolGi2007 eac8cacd42 Removed new config 2022-02-19 18:53:34 +10:30
Dog 81adade05b Capitalised Source Code Installation to be grammatically correct 2022-02-17 23:55:38 +00:00
Dog e3808dc986 Updated title of Readme to look nicer 2022-02-17 23:45:55 +00:00
124 changed files with 7899 additions and 2139 deletions
+38 -70
View File
@@ -3,16 +3,13 @@ image: gradle:eclipse-temurin
# all stages need to be defined here # all stages need to be defined here
stages: stages:
- gradleSetup - build_18-1
- build - build_18-2
- merge # - zip_artifacts
variables: variables:
# Disable the Gradle daemon for Continuous Integration servers as correctness # If we have correctness issues when compiling this can be set to false
# is usually a priority over speed in CI environments. Using a fresh GRADLE_OPTS: "-Dorg.gradle.daemon=true"
# runtime for each build is more reliable since the runtime is completely
# isolated from any previous builds.
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
# Pull core when building # Pull core when building
GIT_SUBMODULE_STRATEGY: recursive GIT_SUBMODULE_STRATEGY: recursive
@@ -23,83 +20,36 @@ before_script:
- echo GE_JOB_ID=$CI_JOB_ID >> generate_jars.env - echo GE_JOB_ID=$CI_JOB_ID >> generate_jars.env
# 1.18.1 build
build_18-1:
# first stage stage: build_18-1
# This prevents us from re-downloading Gradle every stage script: ./gradlew build -PmcVer=1.18.1 --gradle-user-home cache/; ./gradlew merge --gradle-user-home cache/
gradleSetup:
stage: gradleSetup
script: ./gradlew --build-cache --gradle-user-home cache/ check
# build using Java 17 # build using Java 17
image: eclipse-temurin:17 image: eclipse-temurin:17
cache: cache:
key: key: "$CI_COMMIT_REF_NAME_18-1"
files:
- gradle/wrapper/gradle-wrapper.properties
policy: push
paths:
- cache/
# second stage
build:
stage: build
script: ./gradlew build --gradle-user-home cache/
# build using Java 17
image: eclipse-temurin:17
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull-push policy: pull-push
paths: paths:
- .architectury-transformer
- .gradle
- build
- common/.gradle
- common/build
- core/.gradle
- core/build
- fabric/.gradle
- fabric/src/generated
- forge/.gradle
- forge/src/generated
- build
- .gradle - .gradle
- cache/ - cache/
artifacts:
name: "NightlyBuild-$CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
# relative to the root directory
- fabric/build/libs
- forge/build/libs
exclude:
- fabric/build/libs/*-dev.jar
- fabric/build/libs/*-dev-shadow.jar
- fabric/build/libs/*-sources-dev.jar
- fabric/build/libs/*-sources.jar
- forge/build/libs/*-dev.jar
- forge/build/libs/*-dev-shadow.jar
- forge/build/libs/*-sources-dev.jar
- forge/build/libs/*-sources.jar
reports:
# To ensure we've access to these files in the next stage
dotenv: generate_jars.env
expire_in: 1 day
# third stage
merge: # 1.18.2 build
stage: merge build_18-2:
script: ./gradlew merge --gradle-user-home cache/ stage: build_18-2
script: ./gradlew build -PmcVer=1.18.2 --gradle-user-home cache/; ./gradlew merge --gradle-user-home cache/
# build using Java 17 # build using Java 17
image: eclipse-temurin:17 image: eclipse-temurin:17
cache: cache:
key: "$CI_COMMIT_REF_NAME" key: "$CI_COMMIT_REF_NAME_18-2"
policy: pull-push policy: pull-push
paths: paths:
- build
- .gradle - .gradle
- cache/ - cache/
# TEMPORARY, there should be a way of either generating artifacts per build or in a separate stage
artifacts: artifacts:
# This should only be done after the last build, otherwise there will be duplicate jars
name: "Merged_NightlyBuild-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}" name: "Merged_NightlyBuild-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths: paths:
# relative to the root directory # relative to the root directory
@@ -107,7 +57,25 @@ merge:
expire_in: 1 day expire_in: 1 day
## forth stage # # put artifacts in a zip
# # This should only be done after all the builds have been completed
# zip_artifacts:
# stage: zip_artifacts
# # The complier complains if we don't do something with the script line
# script: echo 'Building complete. Creating artifact Zip.'
# # build using Java 17
# image: eclipse-temurin:17
# artifacts:
# # This should only be done after the last build, otherwise there will be duplicate jars
# name: "Merged_NightlyBuild-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
# paths:
# # relative to the root directory
# - Merged
# expire_in: 1 day
# unused deployment stage
#deploy: #deploy:
# stage: deploy # stage: deploy
# image: registry.gitlab.com/gitlab-org/release-cli:latest # image: registry.gitlab.com/gitlab-org/release-cli:latest
+1
View File
@@ -1,3 +1,4 @@
[submodule "core"] [submodule "core"]
path = core path = core
url = https://gitlab.com/jeseibel/distant-horizons-core.git url = https://gitlab.com/jeseibel/distant-horizons-core.git
branch = main
+37
View File
@@ -0,0 +1,37 @@
# 1.18.1 version based stuff
minecraft_version=1.18.1
java_version = 17
# Fabric loader
fabric_loader_version=0.13.3
fabric_api_version=0.46.6+1.18
# Fabric mod versions
modmenu_version=3.0.1
starlight_version_fabric=3554912
lithium_version=mc1.18.1-0.7.7
sodium_version=3605309
iris_version=1.18.x-v1.1.4
bclib_version=1.2.5
immersive_portals_version = v1.0.4-1.18
# Fabric mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight=0
enable_sodium=1
enable_lithium=0
enable_iris=0
enable_bclib=0
# Forge loader
forge_version=39.1.2
# Forge mod versions
starlight_version_forge=3559934
# Forge mod run
# 0 = Dont enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
+38
View File
@@ -0,0 +1,38 @@
# 1.18.2 version based stuff
# architectury_plugin_version = 3.4-SNAPSHOT
# dev_architectury_loom_version = 0.10.0-SNAPSHOT
minecraft_version=1.18.2
java_version = 17
# Fabric loader
fabric_loader_version=0.13.3
fabric_api_version=0.48.0+1.18.2
# Fabric mod versions
modmenu_version=3.1.0
starlight_version_fabric=3667443
lithium_version=mc1.18.2-0.7.9
sodium_version=3669187
iris_version=1.18.x-v1.2.2
immersive_portals_version = v1.0.4-1.18
bclib_version=0
# Fabric mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight=0
enable_sodium=1
enable_lithium=0
enable_iris=0
enable_bclib=0
# Forge loader
forge_version=40.0.18
# Forge mod versions
starlight_version_forge=0
# Forge mod run
# 0 = Dont enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
+49 -19
View File
@@ -1,4 +1,9 @@
# Distant Horizons # <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_logo%20files/LOD%20logo%20flat%20-%20with%20boarder.png" width="32"> Distant Horizons
> A mod that adds a Level of Detail System to Minecraft
# What is Distant Horizons?
This mod adds a Level Of Detail (LOD) system to Minecraft.\ This mod adds a Level Of Detail (LOD) system to Minecraft.\
This implementation renders simplified chunks outside the normal render distance\ This implementation renders simplified chunks outside the normal render distance\
@@ -9,20 +14,30 @@ If you want to see a quick demo, check out a video covering the mod here:
<a href="https://www.youtube.com/watch?v=H2tnvEVbO1c" target="_blank">![Minecraft Level Of Detail (LOD) mod - Alpha 1.5](https://i.ytimg.com/vi_webp/H2tnvEVbO1c/mqdefault.webp)</a> <a href="https://www.youtube.com/watch?v=H2tnvEVbO1c" target="_blank">![Minecraft Level Of Detail (LOD) mod - Alpha 1.5](https://i.ytimg.com/vi_webp/H2tnvEVbO1c/mqdefault.webp)</a>
Architectury version: 3.4-SNAPSHOT\ ### Versions
Forge version: 39.0.5 and 38.0.14\
Fabric version: 0.12.12\ Architectury version: 3.4-SNAPSHOT\
Fabric API version: 0.44.0+1.18 Java Compiler plugin: Manifold Preprocessor
#### 1.18.2 mods
Forge version: 40.0.18\
Fabric version: 0.13.3\
Fabric API version: 0.48.0+1.18.2\
Modmenu version: 3.1.0
#### 1.18.1 mods
Forge version: 39.1.2\
Fabric version: 0.13.3\
Fabric API version: 0.46.6+1.18\
Modmenu version: 3.0.1
Modmenu version: 3.0.0\
Sodium version: mc1.18-0.4.0-alpha5
Notes:\ Notes:\
This version has been confirmed to work in Eclipse and Retail Minecraft.\ This version has been confirmed to work in IDE and Retail Minecraft.\
(Retail running forge version 1.18.1-39.0.5 and fabric version 1.18-0.12.12 and 1.18.1-0.12.12) (Retail running forge version 1.18.1-39.0.5 and fabric version 1.18-0.12.12 and 1.18.1-0.13.2)
## source code installation ## Source Code Installation
See the Fabric Documentation online for more detailed instructions:\ See the Fabric Documentation online for more detailed instructions:\
https://fabricmc.net/wiki/tutorial:setup https://fabricmc.net/wiki/tutorial:setup
@@ -31,31 +46,46 @@ https://fabricmc.net/wiki/tutorial:setup
* A Java Development Kit (JDK) for Java 17 (recommended) or newer. Visit https://www.oracle.com/java/technologies/downloads/ for installers. * A Java Development Kit (JDK) for Java 17 (recommended) or newer. Visit https://www.oracle.com/java/technologies/downloads/ for installers.
* Git or someway to clone git projects. Visit https://git-scm.com/ for installers. * Git or someway to clone git projects. Visit https://git-scm.com/ for installers.
* (Not required) Any Java IDE, for example Intellij IDEA and Eclipse. You may also use any other code editors, such as Visual Studio Code. (Optional) * (Not required) Any Java IDE with plugins that support Manifold, for example Intellij IDEA.
It's better to use IntelliJ IDEA since Eclipse is not supported by Architectury, but it still works.
**If using IntelliJ:** **If using IntelliJ:**
0. Install Manifold plugin
1. open IDEA and import the build.gradle 1. open IDEA and import the build.gradle
2. refresh the Gradle project in IDEA if required 2. refresh the Gradle project in IDEA if required
**If using Ecplise:** **If using Ecplise: (Note that Eclispe currently doesn't support Manifold's preprocessor!)**
1. run the command: `./gradlew geneclipseruns` 1. run the command: `./gradlew geneclipseruns`
2. run the command: `./gradlew eclipse` 2. run the command: `./gradlew eclipse`
3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft) 3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft)
4. Import the project into eclipse 4. Import the project into eclipse
## Switching Versions
This branch support 2 built versions:
- 1.18.2
- 1.18.1 (which also runs on 1.18)
To switch between active versions, change `mcVer=1.18.?` in `gradle.properties` file.
If running on IDE, to ensure IDE pickup the changed versions, you will need to run a gradle command again to allow gradle to update all the libs. (In IntellJ you will also need to do a gradle sync again if it didn't start it automatically.)
>Note: There may be a `java.nio.file.FileSystemException` thrown on running the command after switching versions. To fix it, either restart your IDE (as your IDE is locking up a file) or use tools like LockHunter to unlock the linked file. (Often a lib file under `common\build\lib` or `forge\build\lib` or `fabric\build\lib`). If anyone knows how to solve this issue please comment to this issue: https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
## Compiling ## Compiling
**Using GUI** **Using GUI**
1. Open a command line in the project folder 1. Download the zip of the project and extract it
2. Run the command: `./gradlew build` 2. Download the core from https://gitlab.com/jeseibel/distant-horizons-core and extract into a folder called `core`
3. The compiled jar file will be in the folder `fabric/build/libs/` and `forge/build/libs/` 3. Open a command line in the project folder
4. Run the command: `./gradlew assemble`
5. Then run command: `./gradlew mergeJars`
6. The compiled jar file will be in the folder `Merged`
**If in terminal:** **If in terminal:**
1. `git clone -b 1.18.X --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git` 1. `git clone -b preprocessor_test --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
2. `cd minecraft-lod-mod` 2. `cd minecraft-lod-mod`
3. `./gradlew assemble` or `./gradlew build` 3. `./gradlew assemble`
4. The build should be in `fabric/build/libs/` and `forge/build/libs/` 4. `./gradlew mergeJars`
5. The compiled jar file will be in the folder `Merged`
>Note: You can add the arg: `-PmcVer=1.18.?` to tell gradle to build a selected MC version instead of having to manually modify the `gradle.properties` file.
## Other commands ## Other commands
+52 -4
View File
@@ -1,5 +1,6 @@
import io.github.ran.jarmerger.JarMergerPlugin import io.github.ran.jarmerger.JarMergerPlugin
buildscript { buildscript {
dependencies{ dependencies{
classpath files('plugins/DHJarMerger-1.0.jar') classpath files('plugins/DHJarMerger-1.0.jar')
@@ -11,6 +12,31 @@ plugins {
id "dev.architectury.loom" version "0.10.0-SNAPSHOT" apply false id "dev.architectury.loom" version "0.10.0-SNAPSHOT" apply false
} }
def writeBuildGradlePredefine() {
def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
new File(projectDir, "build.properties").text = "MC_VERSION_${excapedMCVersion}=\n"
}
def loadProperties() {
def defaultMcVersion = '1.18.2'
if (!project.hasProperty("mcVer")) {
println "No mcVer set! Defaulting to ${defaultMcVersion}."
println "Tip: Use -PmcVer='${defaultMcVersion}' in cmd arg to set mcVer."
}
def mcVersion = project.hasProperty("mcVer") ? mcVer : defaultMcVersion
println "Loading properties file at " + mcVersion + ".properties"
def props = new Properties()
props.load(new FileInputStream("$rootProject.rootDir/"+"$mcVersion"+".properties"))
props.each { prop ->
rootProject.ext.set(prop.key, prop.value)
// println "Added prop [key:" + prop.key + ", value:" + prop.value + "]"
}
writeBuildGradlePredefine()
}
loadProperties()
apply plugin: JarMergerPlugin apply plugin: JarMergerPlugin
architectury { architectury {
@@ -35,6 +61,9 @@ subprojects { p ->
// The following line declares the mojmap mappings // The following line declares the mojmap mappings
mappings loom.officialMojangMappings() mappings loom.officialMojangMappings()
//Manifold
annotationProcessor "systems.manifold:manifold-preprocessor:${rootProject.manifold_version}"
// Toml // Toml
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}") implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
@@ -44,12 +73,19 @@ subprojects { p ->
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
} }
if (p != project(":core")) { if (p != project(":core")) {
common(project(":core")) { transitive false } common(project(":core")) { transitive false }
shadowMe(project(":core")) { transitive false } shadowMe(project(":core")) { transitive false }
} }
} }
jar {
manifest {
attributes 'Implementation-Title': rootProject.archives_base_name,
'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.lod.core.JarMain'
}
}
} }
allprojects { p -> allprojects { p ->
@@ -66,6 +102,9 @@ allprojects { p ->
// used to download and compile dependencies from git repos // used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
// For Manifold Preprocessor
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Required for importing Modrinth mods // Required for importing Modrinth mods
maven { maven {
name = "Modrinth" name = "Modrinth"
@@ -110,8 +149,11 @@ allprojects { p ->
description: mod_description, description: mod_description,
homepage: mod_homepage, homepage: mod_homepage,
source: mod_source, source: mod_source,
issues: mod_issues issues: mod_issues,
minecraft_version: minecraft_version,
java_version: java_version
] // The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties ] // The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,41)"
inputs.properties replaceProperties inputs.properties replaceProperties
replaceProperties.put 'project', project replaceProperties.put 'project', project
@@ -134,9 +176,15 @@ allprojects { p ->
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
// Add Manifold Preprocessor
// def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
// options.compilerArgs += ['-Xplugin:Manifold', "-AMC_VERSION_${excapedMCVersion}"]
//
options.compilerArgs += ['-Xplugin:Manifold']
// println options.compilerArgs
if (p != project(":core")) { if (p != project(":core")) {
// Minecraft 1.18 (1.18-pre2) upwards uses Java 17. options.release = rootProject.java_version as Integer
options.release = 17
} }
} }
+1
View File
@@ -0,0 +1 @@
MC_VERSION_1_18_2=
@@ -19,21 +19,25 @@
package com.seibel.lod.common; package com.seibel.lod.common;
import com.seibel.lod.common.wrappers.config.ConfigGui;
import com.seibel.lod.core.config.*; import com.seibel.lod.core.config.*;
import com.seibel.lod.core.enums.config.*; import com.seibel.lod.core.enums.config.*;
import com.seibel.lod.core.enums.rendering.*; import com.seibel.lod.core.enums.rendering.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.*; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.*; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IFogQuality.IAdvancedFog;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IFogQuality.IAdvancedFog.IHeightFog;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IMultiplayer;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IWorldGenerator; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IWorldGenerator;
/** /**
* This handles any configuration the user has access to. * This handles any configuration the user has access to.
* @author coolGi2007 * @author coolGi2007
* @version 12-12-2021 * @version 12-12-2021
*/ */
public class OldConfig
public class Config
//public class Config extends TinyConfig //public class Config extends TinyConfig
{ {
// CONFIG STRUCTURE // CONFIG STRUCTURE
@@ -72,6 +76,9 @@ public class OldConfig
@ConfigAnnotations.ScreenEntry @ConfigAnnotations.ScreenEntry
public static WorldGenerator worldGenerator; public static WorldGenerator worldGenerator;
@ConfigAnnotations.ScreenEntry
public static Multiplayer multiplayer;
@ConfigAnnotations.ScreenEntry @ConfigAnnotations.ScreenEntry
public static Advanced advanced; public static Advanced advanced;
@@ -119,6 +126,11 @@ public class OldConfig
public static String _dropoffQuality = IQuality.DROPOFF_QUALITY_DESC; public static String _dropoffQuality = IQuality.DROPOFF_QUALITY_DESC;
@ConfigAnnotations.Entry @ConfigAnnotations.Entry
public static DropoffQuality dropoffQuality = IQuality.DROPOFF_QUALITY_DEFAULT; public static DropoffQuality dropoffQuality = IQuality.DROPOFF_QUALITY_DEFAULT;
@ConfigAnnotations.FileComment
public static String _lodBiomeBlending = IQuality.LOD_BIOME_BLENDING_DESC;
@ConfigAnnotations.Entry(minValue = 0, maxValue = 7)
public static int lodBiomeBlending = IQuality.LOD_BIOME_BLENDING_MIN_DEFAULT_MAX.defaultValue;
} }
@@ -143,6 +155,94 @@ public class OldConfig
public static String _disableVanillaFog = IFogQuality.DISABLE_VANILLA_FOG_DESC; public static String _disableVanillaFog = IFogQuality.DISABLE_VANILLA_FOG_DESC;
@ConfigAnnotations.Entry @ConfigAnnotations.Entry
public static boolean disableVanillaFog = IFogQuality.DISABLE_VANILLA_FOG_DEFAULT; public static boolean disableVanillaFog = IFogQuality.DISABLE_VANILLA_FOG_DEFAULT;
@ConfigAnnotations.ScreenEntry
public static AdvancedFog advancedFog;
public static class AdvancedFog {
static final double SQRT2 = 1.4142135623730951;
@ConfigAnnotations.FileComment
public static String _farFogStart = IAdvancedFog.FAR_FOG_START_DESC;
@ConfigAnnotations.Entry(minValue = 0.0, maxValue = SQRT2)
public static double farFogStart = IAdvancedFog.FAR_FOG_START_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _farFogEnd = IAdvancedFog.FAR_FOG_END_DESC;
@ConfigAnnotations.Entry(minValue = 0.0, maxValue = SQRT2)
public static double farFogEnd = IAdvancedFog.FAR_FOG_END_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _farFogMin = IAdvancedFog.FAR_FOG_MIN_DESC;
@ConfigAnnotations.Entry(minValue = -5.0, maxValue = SQRT2)
public static double farFogMin = IAdvancedFog.FAR_FOG_MIN_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _farFogMax = IAdvancedFog.FAR_FOG_MAX_DESC;
@ConfigAnnotations.Entry(minValue = 0.0, maxValue = 5.0)
public static double farFogMax = IAdvancedFog.FAR_FOG_MAX_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _farFogType = IAdvancedFog.FAR_FOG_TYPE_DESC;
@ConfigAnnotations.Entry
public static FogSetting.FogType farFogType = IAdvancedFog.FAR_FOG_TYPE_DEFAULT;
@ConfigAnnotations.FileComment
public static String _farFogDensity = IAdvancedFog.FAR_FOG_DENSITY_DESC;
@ConfigAnnotations.Entry(minValue = 0.01, maxValue = 50.0)
public static double farFogDensity = IAdvancedFog.FAR_FOG_DENSITY_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.ScreenEntry
public static HeightFog heightFog;
public static class HeightFog {
@ConfigAnnotations.FileComment
public static String _heightFogMixMode = IHeightFog.HEIGHT_FOG_MIX_MODE_DESC;
@ConfigAnnotations.Entry
public static HeightFogMixMode heightFogMixMode = IHeightFog.HEIGHT_FOG_MIX_MODE_DEFAULT;
@ConfigAnnotations.FileComment
public static String _heightFogMode = IHeightFog.HEIGHT_FOG_MODE_DESC;
@ConfigAnnotations.Entry
public static HeightFogMode heightFogMode = IHeightFog.HEIGHT_FOG_MODE_DEFAULT;
@ConfigAnnotations.FileComment
public static String _heightFogHeight = IHeightFog.HEIGHT_FOG_HEIGHT_DESC;
@ConfigAnnotations.Entry(minValue = -4096.0, maxValue = 4096.0)
public static double heightFogHeight = IHeightFog.HEIGHT_FOG_HEIGHT_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _heightFogStart = IHeightFog.HEIGHT_FOG_START_DESC;
@ConfigAnnotations.Entry(minValue = 0.0, maxValue = SQRT2)
public static double heightFogStart = IHeightFog.HEIGHT_FOG_START_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _heightFogEnd = IHeightFog.HEIGHT_FOG_END_DESC;
@ConfigAnnotations.Entry(minValue = 0.0, maxValue = SQRT2)
public static double heightFogEnd = IHeightFog.HEIGHT_FOG_END_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _heightFogMin = IHeightFog.HEIGHT_FOG_MIN_DESC;
@ConfigAnnotations.Entry(minValue = -5.0, maxValue = SQRT2)
public static double heightFogMin = IHeightFog.HEIGHT_FOG_MIN_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _heightFogMax = IHeightFog.HEIGHT_FOG_MAX_DESC;
@ConfigAnnotations.Entry(minValue = 0.0, maxValue = 5.0)
public static double heightFogMax = IHeightFog.HEIGHT_FOG_MAX_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
public static String _heightFogType = IHeightFog.HEIGHT_FOG_TYPE_DESC;
@ConfigAnnotations.Entry
public static FogSetting.FogType heightFogType = IHeightFog.HEIGHT_FOG_TYPE_DEFAULT;
@ConfigAnnotations.FileComment
public static String _heightFogDensity = IHeightFog.HEIGHT_FOG_DENSITY_DESC;
@ConfigAnnotations.Entry(minValue = 0.01, maxValue = 50.0)
public static double heightFogDensity = IHeightFog.HEIGHT_FOG_DENSITY_MIN_DEFAULT_MAX.defaultValue;
}
}
} }
@@ -163,6 +263,16 @@ public class OldConfig
@ConfigAnnotations.Entry @ConfigAnnotations.Entry
public static boolean useExtendedNearClipPlane = IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT; public static boolean useExtendedNearClipPlane = IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT;
@ConfigAnnotations.FileComment
public static String _brightnessMultiplier = IAdvancedGraphics.BRIGHTNESS_MULTIPLIER_DESC;
@ConfigAnnotations.Entry
public static double brightnessMultiplier = IAdvancedGraphics.BRIGHTNESS_MULTIPLIER_DEFAULT;
@ConfigAnnotations.FileComment
public static String _saturationMultiplier = IAdvancedGraphics.SATURATION_MULTIPLIER_DESC;
@ConfigAnnotations.Entry
public static double saturationMultiplier = IAdvancedGraphics.SATURATION_MULTIPLIER_DEFAULT;
/* /*
@ConfigAnnotations.FileComment @ConfigAnnotations.FileComment
public static String _backsideCullingRange = IAdvancedGraphics.VANILLA_CULLING_RANGE_DESC; public static String _backsideCullingRange = IAdvancedGraphics.VANILLA_CULLING_RANGE_DESC;
@@ -209,6 +319,22 @@ public class OldConfig
public static BlocksToAvoid blocksToAvoid = IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT; public static BlocksToAvoid blocksToAvoid = IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT;
} }
public static class Multiplayer
{
@ConfigAnnotations.FileComment
public static String _serverFolderNameMode = IMultiplayer.SERVER_FOLDER_NAME_MODE_DESC;
@ConfigAnnotations.Entry
public static ServerFolderNameMode serverFolderNameMode = IMultiplayer.SERVER_FOLDER_NAME_MODE_DEFAULT;
@ConfigAnnotations.FileComment
public static String _multiDimensionRequiredSimilarity = IMultiplayer.MULTI_DIMENSION_REQUIRED_SIMILARITY_DESC;
@ConfigAnnotations.Entry
public static double multiDimensionRequiredSimilarity = IMultiplayer.MULTI_DIMENSION_REQUIRED_SIMILARITY_DEFAULT;
}
public static class Advanced public static class Advanced
{ {
@ConfigAnnotations.ScreenEntry @ConfigAnnotations.ScreenEntry
@@ -4,8 +4,6 @@ import com.seibel.lod.common.forge.LodForgeMethodCaller;
import com.seibel.lod.common.networking.NetworkInterface; import com.seibel.lod.common.networking.NetworkInterface;
import com.seibel.lod.common.wrappers.DependencySetup; import com.seibel.lod.common.wrappers.DependencySetup;
import com.seibel.lod.common.wrappers.config.ConfigGui; import com.seibel.lod.common.wrappers.config.ConfigGui;
import com.seibel.lod.core.Config;
import com.seibel.lod.core.config.ConfigBase;
/** /**
* This is the common main class * This is the common main class
@@ -17,9 +15,7 @@ public class LodCommonMain {
public static LodForgeMethodCaller forgeMethodCaller; public static LodForgeMethodCaller forgeMethodCaller;
public static NetworkInterface networkInterface; public static NetworkInterface networkInterface;
public static final boolean IsNewConfig = false; public static void startup(LodForgeMethodCaller caller, boolean serverSided, NetworkInterface networkInterface) {
public static void startup(LodForgeMethodCaller caller, boolean serverSided) {
LodCommonMain.serverSided = serverSided; LodCommonMain.serverSided = serverSided;
if (caller != null) { if (caller != null) {
LodCommonMain.forge = true; LodCommonMain.forge = true;
@@ -27,15 +23,17 @@ public class LodCommonMain {
} }
DependencySetup.createInitialBindings(); DependencySetup.createInitialBindings();
LodCommonMain.networkInterface = networkInterface;
if (!serverSided) {
networkInterface.register_Client();
} else {
networkInterface.register_Server();
}
} }
public static void initConfig() { public static void initConfig() {
if (!IsNewConfig) ConfigGui.init(Config.class);
ConfigGui.init(OldConfig.class);
}
public static void registerNetworking(NetworkInterface networkInterface) {
LodCommonMain.networkInterface = networkInterface;
} }
} }
@@ -1,6 +1,6 @@
package com.seibel.lod.common.forge; package com.seibel.lod.common.forge;
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper; import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@@ -15,5 +15,5 @@ import java.util.Random;
* @author Ran * @author Ran
*/ */
public interface LodForgeMethodCaller { public interface LodForgeMethodCaller {
List<BakedQuad> getQuads(MinecraftWrapper mc, Block block, BlockState blockState, Direction direction, Random random); List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random);
} }
@@ -0,0 +1,30 @@
package com.seibel.lod.common.networking;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
/**
* This is packet handler for our mod
* It basically handles the packets sent from the server & client
*
* @author Ran
*/
public class NetworkHandler {
// If you need the response sender then tell me
// I'll add extra code to get the response sender
public static void receivePacketClient(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf) {
// TODO: Server sided stuff here
// You can make client execute something by using client.execute(Runnable)
// In the fabric docs it says that client.execute is ran on the render thread?
}
// If you need the response sender then tell me
// I'll add extra code to get the response sender
public static void receivePacketServer(MinecraftServer server, ServerPlayer client, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf) {
// TODO: Server sided stuff here
}
}
@@ -1,12 +1,9 @@
package com.seibel.lod.common.networking; package com.seibel.lod.common.networking;
import net.minecraft.network.FriendlyByteBuf;
/** /**
* @author Ran * @author Ran
*/ */
public interface NetworkInterface { public interface NetworkInterface {
void send(FriendlyByteBuf packetByteBuf); void register_Client();
void register_Server();
FriendlyByteBuf receive();
} }
@@ -1,20 +1,100 @@
package com.seibel.lod.common.networking; package com.seibel.lod.common.networking;
import com.seibel.lod.core.ModInfo;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import java.util.Objects;
/** /**
* This class holds most of the networking code for the mod. * This class holds most of the networking code for the mod.
* @author Ran * @author Ran
*/ */
public class Networking { public class Networking {
// public void example(int packetId) { public static final ResourceLocation resourceLocation_meow = new ResourceLocation("lod", "meow");
// FriendlyByteBuf packetByteBuf = Networking.createNew();
// packetByteBuf.writeInt(packetId);
// LodCommonMain.networkInterface.send(packetByteBuf);
// }
public static FriendlyByteBuf createNew() { public static FriendlyByteBuf createNew() {
return new FriendlyByteBuf(Unpooled.buffer()); // TODO: Probably replace the Unpooled.buffer()
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeInt(ModInfo.PROTOCOL_VERSION);
return buf;
} }
/*
* All code below is from the fabric api and might have been modified to work with Distant Horizons
* Which is licensed under the Apache License 2.0
*/
/**
* Sends a packet to a player.
*
* @param player the player to send the packet to
* @param buf the payload of the packet.
*/
public static void send(ServerPlayer player, FriendlyByteBuf buf) {
Objects.requireNonNull(player, "Server player entity cannot be null");
Objects.requireNonNull(resourceLocation_meow, "Channel name cannot be null");
Objects.requireNonNull(buf, "Packet byte buf cannot be null");
player.connection.send(createS2CPacket(resourceLocation_meow, buf));
}
/**
* Sends a packet to the connected server.
*
* @param buf the payload of the packet
* @throws IllegalStateException if the client is not connected to a server
*/
public static void send(FriendlyByteBuf buf) throws IllegalStateException {
// You cant send without a client player, so this is fine
if (Minecraft.getInstance().getConnection() != null) {
Minecraft.getInstance().getConnection().send(createC2SPacket(resourceLocation_meow, buf));
return;
}
throw new IllegalStateException("Cannot send packets when not in game!");
}
/**
* Creates a packet which may be sent to the connected client.
*
* @param channelName the channel name
* @param buf the packet byte buf which represents the payload of the packet
* @return a new packet
*/
public static Packet<?> createS2CPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
Objects.requireNonNull(channelName, "Channel cannot be null");
Objects.requireNonNull(buf, "Buf cannot be null");
return createPlayS2CPacket(channelName, buf);
}
public static Packet<?> createPlayS2CPacket(ResourceLocation channel, FriendlyByteBuf buf) {
return new ClientboundCustomPayloadPacket(channel, buf);
}
/**
* Creates a packet which may be sent to a the connected server.
*
* @param channelName the channel name
* @param buf the packet byte buf which represents the payload of the packet
* @return a new packet
*/
public static Packet<?> createC2SPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
Objects.requireNonNull(channelName, "Channel name cannot be null");
Objects.requireNonNull(buf, "Buf cannot be null");
return createPlayC2SPacket(channelName, buf);
}
public static Packet<?> createPlayC2SPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
return new ServerboundCustomPayloadPacket(channelName, buf);
}
} }
@@ -1,17 +1,15 @@
package com.seibel.lod.common.wrappers; package com.seibel.lod.common.wrappers;
import com.seibel.lod.common.LodCommonMain; import com.seibel.lod.common.LodCommonMain;
import com.seibel.lod.common.wrappers.block.BlockColorSingletonWrapper; import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.lod.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
import com.seibel.lod.core.handlers.IReflectionHandler; import com.seibel.lod.core.handlers.IReflectionHandler;
import com.seibel.lod.core.handlers.ReflectionHandler; import com.seibel.lod.core.handlers.ReflectionHandler;
import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants; import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
/** /**
* Binds all necessary dependencies, so we * Binds all necessary dependencies, so we
@@ -24,14 +22,15 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
* @version 12-1-2021 * @version 12-1-2021
*/ */
public class DependencySetup { public class DependencySetup {
public static void createInitialBindings() { public static void createInitialBindings()
SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE); {
if (!LodCommonMain.serverSided) {
SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions()));
}
SingletonHandler.bind(IVersionConstants.class, VersionConstants.INSTANCE); SingletonHandler.bind(IVersionConstants.class, VersionConstants.INSTANCE);
if (!LodCommonMain.serverSided)
{
SingletonHandler.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftClientWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftClientWrapper.INSTANCE.getOptions()));
}
SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
DependencySetupDoneCheck.isDone = true; DependencySetupDoneCheck.isDone = true;
@@ -22,9 +22,12 @@ package com.seibel.lod.common.wrappers;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import com.seibel.lod.common.wrappers.block.BlockPosWrapper;
import com.seibel.lod.core.enums.LodDirection; import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.objects.math.Mat4f; import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
/** /**
@@ -47,8 +50,30 @@ public class McObjectConverter
} }
static final Direction[] directions;
static final LodDirection[] lodDirections;
static {
LodDirection[] lodDirs = LodDirection.values();
directions = new Direction[lodDirs.length];
lodDirections = new LodDirection[lodDirs.length];
for (LodDirection lodDir : lodDirs) {
Direction dir = Direction.byName(lodDir.name());
directions[lodDir.ordinal()] = dir;
lodDirections[dir.ordinal()] = lodDir;
}
}
public static BlockPos Convert(AbstractBlockPosWrapper wrappedPos) {
return new BlockPos(wrappedPos.getX(),wrappedPos.getY(), wrappedPos.getZ());
}
public static Direction Convert(LodDirection lodDirection) public static Direction Convert(LodDirection lodDirection)
{ {
return Direction.byName(lodDirection.name()); return directions[lodDirection.ordinal()];
}
public static LodDirection Convert(Direction direction)
{
return lodDirections[direction.ordinal()];
} }
} }
@@ -25,37 +25,15 @@ public class VersionConstants implements IVersionConstants
return 0; return 0;
} }
@Override
public boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode)
{
// We are always asking the server to generate the chunk,
// so no use running this stuff multithreaded.
return true;
/*
switch (distanceGenerationMode) {
default:
case NONE:
case BIOME_ONLY:
case BIOME_ONLY_SIMULATE_HEIGHT:
case SURFACE:
case FEATURES:
return false;
case FULL:
return true;
}*/
}
@Override @Override
public int getWorldGenerationCountPerThread() public int getWorldGenerationCountPerThread()
{ {
return 1; return 1;
} }
@Override @Override
public boolean hasBatchGenerationImplementation() public boolean isVanillaRenderedChunkSquare()
{ {
return true; return false;
} }
} }
@@ -26,11 +26,9 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper; import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
import com.seibel.lod.common.wrappers.block.BlockPosWrapper; import com.seibel.lod.common.wrappers.block.BlockPosWrapper;
import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper; import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.WorldGeneratorWrapper;
/** /**
* This handles creating abstract wrapper objects. * This handles creating abstract wrapper objects.
@@ -88,14 +86,6 @@ public class WrapperFactory implements IWrapperFactory
return new ChunkPosWrapper(blockPos); return new ChunkPosWrapper(blockPos);
} }
@Override
public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper);
}
public AbstractBatchGenerationEnvionmentWrapper createBatchGenerator(LodBuilder newLodBuilder, public AbstractBatchGenerationEnvionmentWrapper createBatchGenerator(LodBuilder newLodBuilder,
LodDimension newLodDimension, IWorldWrapper worldWrapper) LodDimension newLodDimension, IWorldWrapper worldWrapper)
{ {
@@ -1,46 +0,0 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.common.wrappers.block;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import net.minecraft.world.level.block.Blocks;
/**
* Contains methods that would have been static in BlockColorWrapper.
* Since interfaces can't create/implement static methods we have
* to split the object up in two.
*
* @author James Seibel
* @version 11-17-2021
*/
public class BlockColorSingletonWrapper implements IBlockColorSingletonWrapper
{
public static final BlockColorSingletonWrapper INSTANCE = new BlockColorSingletonWrapper();
@Override
public IBlockColorWrapper getWaterColor()
{
return BlockColorWrapper.getBlockColorWrapper(Blocks.WATER);
}
}
@@ -1,317 +0,0 @@
package com.seibel.lod.common.wrappers.block;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
/**
* @author James Seibel
* @version 11-21-2021
*/
public class BlockColorWrapper implements IBlockColorWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
// public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static final AbstractBlockPosWrapper blockPos = new BlockPosWrapper(0, 0, 0);
public static final Random random = new Random(0);
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
private final Block block;
private int color;
private boolean isColored;
private boolean toTint;
private boolean foliageTint;
private boolean grassTint;
private boolean waterTint;
/**Constructor only require for the block instance we are wrapping**/
public BlockColorWrapper(Block block)
{
this.block = block;
this.color = 0;
this.isColored = true;
this.toTint = false;
this.foliageTint = false;
this.grassTint = false;
this.waterTint = false;
setupColorAndTint();
/*StringBuilder s = new StringBuilder();
s.append(block + "\n"
+ Integer.toHexString(
Minecraft.getInstance().getBlockColors().createDefault().getColor(
block.defaultBlockState(),
(World) MinecraftWrapper.INSTANCE.getWrappedServerLevel().getLevel(),
blockPosWrapper.getBlockPos())) + "\n"
);
for(Property x : Minecraft.getInstance().getBlockColors().getColoringProperties(block))
s.append(x.getName() + " " + x.getPossibleValues() + '\n');
System.out.println(s);*/
//System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint);
}
/**
* this return a wrapper of the block in input
* @param block object of the block to wrap
*/
public static IBlockColorWrapper getBlockColorWrapper(Block block)
{
//first we check if the block has already been wrapped
BlockColorWrapper colorWrapper = blockColorWrapperMap.get(block);
if (colorWrapper != null)
return colorWrapper;
//if it hasn't been created yet, we create it and save it in the map
colorWrapper = new BlockColorWrapper(block);
BlockColorWrapper colorWrapperCAS = blockColorWrapperMap.putIfAbsent(block, colorWrapper);
//we return the newly created wrapper
return colorWrapperCAS==null ? colorWrapper : colorWrapperCAS;
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint()
{
BlockState blockState = block.defaultBlockState();
//BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
//boolean isTinted = false;
//int listSize = 0;
// first step is to check if this block has a tinted face
//for (Direction direction : directions)
//{
// quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
// listSize = Math.max(listSize, quads.size());
// for (BakedQuad bakedQuad : quads)
// {
// isTinted |= bakedQuad.isTinted();
// }
//}
//if it contains a tinted face then we store this block in the toTint set
//if (isTinted)
// this.toTint = true;
//now we get the first non-empty face
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
if (!quads.isEmpty() && !(block instanceof RotatedPillarBlock && direction == Direction.UP))
break;
}
//the quads list is not empty we extract the first one
if (!quads.isEmpty())
{
isColored = true;
texture = quads.get(0).getSprite();
}
else
{
isColored = true;
texture = mc.getModelManager().getBlockModelShaper().getParticleIcon(block.defaultBlockState());
}
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int tempColor;
int colorMultiplier;
// generate the block's color
// for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
boolean lookForTint = grassInstance() || leavesInstance() || waterInstance();
int frameIndex = 0; // TODO
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getWidth(); u++)
{
for (int v = 0; v < texture.getHeight(); v++)
{
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v);
if (ColorUtil.getAlpha(TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v)) == 0)
continue;
if (lookForTint)
{
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
int colorMin = 16 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
}
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(tempColor) > (ColorUtil.getBlue(tempColor) + 30)) || !(ColorUtil.getGreen(tempColor) > (ColorUtil.getRed(tempColor) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(tempColor) * ColorUtil.getAlpha(tempColor) * colorMultiplier;
red += ColorUtil.getBlue(tempColor) * ColorUtil.getBlue(tempColor) * colorMultiplier;
green += ColorUtil.getGreen(tempColor) * ColorUtil.getGreen(tempColor) * colorMultiplier;
blue += ColorUtil.getRed(tempColor) * ColorUtil.getRed(tempColor) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = 0;
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
(int) Math.sqrt(alpha / count),
(int) Math.sqrt(red / count),
(int) Math.sqrt(green / count),
(int) Math.sqrt(blue / count));
}
// determine if this block should use the biome color tint
if (lookForTint && (float) numberOfGreyPixel / count > 0.75f)
this.toTint = true;
// we check which kind of tint we need to apply
this.grassTint = grassInstance() && toTint;
this.foliageTint = leavesInstance() && toTint;
this.waterTint = waterInstance();
//hardcoded leaves
if (block == Blocks.SPRUCE_LEAVES)
color = ColorUtil.multiplyRGBcolors(tempColor, 0xFF619961);
else if (block == Blocks.BIRCH_LEAVES)
color = ColorUtil.multiplyRGBcolors(tempColor, 0xFF80A755);
else
color = tempColor;
}
/** determine if the given block should use the biome's grass color */
private boolean grassInstance()
{
return block instanceof GrassBlock
|| block instanceof BushBlock
// || block instanceof IGrowable
// || block instanceof AbstractPlantBlock
// || block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean leavesInstance()
{
return (block instanceof LeavesBlock && block != Blocks.SPRUCE_LEAVES && block != Blocks.BIRCH_LEAVES/* && block != Blocks.AZALEA_LEAVES && block != Blocks.FLOWERING_AZALEA_LEAVES*/)
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's foliage color */
private boolean waterInstance()
{
return block == Blocks.WATER;
}
@Override
public String getName(){
return block.getName().toString();
}
//--------------//
//Colors getters//
//--------------//
@Override
public boolean hasColor()
{
return isColored;
}
@Override
public int getColor()
{
return color;
}
//------------//
//Tint getters//
//------------//
@Override
public boolean hasTint()
{
return toTint;
}
@Override
public boolean hasGrassTint()
{
return grassTint;
}
@Override
public boolean hasFolliageTint()
{
return foliageTint;
}
@Override
public boolean hasWaterTint()
{
return waterTint;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockColorWrapper))
return false;
BlockColorWrapper that = (BlockColorWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,27 @@
package com.seibel.lod.common.wrappers.block;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.BlockState;
public class BlockDetailMap
{
private static ConcurrentHashMap<BlockState, BlockDetailWrapper> map = new ConcurrentHashMap<BlockState, BlockDetailWrapper>();
private BlockDetailMap() {}
public static BlockDetailWrapper getOrMakeBlockDetailCache(BlockState bs, BlockPos pos, LevelReader getter) {
BlockDetailWrapper cache = map.get(bs);
if (cache != null) return cache;
if (bs.getFluidState().isEmpty()) {
cache = BlockDetailWrapper.make(bs, pos, getter);
} else {
cache = BlockDetailWrapper.make(bs.getFluidState().createLegacyBlock(), pos, getter);
}
BlockDetailWrapper cacheCAS = map.putIfAbsent(bs, cache);
return cacheCAS==null ? cache : cacheCAS;
}
}
@@ -0,0 +1,315 @@
package com.seibel.lod.common.wrappers.block;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.seibel.lod.common.Config;
import com.seibel.lod.common.wrappers.McObjectConverter;
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.block.BlockTintCache;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.RotatedPillarBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class BlockDetailWrapper extends IBlockDetailWrapper
{
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
public static final int FLOWER_COLOR_SCALE = 5;
public static final Random random = new Random(0);
enum ColorMode {
Default,
Flower,
Leaves;
static ColorMode getColorMode(Block b) {
if (b instanceof LeavesBlock) return Leaves;
if (b instanceof FlowerBlock) return Flower;
return Default;
}
}
//TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) {
int count = 0;
double alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getWidth(); u++)
{
for (int v = 0; v < texture.getHeight(); v++)
{
//note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB
//OpenGL RGBA format native order: 0xRR GG BB AA
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
double r = ((tempColor & 0x000000FF) )/255.;
double g = ((tempColor & 0x0000FF00) >>> 8)/255.;
double b = ((tempColor & 0x00FF0000) >>> 16)/255.;
double a = ((tempColor & 0xFF000000) >>> 24)/255.;
int scale = 1;
if (colorMode == ColorMode.Leaves) {
r *= a;
g *= a;
b *= a;
a = 1.;
} else if (a==0.) {
continue;
} else if (colorMode == ColorMode.Flower && (g+0.1<b || g+0.1<r)) {
scale = FLOWER_COLOR_SCALE;
}
count += scale;
alpha += a*a*scale;
red += r*r*scale;
green += g*g*scale;
blue += b*b*scale;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = ColorUtil.rgbToInt(255,255,0,255);
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
(int) (Math.sqrt(alpha/count)*255.),
(int) (Math.sqrt(red / count)*255.),
(int) (Math.sqrt(green / count)*255.),
(int) (Math.sqrt(blue / count)*255.));
}
// TODO: Remove this when transparency is added!
double colorAlpha = ColorUtil.getAlpha(tempColor)/255.;
tempColor = ColorUtil.rgbToInt(
ColorUtil.getAlpha(tempColor),
(int)(ColorUtil.getRed(tempColor) * colorAlpha),
(int)(ColorUtil.getGreen(tempColor) * colorAlpha),
(int)(ColorUtil.getBlue(tempColor) * colorAlpha)
);
return tempColor;
}
private static final Block[] BLOCK_TO_AVOID = {Blocks.AIR, Blocks.CAVE_AIR, Blocks.BARRIER};
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
private static boolean isBlockToBeAvoid(Block b) {
for (Block bta : BLOCK_TO_AVOID)
if (bta==b) return true;
return false;
}
final BlockState state;
final BlockPos samplePos;
final LevelReader sampleGetter;
boolean isShapeResolved = false;
boolean[] dontOccludeFaces = null;
boolean noCollision = false;
boolean noFullFace = false;
boolean isColorResolved = false;
int baseColor = 0; //TODO: Impl per-face color
boolean needShade = true;
boolean needPostTinting = false;
int tintIndex = 0;
public static BlockDetailWrapper NULL_BLOCK_DETAIL = new BlockDetailWrapper();
public BlockDetailWrapper(BlockState state, BlockPos pos, LevelReader getter) {
this.state = state;
this.samplePos = pos;
this.sampleGetter = getter;
}
private BlockDetailWrapper() {
this.state = null;
this.samplePos = null;
this.sampleGetter = null;
}
static BlockDetailWrapper make(BlockState bs, BlockPos pos, LevelReader getter) {
if(!bs.getFluidState().isEmpty()) { // Is a fluidBlock
if (isBlockToBeAvoid(bs.getBlock())) return NULL_BLOCK_DETAIL;
if (bs.isAir()) return NULL_BLOCK_DETAIL;
return new BlockDetailWrapper(bs, pos, getter);
} else {
if (bs.getRenderShape() != RenderShape.MODEL) return NULL_BLOCK_DETAIL;
if (isBlockToBeAvoid(bs.getBlock())) return NULL_BLOCK_DETAIL;
return new BlockDetailWrapper(bs, pos, getter);
}
}
private void resolveShapes() {
if (isShapeResolved) return;
if (state.getFluidState().isEmpty()) {
noCollision = state.getCollisionShape(sampleGetter, samplePos).isEmpty();
dontOccludeFaces = new boolean[6];
if (state.canOcclude()) {
/* FIXME: Figure out how or if needed to impl per-face culling?
for (Direction dir : Direction.values()) {
dontOccludeFaces[McObjectConverter.Convert(dir).ordinal()]
= state.getFaceOcclusionShape(sampleGetter, samplePos, dir).isEmpty();
}*/
} else {
Arrays.fill(dontOccludeFaces, true);
}
VoxelShape voxelShape = state.getShape(sampleGetter, samplePos);
if (voxelShape.isEmpty()) {
noFullFace = true;
} else {
AABB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
noFullFace = xWidth < 1 && zWidth < 1 && yWidth < 1;
}
} else { // Liquid Block
dontOccludeFaces = new boolean[6];
}
isShapeResolved = true;
}
private void resolveColors() {
if (isColorResolved) return;
if (state.getFluidState().isEmpty()) {
List<BakedQuad> quads = null;
for (Direction direction : DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(state).getQuads(state, direction, random);
if (!quads.isEmpty() &&
!(state.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
break;
};
if (quads != null && !quads.isEmpty()) {
needPostTinting = quads.get(0).isTinted();
needShade = quads.get(0).isShade();
tintIndex = quads.get(0).getTintIndex();
baseColor = calculateColorFromTexture(quads.get(0).getSprite(),
ColorMode.getColorMode(state.getBlock()));
}
} else { // Liquid Block
needPostTinting = true;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state),
ColorMode.getColorMode(state.getBlock()));
}
isColorResolved = true;
}
private BlockAndTintGetter wrapColorResolver(LevelReader level) {
int blendDistance = CONFIG.client().graphics().quality().getLodBiomeBlending();
if (blendDistance == 0) {
return new TintGetterOverrideFast(level);
} else {
return new TintGetterOverrideSmooth(level, blendDistance);
}
}
@Override
public int getAndResolveFaceColor(LodDirection dir, IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
{
// FIXME: impl per-face colors
resolveColors();
if (!needPostTinting) return baseColor;
int tintColor = Minecraft.getInstance().getBlockColors()
.getColor(state, wrapColorResolver(((ChunkWrapper)chunk).getColorResolver()),
McObjectConverter.Convert(blockPos), tintIndex);
if (tintColor == -1) return baseColor;
return ColorUtil.multiplyARGBwithRGB(baseColor, tintColor);
}
@Override
public boolean hasFaceCullingFor(LodDirection dir)
{
resolveShapes();
return !dontOccludeFaces[dir.ordinal()];
}
@Override
public boolean hasNoCollision()
{
resolveShapes();
return noCollision;
}
@Override
public boolean noFaceIsFullFace()
{
resolveShapes();
return noFullFace;
}
@Override
public String serialize()
{
// FIXME: Impl this for the blockState Storage stuff
return null;
}
@Override
protected boolean isSame(IBlockDetailWrapper iBlockDetail)
{
return ((BlockDetailWrapper)iBlockDetail).state.getBlock().equals(state.getBlock());
}
}
@@ -1,157 +0,0 @@
package com.seibel.lod.common.wrappers.block;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
/**
* @author James Seibel
* @version 11-21-2021
*/
public class BlockShapeWrapper implements IBlockShapeWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockShapeWrapper> blockShapeWrapperMap = new ConcurrentHashMap<>();
public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper();
private final Block block;
private final boolean toAvoid;
private boolean nonFull;
private boolean noCollision;
/**Constructor only require for the block instance we are wrapping**/
public BlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, int x, int y, int z)
{
this.block = block;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = ofBlockToAvoid();
setupShapes(chunkWrapper, x, y, z);
//System.out.println(block + " non full " + nonFull + " no collision " + noCollision + " to avoid " + toAvoid);
}
private BlockShapeWrapper()
{
this.block = Blocks.WATER;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = false;
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
*/
static public BlockShapeWrapper getBlockShapeWrapper(Block block, ChunkWrapper chunkWrapper, int x, int y, int z)
{
//first we check if the block has already been wrapped
BlockShapeWrapper blockWrapper = blockShapeWrapperMap.get(block);
if (blockWrapper != null)
return blockWrapper;
//if it hasn't been created yet, we create it and save it in the map
blockWrapper = new BlockShapeWrapper(block, chunkWrapper, x, y, z);
BlockShapeWrapper blockWrapperCAS = blockShapeWrapperMap.putIfAbsent(block, blockWrapper);
//we return the newly created wrapper
return blockWrapperCAS==null ? blockWrapper : blockWrapperCAS;
}
private void setupShapes(IChunkWrapper chunkWrapper, int x, int y, int z)
{
ChunkAccess chunk = ((ChunkWrapper) chunkWrapper).getChunk();
BlockPos blockPos = new BlockPos(x, y, z);
boolean noCollisionSetted = false;
boolean nonFullSetted = false;
if (!block.defaultBlockState().getFluidState().isEmpty())// || block instanceof SixWayBlock)
{
noCollisionSetted = true;
nonFullSetted = true;
noCollision = false;
nonFull = false;
}
if (!nonFullSetted)
{
VoxelShape voxelShape = block.defaultBlockState().getShape(chunk, blockPos);
if (!voxelShape.isEmpty())
{
AABB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
nonFull = xWidth < 1 && zWidth < 1 && yWidth < 1;
}
else
{
nonFull = false;
}
}
if (!noCollisionSetted)
{
VoxelShape collisionShape = block.defaultBlockState().getCollisionShape(chunk, blockPos);
noCollision = collisionShape.isEmpty();
}
}
@Override
public boolean ofBlockToAvoid()
{
return block.equals(Blocks.AIR)
|| block.equals(Blocks.CAVE_AIR)
|| block.equals(Blocks.BARRIER);
}
//-----------------//
//Avoidance getters//
//-----------------//
@Override
public boolean isNonFull()
{
return nonFull;
}
@Override
public boolean hasNoCollision()
{
return noCollision;
}
@Override
public boolean isToAvoid()
{
return toAvoid;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockShapeWrapper))
return false;
BlockShapeWrapper that = (BlockShapeWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,196 @@
package com.seibel.lod.common.wrappers.block;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.Util;
import net.minecraft.client.color.block.BlockTintCache;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TintGetterOverrideFast implements BlockAndTintGetter {
LevelReader parent;
private final Object2ObjectArrayMap<ColorResolver, ConcurrentHashMap<Biome, Integer>> tintCaches;
public TintGetterOverrideFast(LevelReader parent) {
this.parent = parent;
this.tintCaches = Util.make(new Object2ObjectArrayMap(3), object2ObjectArrayMap -> {
object2ObjectArrayMap.put(BiomeColors.GRASS_COLOR_RESOLVER, new ConcurrentHashMap<Biome, Integer>());
object2ObjectArrayMap.put(BiomeColors.FOLIAGE_COLOR_RESOLVER, new ConcurrentHashMap<Biome, Integer>());
object2ObjectArrayMap.put(BiomeColors.WATER_COLOR_RESOLVER, new ConcurrentHashMap<Biome, Integer>());
});
}
private Biome _getBiome(BlockPos pos) {
#if MC_VERSION_1_18_2
return parent.getBiome(pos).value();
#elif MC_VERSION_1_18_1
return parent.getBiome(pos);
#endif
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
Biome b = _getBiome(blockPos);
return tintCaches.get(colorResolver).computeIfAbsent(b, (key) -> colorResolver.getColor(b, blockPos.getX(), blockPos.getZ()));
}
@Override
public float getShade(Direction direction, boolean bl) {
return parent.getShade(direction, bl);
}
@Override
public LevelLightEngine getLightEngine() {
return parent.getLightEngine();
}
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) {
return parent.getBrightness(lightLayer, blockPos);
}
@Override
public int getRawBrightness(BlockPos blockPos, int i) {
return parent.getRawBrightness(blockPos, i);
}
@Override
public boolean canSeeSky(BlockPos blockPos) {
return parent.canSeeSky(blockPos);
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) {
return parent.getBlockEntity(blockPos);
}
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) {
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override
public BlockState getBlockState(BlockPos blockPos) {
return parent.getBlockState(blockPos);
}
@Override
public FluidState getFluidState(BlockPos blockPos) {
return parent.getFluidState(blockPos);
}
@Override
public int getLightEmission(BlockPos blockPos) {
return parent.getLightEmission(blockPos);
}
@Override
public int getMaxLightLevel() {
return parent.getMaxLightLevel();
}
@Override
public Stream<BlockState> getBlockStates(AABB aABB) {
return parent.getBlockStates(aABB);
}
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) {
return parent.isBlockInLine(clipBlockStateContext);
}
@Override
public BlockHitResult clip(ClipContext clipContext) {
return parent.clip(clipContext);
}
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState) {
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) {
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override
public double getBlockFloorHeight(BlockPos blockPos) {
return parent.getBlockFloorHeight(blockPos);
}
@Override
public int getHeight() {
return parent.getHeight();
}
@Override
public int getMinBuildHeight() {
return parent.getMinBuildHeight();
}
@Override
public int getMaxBuildHeight() {
return parent.getMaxBuildHeight();
}
@Override
public int getSectionsCount() {
return parent.getSectionsCount();
}
@Override
public int getMinSection() {
return parent.getMinSection();
}
@Override
public int getMaxSection() {
return parent.getMaxSection();
}
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) {
return parent.isOutsideBuildHeight(blockPos);
}
@Override
public boolean isOutsideBuildHeight(int i) {
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i) {
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i) {
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i) {
return parent.getSectionYFromSectionIndex(i);
}
}
@@ -0,0 +1,219 @@
package com.seibel.lod.common.wrappers.block;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.Util;
import net.minecraft.client.color.block.BlockTintCache;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TintGetterOverrideSmooth implements BlockAndTintGetter {
LevelReader parent;
private final Object2ObjectArrayMap<ColorResolver, BlockTintCache> tintCaches;
public int smoothingRange;
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange) {
this.parent = parent;
this.smoothingRange = smoothingRange;
this.tintCaches = Util.make(new Object2ObjectArrayMap(3), object2ObjectArrayMap -> {
object2ObjectArrayMap.put(BiomeColors.GRASS_COLOR_RESOLVER, new BlockTintCache((pos) -> calculateBlockTint(pos, BiomeColors.GRASS_COLOR_RESOLVER)));
object2ObjectArrayMap.put(BiomeColors.FOLIAGE_COLOR_RESOLVER, new BlockTintCache((pos) -> calculateBlockTint(pos, BiomeColors.FOLIAGE_COLOR_RESOLVER)));
object2ObjectArrayMap.put(BiomeColors.WATER_COLOR_RESOLVER, new BlockTintCache((pos) -> calculateBlockTint(pos, BiomeColors.WATER_COLOR_RESOLVER)));
});
}
private Biome _getBiome(BlockPos pos) {
#if MC_VERSION_1_18_2
return parent.getBiome(pos).value();
#elif MC_VERSION_1_18_1
return parent.getBiome(pos);
#endif
}
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
int i = smoothingRange;
if (i == 0)
return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
int j = (i * 2 + 1) * (i * 2 + 1);
int k = 0;
int l = 0;
int m = 0;
Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
while (cursor3D.advance())
{
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
int n = colorResolver.getColor(_getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
k += (n & 0xFF0000) >> 16;
l += (n & 0xFF00) >> 8;
m += n & 0xFF;
}
return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
BlockTintCache blockTintCache = this.tintCaches.get(colorResolver);
return blockTintCache.getColor(blockPos);
}
@Override
public float getShade(Direction direction, boolean bl) {
return parent.getShade(direction, bl);
}
@Override
public LevelLightEngine getLightEngine() {
return parent.getLightEngine();
}
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) {
return parent.getBrightness(lightLayer, blockPos);
}
@Override
public int getRawBrightness(BlockPos blockPos, int i) {
return parent.getRawBrightness(blockPos, i);
}
@Override
public boolean canSeeSky(BlockPos blockPos) {
return parent.canSeeSky(blockPos);
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) {
return parent.getBlockEntity(blockPos);
}
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) {
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override
public BlockState getBlockState(BlockPos blockPos) {
return parent.getBlockState(blockPos);
}
@Override
public FluidState getFluidState(BlockPos blockPos) {
return parent.getFluidState(blockPos);
}
@Override
public int getLightEmission(BlockPos blockPos) {
return parent.getLightEmission(blockPos);
}
@Override
public int getMaxLightLevel() {
return parent.getMaxLightLevel();
}
@Override
public Stream<BlockState> getBlockStates(AABB aABB) {
return parent.getBlockStates(aABB);
}
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) {
return parent.isBlockInLine(clipBlockStateContext);
}
@Override
public BlockHitResult clip(ClipContext clipContext) {
return parent.clip(clipContext);
}
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState) {
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) {
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override
public double getBlockFloorHeight(BlockPos blockPos) {
return parent.getBlockFloorHeight(blockPos);
}
@Override
public int getHeight() {
return parent.getHeight();
}
@Override
public int getMinBuildHeight() {
return parent.getMinBuildHeight();
}
@Override
public int getMaxBuildHeight() {
return parent.getMaxBuildHeight();
}
@Override
public int getSectionsCount() {
return parent.getSectionsCount();
}
@Override
public int getMinSection() {
return parent.getMinSection();
}
@Override
public int getMaxSection() {
return parent.getMaxSection();
}
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) {
return parent.isOutsideBuildHeight(blockPos);
}
@Override
public boolean isOutsideBuildHeight(int i) {
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i) {
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i) {
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i) {
return parent.getSectionYFromSectionIndex(i);
}
}
@@ -1,41 +1,47 @@
package com.seibel.lod.common.wrappers.chunk; package com.seibel.lod.common.wrappers.chunk;
import com.seibel.lod.common.wrappers.block.BlockDetailWrapper;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.util.LevelPosUtil; import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper; import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.common.wrappers.WrapperUtil; import com.seibel.lod.common.wrappers.WrapperUtil;
import com.seibel.lod.common.wrappers.block.BlockColorWrapper; import com.seibel.lod.common.wrappers.block.BlockDetailMap;
import com.seibel.lod.common.wrappers.block.BlockShapeWrapper;
import com.seibel.lod.common.wrappers.world.BiomeWrapper; import com.seibel.lod.common.wrappers.world.BiomeWrapper;
import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightedWorldGenRegion;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.QuartPos; import net.minecraft.core.QuartPos;
import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.AirBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.LiquidBlockContainer; import net.minecraft.world.level.block.LiquidBlockContainer;
import net.minecraft.world.level.block.SimpleWaterloggedBlock; import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
/** /**
* *
* @author James Seibel * @author James Seibel
* @version 11-21-2021 * @version 3-5-2022
*/ */
public class ChunkWrapper implements IChunkWrapper public class ChunkWrapper implements IChunkWrapper
{ {
private final ChunkAccess chunk; private final ChunkAccess chunk;
private final BlockAndTintGetter lightSource; private final LevelReader lightSource;
public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource)
{
this.chunk = chunk;
this.lightSource = lightSource;
}
@Override @Override
public int getHeight(){ public int getHeight(){
@@ -62,36 +68,37 @@ public class ChunkWrapper implements IChunkWrapper
@Override @Override
public IBiomeWrapper getBiome(int x, int y, int z) public IBiomeWrapper getBiome(int x, int y, int z)
{ {
#if MC_VERSION_1_18_2
return BiomeWrapper.getBiomeWrapper(chunk.getNoiseBiome(
QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)).value());
#elif MC_VERSION_1_18_1
return BiomeWrapper.getBiomeWrapper(chunk.getNoiseBiome( return BiomeWrapper.getBiomeWrapper(chunk.getNoiseBiome(
QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z))); QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)));
#endif
} }
@Override @Override
public IBlockColorWrapper getBlockColorWrapper(int x, int y, int z) public IBlockDetailWrapper getBlockDetail(int x, int y, int z) {
{ BlockPos pos = new BlockPos(x,y,z);
BlockState blockState = chunk.getBlockState(new BlockPos(x,y,z)); BlockState blockState = chunk.getBlockState(pos);
Block block = blockState.getBlock(); IBlockDetailWrapper blockDetail = BlockDetailMap.getOrMakeBlockDetailCache(blockState, pos, lightSource);
return BlockColorWrapper.getBlockColorWrapper(block); return blockDetail == BlockDetailWrapper.NULL_BLOCK_DETAIL ? null : blockDetail;
} }
@Override @Override
public IBlockShapeWrapper getBlockShapeWrapper(int x, int y, int z) public IBlockDetailWrapper getBlockDetailAtFace(int x, int y, int z, LodDirection dir) {
{ int fy = y+dir.getNormal().y;
BlockState blockState = chunk.getBlockState(new BlockPos(x,y,z)); if (fy < getMinBuildHeight() || fy > getMaxBuildHeight()) return null;
Block block = blockState.getBlock(); BlockPos pos = new BlockPos(x+dir.getNormal().x,fy,z+dir.getNormal().z);
return BlockShapeWrapper.getBlockShapeWrapper(block, this, x, y, z); BlockState blockState;
if (blockPosInsideChunk(x,y,z))
blockState = chunk.getBlockState(pos);
else {
blockState = lightSource.getBlockState(pos);
} }
if (blockState == null || blockState.isAir()) return null;
@Deprecated IBlockDetailWrapper blockDetail = BlockDetailMap.getOrMakeBlockDetailCache(blockState, pos, lightSource);
public ChunkWrapper(ChunkAccess chunk) return blockDetail == BlockDetailWrapper.NULL_BLOCK_DETAIL ? null : blockDetail;
{
this.chunk = chunk;
this.lightSource = null;
}
public ChunkWrapper(ChunkAccess chunk, BlockAndTintGetter lightSource)
{
this.chunk = chunk;
this.lightSource = lightSource;
} }
public ChunkAccess getChunk() { public ChunkAccess getChunk() {
@@ -181,4 +188,21 @@ public class ChunkWrapper implements IChunkWrapper
return lightSource.getBrightness(LightLayer.SKY, new BlockPos(x,y,z)); return lightSource.getBrightness(LightLayer.SKY, new BlockPos(x,y,z));
} }
@Override
public boolean doesNearbyChunksExist() {
if (lightSource instanceof LightedWorldGenRegion) return true;
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx==0 && dz==0) continue;
if (lightSource.getChunk(dx+getChunkPosX(), dz+getChunkPosZ(), ChunkStatus.BIOMES, false) == null) return false;
}
}
return true;
}
public LevelReader getColorResolver()
{
return lightSource;
}
} }
@@ -92,7 +92,7 @@ public abstract class ConfigGui
// Change these to your own mod // Change these to your own mod
private static final String MOD_NAME = ModInfo.NAME; // For file saving and identifying private static final String MOD_NAME = ModInfo.NAME; // For file saving and identifying
private static final String MOD_NAME_READABLE = ModInfo.READABLE_NAME; // For logs private static final String MOD_NAME_READABLE = ModInfo.READABLE_NAME; // For logs
// private static final Logger LOGGER = ClientApi.LOGGER; // For logs // private static final Logger LOGGER = ApiShared.LOGGER; // For logs
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME); // For logs (this inits before ClientAPI so this is a temp fix) private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME); // For logs (this inits before ClientAPI so this is a temp fix)
@@ -607,6 +607,7 @@ public abstract class ConfigGui
else if (info.screenButton) else if (info.screenButton)
{ {
Button widget = new Button(this.width / 2 - info.width, this.height - 28, info.width * 2, 20, name, (button -> { Button widget = new Button(this.width / 2 - info.width, this.height - 28, info.width * 2, 20, name, (button -> {
saveToFile();
Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, info.gotoScreen)); Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, info.gotoScreen));
})); }));
this.list.addButton(widget, null, null, null); this.list.addButton(widget, null, null, null);
@@ -3,7 +3,7 @@ package com.seibel.lod.common.wrappers.config;
import com.seibel.lod.core.enums.config.*; import com.seibel.lod.core.enums.config.*;
import com.seibel.lod.core.enums.rendering.*; import com.seibel.lod.core.enums.rendering.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.common.OldConfig; import com.seibel.lod.common.Config;
/** /**
* This holds the config defaults and setters/getters * This holds the config defaults and setters/getters
@@ -28,6 +28,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
{ {
public final IGraphics graphics; public final IGraphics graphics;
public final IWorldGenerator worldGenerator; public final IWorldGenerator worldGenerator;
public final IMultiplayer multiplayer;
public final IAdvanced advanced; public final IAdvanced advanced;
@@ -43,6 +44,11 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
return worldGenerator; return worldGenerator;
} }
@Override
public IMultiplayer multiplayer() {
return multiplayer;
}
@Override @Override
public IAdvanced advanced() public IAdvanced advanced()
{ {
@@ -53,7 +59,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public boolean getOptionsButton() public boolean getOptionsButton()
{ {
return OldConfig.optionsButton; return Config.optionsButton;
} }
@Override @Override
public void setOptionsButton(boolean newOptionsButton) public void setOptionsButton(boolean newOptionsButton)
@@ -70,6 +76,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
{ {
graphics = new Graphics(); graphics = new Graphics();
worldGenerator = new WorldGenerator(); worldGenerator = new WorldGenerator();
multiplayer = new Multiplayer();
advanced = new Advanced(); advanced = new Advanced();
} }
@@ -117,7 +124,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public HorizontalResolution getDrawResolution() public HorizontalResolution getDrawResolution()
{ {
return OldConfig.Client.Graphics.Quality.drawResolution; return Config.Client.Graphics.Quality.drawResolution;
} }
@Override @Override
public void setDrawResolution(HorizontalResolution newHorizontalResolution) public void setDrawResolution(HorizontalResolution newHorizontalResolution)
@@ -130,7 +137,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public int getLodChunkRenderDistance() public int getLodChunkRenderDistance()
{ {
return OldConfig.Client.Graphics.Quality.lodChunkRenderDistance; return Config.Client.Graphics.Quality.lodChunkRenderDistance;
} }
@Override @Override
public void setLodChunkRenderDistance(int newLodChunkRenderDistance) public void setLodChunkRenderDistance(int newLodChunkRenderDistance)
@@ -143,7 +150,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public VerticalQuality getVerticalQuality() public VerticalQuality getVerticalQuality()
{ {
return OldConfig.Client.Graphics.Quality.verticalQuality; return Config.Client.Graphics.Quality.verticalQuality;
} }
@Override @Override
public void setVerticalQuality(VerticalQuality newVerticalQuality) public void setVerticalQuality(VerticalQuality newVerticalQuality)
@@ -156,7 +163,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public int getHorizontalScale() public int getHorizontalScale()
{ {
return OldConfig.Client.Graphics.Quality.horizontalScale; return Config.Client.Graphics.Quality.horizontalScale;
} }
@Override @Override
public void setHorizontalScale(int newHorizontalScale) public void setHorizontalScale(int newHorizontalScale)
@@ -169,7 +176,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public HorizontalQuality getHorizontalQuality() public HorizontalQuality getHorizontalQuality()
{ {
return OldConfig.Client.Graphics.Quality.horizontalQuality; return Config.Client.Graphics.Quality.horizontalQuality;
} }
@Override @Override
public void setHorizontalQuality(HorizontalQuality newHorizontalQuality) public void setHorizontalQuality(HorizontalQuality newHorizontalQuality)
@@ -180,22 +187,40 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public DropoffQuality getDropoffQuality() { public DropoffQuality getDropoffQuality() {
return OldConfig.Client.Graphics.Quality.dropoffQuality; return Config.Client.Graphics.Quality.dropoffQuality;
} }
@Override @Override
public void setDropoffQuality(DropoffQuality newDropoffQuality) { public void setDropoffQuality(DropoffQuality newDropoffQuality) {
ConfigGui.editSingleOption.getEntry("client.graphics.quality.dropoffQuality").value = newDropoffQuality; ConfigGui.editSingleOption.getEntry("client.graphics.quality.dropoffQuality").value = newDropoffQuality;
ConfigGui.editSingleOption.saveOption("client.graphics.quality.dropoffQuality"); ConfigGui.editSingleOption.saveOption("client.graphics.quality.dropoffQuality");
} }
@Override
public int getLodBiomeBlending() {
return Config.Client.Graphics.Quality.lodBiomeBlending;
}
@Override
public void setLodBiomeBlending(int newLodBiomeBlending) {
ConfigGui.editSingleOption.getEntry("client.graphics.quality.lodBiomeBlending").value = newLodBiomeBlending;
ConfigGui.editSingleOption.saveOption("client.graphics.quality.lodBiomeBlending");
}
} }
public static class FogQuality implements IFogQuality public static class FogQuality implements IFogQuality
{ {
public final IAdvancedFog advancedFog;
FogQuality()
{
advancedFog = new AdvancedFog();
}
@Override @Override
public FogDistance getFogDistance() public FogDistance getFogDistance()
{ {
return OldConfig.Client.Graphics.FogQuality.fogDistance; return Config.Client.Graphics.FogQuality.fogDistance;
} }
@Override @Override
public void setFogDistance(FogDistance newFogDistance) public void setFogDistance(FogDistance newFogDistance)
@@ -208,7 +233,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public FogDrawMode getFogDrawMode() public FogDrawMode getFogDrawMode()
{ {
return OldConfig.Client.Graphics.FogQuality.fogDrawMode; return Config.Client.Graphics.FogQuality.fogDrawMode;
} }
@Override @Override
@@ -222,7 +247,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public FogColorMode getFogColorMode() public FogColorMode getFogColorMode()
{ {
return OldConfig.Client.Graphics.FogQuality.fogColorMode; return Config.Client.Graphics.FogQuality.fogColorMode;
} }
@Override @Override
@@ -236,7 +261,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public boolean getDisableVanillaFog() public boolean getDisableVanillaFog()
{ {
return OldConfig.Client.Graphics.FogQuality.disableVanillaFog; return Config.Client.Graphics.FogQuality.disableVanillaFog;
} }
@Override @Override
public void setDisableVanillaFog(boolean newDisableVanillaFog) public void setDisableVanillaFog(boolean newDisableVanillaFog)
@@ -244,6 +269,167 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.disableVanillaFog").value = newDisableVanillaFog; ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.disableVanillaFog").value = newDisableVanillaFog;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.disableVanillaFog"); ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.disableVanillaFog");
} }
@Override
public IAdvancedFog advancedFog() {
return advancedFog;
}
public static class AdvancedFog implements IAdvancedFog {
public final IHeightFog heightFog;
public AdvancedFog() {
heightFog = new HeightFog();
}
@Override
public double getFarFogStart() {
return Config.Client.Graphics.FogQuality.AdvancedFog.farFogStart;
}
@Override
public double getFarFogEnd() {
return Config.Client.Graphics.FogQuality.AdvancedFog.farFogEnd;
}
@Override
public double getFarFogMin() {
return Config.Client.Graphics.FogQuality.AdvancedFog.farFogMin;
}
@Override
public double getFarFogMax() {
return Config.Client.Graphics.FogQuality.AdvancedFog.farFogMax;
}
@Override
public FogSetting.FogType getFarFogType() {
return Config.Client.Graphics.FogQuality.AdvancedFog.farFogType;
}
@Override
public double getFarFogDensity() {
return Config.Client.Graphics.FogQuality.AdvancedFog.farFogDensity;
}
@Override
public void setFarFogStart(double newFarFogStart) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.farFogStart").value = newFarFogStart;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.farFogStart");
}
@Override
public void setFarFogEnd(double newFarFogEnd) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.farFogEnd").value = newFarFogEnd;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.farFogEnd");
}
@Override
public void setFarFogMin(double newFarFogMin) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.farFogMin").value = newFarFogMin;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.farFogMin");
}
@Override
public void setFarFogMax(double newFarFogMax) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.farFogMax").value = newFarFogMax;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.farFogMax");
}
@Override
public void setFarFogType(FogSetting.FogType newFarFogType) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.farFogType").value = newFarFogType;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.farFogType");
}
@Override
public void setFarFogDensity(double newFarFogDensity) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.farFogDensity").value = newFarFogDensity;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.farFogDensity");
}
@Override
public IHeightFog heightFog() {
return heightFog;
}
public static class HeightFog implements IHeightFog {
@Override
public HeightFogMixMode getHeightFogMixMode() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogMixMode;
}
@Override
public HeightFogMode getHeightFogMode() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogMode;
}
@Override
public double getHeightFogHeight() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogHeight;
}
@Override
public double getHeightFogStart() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogStart;
}
@Override
public double getHeightFogEnd() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogEnd;
}
@Override
public double getHeightFogMin() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogMin;
}
@Override
public double getHeightFogMax() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogMax;
}
@Override
public FogSetting.FogType getHeightFogType() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogType;
}
@Override
public double getHeightFogDensity() {
return Config.Client.Graphics.FogQuality.AdvancedFog.heightFog.heightFogDensity;
}
@Override
public void setHeightFogMixMode(HeightFogMixMode newHeightFogMixMode) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogMixMode").value = newHeightFogMixMode;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogMixMode");
}
@Override
public void setHeightFogMode(HeightFogMode newHeightFogMode) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogMode").value = newHeightFogMode;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogMode");
}
@Override
public void setHeightFogHeight(double newHeightFogHeight) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogHeight").value = newHeightFogHeight;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogHeight");
}
@Override
public void setHeightFogStart(double newHeightFogStart) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogStart").value = newHeightFogStart;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogStart");
}
@Override
public void setHeightFogEnd(double newHeightFogEnd) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogEnd").value = newHeightFogEnd;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogEnd");
}
@Override
public void setHeightFogMin(double newHeightFogMin) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogMin").value = newHeightFogMin;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogMin");
}
@Override
public void setHeightFogMax(double newHeightFogMax) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogMax").value = newHeightFogMax;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogMax");
}
@Override
public void setHeightFogType(FogSetting.FogType newHeightFogType) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogType").value = newHeightFogType;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogType");
}
@Override
public void setHeightFogDensity(double newHeightFogDensity) {
ConfigGui.editSingleOption.getEntry("client.graphics.fogQuality.advancedFog.heightFog.heightFogDensity").value = newHeightFogDensity;
ConfigGui.editSingleOption.saveOption("client.graphics.fogQuality.advancedFog.heightFog.heightFogDensity");
}
}
}
} }
@@ -252,7 +438,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public boolean getDisableDirectionalCulling() public boolean getDisableDirectionalCulling()
{ {
return OldConfig.Client.Graphics.AdvancedGraphics.disableDirectionalCulling; return Config.Client.Graphics.AdvancedGraphics.disableDirectionalCulling;
} }
@Override @Override
public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling) public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling)
@@ -265,7 +451,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public VanillaOverdraw getVanillaOverdraw() public VanillaOverdraw getVanillaOverdraw()
{ {
return OldConfig.Client.Graphics.AdvancedGraphics.vanillaOverdraw; return Config.Client.Graphics.AdvancedGraphics.vanillaOverdraw;
} }
@Override @Override
public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw) public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw)
@@ -277,7 +463,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public int getBacksideCullingRange() public int getBacksideCullingRange()
{ {
return OldConfig.Client.Graphics.AdvancedGraphics.backsideCullingRange; return Config.Client.Graphics.AdvancedGraphics.backsideCullingRange;
} }
@Override @Override
public void setBacksideCullingRange(int newBacksideCullingRange) public void setBacksideCullingRange(int newBacksideCullingRange)
@@ -289,7 +475,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public boolean getUseExtendedNearClipPlane() public boolean getUseExtendedNearClipPlane()
{ {
return OldConfig.Client.Graphics.AdvancedGraphics.useExtendedNearClipPlane; return Config.Client.Graphics.AdvancedGraphics.useExtendedNearClipPlane;
} }
@Override @Override
public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane) public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane)
@@ -297,6 +483,30 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
ConfigGui.editSingleOption.getEntry("client.graphics.advancedGraphics.useExtendedNearClipPlane").value = newUseExtendedNearClipPlane; ConfigGui.editSingleOption.getEntry("client.graphics.advancedGraphics.useExtendedNearClipPlane").value = newUseExtendedNearClipPlane;
ConfigGui.editSingleOption.saveOption("client.graphics.advancedGraphics.useExtendedNearClipPlane"); ConfigGui.editSingleOption.saveOption("client.graphics.advancedGraphics.useExtendedNearClipPlane");
} }
@Override
public double getBrightnessMultiplier()
{
return Config.Client.Graphics.AdvancedGraphics.brightnessMultiplier;
}
@Override
public void setBrightnessMultiplier(double newBrightnessMultiplier)
{
ConfigGui.editSingleOption.getEntry("client.graphics.advancedGraphics.brightnessMultiplier").value = newBrightnessMultiplier;
ConfigGui.editSingleOption.saveOption("client.graphics.advancedGraphics.brightnessMultiplier");
}
@Override
public double getSaturationMultiplier()
{
return Config.Client.Graphics.AdvancedGraphics.saturationMultiplier;
}
@Override
public void setSaturationMultiplier(double newSaturationMultiplier)
{
ConfigGui.editSingleOption.getEntry("client.graphics.advancedGraphics.saturationMultiplier").value = newSaturationMultiplier;
ConfigGui.editSingleOption.saveOption("client.graphics.advancedGraphics.saturationMultiplier");
}
} }
} }
@@ -311,7 +521,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public GenerationPriority getGenerationPriority() public GenerationPriority getGenerationPriority()
{ {
return OldConfig.Client.WorldGenerator.generationPriority; return Config.Client.WorldGenerator.generationPriority;
} }
@Override @Override
public void setGenerationPriority(GenerationPriority newGenerationPriority) public void setGenerationPriority(GenerationPriority newGenerationPriority)
@@ -324,7 +534,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public DistanceGenerationMode getDistanceGenerationMode() public DistanceGenerationMode getDistanceGenerationMode()
{ {
return OldConfig.Client.WorldGenerator.distanceGenerationMode; return Config.Client.WorldGenerator.distanceGenerationMode;
} }
@Override @Override
public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode) public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode)
@@ -337,7 +547,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public boolean getAllowUnstableFeatureGeneration() public boolean getAllowUnstableFeatureGeneration()
{ {
return OldConfig.Client.WorldGenerator.allowUnstableFeatureGeneration; return Config.Client.WorldGenerator.allowUnstableFeatureGeneration;
} }
@Override @Override
public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration) public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration)
@@ -350,7 +560,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public BlocksToAvoid getBlocksToAvoid() public BlocksToAvoid getBlocksToAvoid()
{ {
return OldConfig.Client.WorldGenerator.blocksToAvoid; return Config.Client.WorldGenerator.blocksToAvoid;
} }
@Override @Override
public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid) public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid)
@@ -372,7 +582,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public LightGenerationMode getLightGenerationMode() public LightGenerationMode getLightGenerationMode()
{ {
return OldConfig.Client.WorldGenerator.lightGenerationMode; return Config.Client.WorldGenerator.lightGenerationMode;
} }
@Override @Override
public void setLightGenerationMode(LightGenerationMode newLightGenerationMode) public void setLightGenerationMode(LightGenerationMode newLightGenerationMode)
@@ -384,6 +594,38 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
//=====================//
// Multiplayer Configs //
//=====================//
public static class Multiplayer implements IMultiplayer
{
@Override
public ServerFolderNameMode getServerFolderNameMode()
{
return Config.Client.Multiplayer.serverFolderNameMode;
}
@Override
public void setServerFolderNameMode(ServerFolderNameMode newServerFolderNameMode)
{
ConfigGui.editSingleOption.getEntry("client.multiplayer.serverFolderNameMode").value = newServerFolderNameMode;
ConfigGui.editSingleOption.saveOption("client.multiplayer.serverFolderNameMode");
}
@Override
public double getMultiDimensionRequiredSimilarity()
{
return Config.Client.Multiplayer.multiDimensionRequiredSimilarity;
}
@Override
public void setMultiDimensionRequiredSimilarity(double newMultiDimensionMinimumSimilarityPercent)
{
ConfigGui.editSingleOption.getEntry("client.multiplayer.multiDimensionMinimumSimilarityPercent").value = newMultiDimensionMinimumSimilarityPercent;
ConfigGui.editSingleOption.saveOption("client.multiplayer.multiDimensionMinimumSimilarityPercent");
}
}
//============================// //============================//
// AdvancedModOptions Configs // // AdvancedModOptions Configs //
@@ -428,7 +670,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public int getNumberOfWorldGenerationThreads() public int getNumberOfWorldGenerationThreads()
{ {
return OldConfig.Client.Advanced.Threading.numberOfWorldGenerationThreads; return Config.Client.Advanced.Threading.numberOfWorldGenerationThreads;
} }
@Override @Override
public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads) public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads)
@@ -441,7 +683,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public int getNumberOfBufferBuilderThreads() public int getNumberOfBufferBuilderThreads()
{ {
return OldConfig.Client.Advanced.Threading.numberOfBufferBuilderThreads; return Config.Client.Advanced.Threading.numberOfBufferBuilderThreads;
} }
@Override @Override
public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads) public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads)
@@ -505,7 +747,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public GpuUploadMethod getGpuUploadMethod() public GpuUploadMethod getGpuUploadMethod()
{ {
return OldConfig.Client.Advanced.Buffers.gpuUploadMethod; return Config.Client.Advanced.Buffers.gpuUploadMethod;
} }
@Override @Override
public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog) public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog)
@@ -518,7 +760,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public int getGpuUploadPerMegabyteInMilliseconds() public int getGpuUploadPerMegabyteInMilliseconds()
{ {
return OldConfig.Client.Advanced.Buffers.gpuUploadPerMegabyteInMilliseconds; return Config.Client.Advanced.Buffers.gpuUploadPerMegabyteInMilliseconds;
} }
@Override @Override
public void setGpuUploadPerMegabyteInMilliseconds(int newMilliseconds) { public void setGpuUploadPerMegabyteInMilliseconds(int newMilliseconds) {
@@ -530,7 +772,7 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
@Override @Override
public BufferRebuildTimes getRebuildTimes() public BufferRebuildTimes getRebuildTimes()
{ {
return OldConfig.Client.Advanced.Buffers.rebuildTimes; return Config.Client.Advanced.Buffers.rebuildTimes;
} }
@Override @Override
public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes) public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes)
@@ -1,505 +0,0 @@
package com.seibel.lod.common.wrappers.config;
import com.seibel.lod.core.enums.config.*;
import com.seibel.lod.core.enums.rendering.*;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.Config;
/**
* This is temporary for testing the new config
*
* @author coolGi2007
* @version 02-13-2022
*/
public class NewLodConfigWrapperSingleton implements ILodConfigWrapperSingleton
{
public static final NewLodConfigWrapperSingleton INSTANCE = new NewLodConfigWrapperSingleton();
private static final Client client = new Client();
@Override
public IClient client()
{
return client;
}
public static class Client implements IClient
{
public final IGraphics graphics;
public final IWorldGenerator worldGenerator;
public final IAdvanced advanced;
@Override
public IGraphics graphics()
{
return graphics;
}
@Override
public IWorldGenerator worldGenerator()
{
return worldGenerator;
}
@Override
public IAdvanced advanced()
{
return advanced;
}
@Override
public boolean getOptionsButton()
{
return Config.client.optionsButton.get();
}
@Override
public void setOptionsButton(boolean newOptionsButton)
{
Config.client.optionsButton.set(newOptionsButton);
}
//================//
// Client Configs //
//================//
public Client()
{
graphics = new Graphics();
worldGenerator = new WorldGenerator();
advanced = new Advanced();
}
//==================//
// Graphics Configs //
//==================//
public static class Graphics implements IGraphics
{
public final IQuality quality;
public final IFogQuality fogQuality;
public final IAdvancedGraphics advancedGraphics;
@Override
public IQuality quality()
{
return quality;
}
@Override
public IFogQuality fogQuality()
{
return fogQuality;
}
@Override
public IAdvancedGraphics advancedGraphics()
{
return advancedGraphics;
}
Graphics()
{
quality = new Quality();
fogQuality = new FogQuality();
advancedGraphics = new AdvancedGraphics();
}
public static class Quality implements IQuality
{
@Override
public HorizontalResolution getDrawResolution()
{
return Config.Client.Graphics.Quality.drawResolution.get();
}
@Override
public void setDrawResolution(HorizontalResolution newHorizontalResolution)
{
Config.Client.Graphics.Quality.drawResolution.set(newHorizontalResolution);
}
@Override
public int getLodChunkRenderDistance()
{
return Config.Client.Graphics.Quality.lodChunkRenderDistance.get();
}
@Override
public void setLodChunkRenderDistance(int newLodChunkRenderDistance)
{
Config.Client.Graphics.Quality.lodChunkRenderDistance.set(newLodChunkRenderDistance);
}
@Override
public VerticalQuality getVerticalQuality()
{
return Config.Client.Graphics.Quality.verticalQuality.get();
}
@Override
public void setVerticalQuality(VerticalQuality newVerticalQuality)
{
Config.Client.Graphics.Quality.verticalQuality.set(newVerticalQuality);
}
@Override
public int getHorizontalScale()
{
return Config.Client.Graphics.Quality.horizontalScale.get();
}
@Override
public void setHorizontalScale(int newHorizontalScale)
{
Config.Client.Graphics.Quality.horizontalScale.set(newHorizontalScale);
}
@Override
public HorizontalQuality getHorizontalQuality()
{
return Config.Client.Graphics.Quality.horizontalQuality.get();
}
@Override
public void setHorizontalQuality(HorizontalQuality newHorizontalQuality)
{
Config.Client.Graphics.Quality.horizontalQuality.set(newHorizontalQuality);
}
@Override
public DropoffQuality getDropoffQuality() {
return Config.Client.Graphics.Quality.dropoffQuality.get();
}
@Override
public void setDropoffQuality(DropoffQuality newDropoffQuality) {
Config.Client.Graphics.Quality.dropoffQuality.set(newDropoffQuality);
}
}
public static class FogQuality implements IFogQuality
{
@Override
public FogDistance getFogDistance()
{
return Config.Client.Graphics.FogQuality.fogDistance.get();
}
@Override
public void setFogDistance(FogDistance newFogDistance)
{
Config.Client.Graphics.FogQuality.fogDistance.set(newFogDistance);
}
@Override
public FogDrawMode getFogDrawMode()
{
return Config.Client.Graphics.FogQuality.fogDrawMode.get();
}
@Override
public void setFogDrawMode(FogDrawMode setFogDrawMode)
{
Config.Client.Graphics.FogQuality.fogDrawMode.set(setFogDrawMode);
}
@Override
public FogColorMode getFogColorMode()
{
return Config.Client.Graphics.FogQuality.fogColorMode.get();
}
@Override
public void setFogColorMode(FogColorMode newFogColorMode)
{
Config.Client.Graphics.FogQuality.fogColorMode.set(newFogColorMode);
}
@Override
public boolean getDisableVanillaFog()
{
return Config.Client.Graphics.FogQuality.disableVanillaFog.get();
}
@Override
public void setDisableVanillaFog(boolean newDisableVanillaFog)
{
Config.Client.Graphics.FogQuality.disableVanillaFog.set(newDisableVanillaFog);
}
}
public static class AdvancedGraphics implements IAdvancedGraphics
{
@Override
public boolean getDisableDirectionalCulling()
{
return Config.Client.Graphics.AdvancedGraphics.disableDirectionalCulling.get();
}
@Override
public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling)
{
Config.Client.Graphics.AdvancedGraphics.disableDirectionalCulling.set(newDisableDirectionalCulling);
}
@Override
public VanillaOverdraw getVanillaOverdraw()
{
return Config.Client.Graphics.AdvancedGraphics.vanillaOverdraw.get();
}
@Override
public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw)
{
Config.Client.Graphics.AdvancedGraphics.vanillaOverdraw.set(newVanillaOverdraw);
}
@Override
public boolean getUseExtendedNearClipPlane()
{
return Config.Client.Graphics.AdvancedGraphics.useExtendedNearClipPlane.get();
}
@Override
public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane)
{
Config.Client.Graphics.AdvancedGraphics.useExtendedNearClipPlane.set(newUseExtendedNearClipPlane);
}
}
}
//========================//
// WorldGenerator Configs //
//========================//
public static class WorldGenerator implements IWorldGenerator
{
@Override
public GenerationPriority getGenerationPriority()
{
return Config.Client.WorldGenerator.generationPriority.get();
}
@Override
public void setGenerationPriority(GenerationPriority newGenerationPriority)
{
Config.Client.WorldGenerator.generationPriority.set(newGenerationPriority);
}
@Override
public DistanceGenerationMode getDistanceGenerationMode()
{
return Config.Client.WorldGenerator.distanceGenerationMode.get();
}
@Override
public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode)
{
Config.Client.WorldGenerator.distanceGenerationMode.set(newDistanceGenerationMode);
}
/*
@Override
public boolean getAllowUnstableFeatureGeneration()
{
return Config.Client.WorldGenerator.allowUnstableFeatureGeneration;
}
@Override
public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration)
{
ConfigGui.editSingleOption.getEntry("client.worldGenerator.allowUnstableFeatureGeneration").value = newAllowUnstableFeatureGeneration;
ConfigGui.editSingleOption.saveOption("client.worldGenerator.allowUnstableFeatureGeneration");
}*/
@Override
public BlocksToAvoid getBlocksToAvoid()
{
return Config.Client.WorldGenerator.blocksToAvoid.get();
}
@Override
public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid)
{
Config.Client.WorldGenerator.blocksToAvoid.set(newBlockToAvoid);
}
@Override
public boolean getEnableDistantGeneration()
{
return Config.Client.WorldGenerator.enableDistantGeneration.get();
}
@Override
public void setEnableDistantGeneration(boolean newEnableDistantGeneration)
{
Config.Client.WorldGenerator.enableDistantGeneration.set(newEnableDistantGeneration);
}
@Override
public LightGenerationMode getLightGenerationMode()
{
return Config.Client.WorldGenerator.lightGenerationMode.get();
}
@Override
public void setLightGenerationMode(LightGenerationMode newLightGenerationMode)
{
Config.Client.WorldGenerator.lightGenerationMode.set(newLightGenerationMode);
}
}
//============================//
// AdvancedModOptions Configs //
//============================//
public static class Advanced implements IAdvanced
{
public final IThreading threading;
public final IDebugging debugging;
public final IBuffers buffers;
@Override
public IThreading threading()
{
return threading;
}
@Override
public IDebugging debugging()
{
return debugging;
}
@Override
public IBuffers buffers()
{
return buffers;
}
public Advanced()
{
threading = new Threading();
debugging = new Debugging();
buffers = new Buffers();
}
public static class Threading implements IThreading
{
@Override
public int getNumberOfWorldGenerationThreads()
{
return Config.Client.Advanced.Threading.numberOfWorldGenerationThreads.get();
}
@Override
public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads)
{
Config.Client.Advanced.Threading.numberOfWorldGenerationThreads.set(newNumberOfWorldGenerationThreads);
}
@Override
public int getNumberOfBufferBuilderThreads()
{
return Config.Client.Advanced.Threading.numberOfBufferBuilderThreads.get();
}
@Override
public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads)
{
Config.Client.Advanced.Threading.numberOfBufferBuilderThreads.set(newNumberOfWorldBuilderThreads);
}
}
//===============//
// Debug Options //
//===============//
public static class Debugging implements IDebugging
{
@Override
public boolean getDrawLods()
{
return Config.Client.Advanced.Debugging.drawLods.get();
}
@Override
public void setDrawLods(boolean newDrawLods)
{
Config.Client.Advanced.Debugging.drawLods.set(newDrawLods);
}
@Override
public DebugMode getDebugMode()
{
return Config.Client.Advanced.Debugging.debugMode.get();
}
@Override
public void setDebugMode(DebugMode newDebugMode)
{
Config.Client.Advanced.Debugging.debugMode.set(newDebugMode);
}
@Override
public boolean getDebugKeybindingsEnabled()
{
return Config.Client.Advanced.Debugging.enableDebugKeybindings.get();
}
@Override
public void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings)
{
Config.Client.Advanced.Debugging.enableDebugKeybindings.set(newEnableDebugKeybindings);
}
}
public static class Buffers implements IBuffers
{
@Override
public GpuUploadMethod getGpuUploadMethod()
{
return Config.Client.Advanced.Buffers.gpuUploadMethod.get();
}
@Override
public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog)
{
Config.Client.Advanced.Buffers.gpuUploadMethod.set(newDisableVanillaFog);
}
@Override
public int getGpuUploadPerMegabyteInMilliseconds()
{
return Config.Client.Advanced.Buffers.gpuUploadPerMegabyteInMilliseconds.get();
}
@Override
public void setGpuUploadPerMegabyteInMilliseconds(int newMilliseconds) {
Config.Client.Advanced.Buffers.gpuUploadPerMegabyteInMilliseconds.set(newMilliseconds);
}
@Override
public BufferRebuildTimes getRebuildTimes()
{
return Config.Client.Advanced.Buffers.rebuildTimes.get();
}
@Override
public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes)
{
Config.Client.Advanced.Buffers.rebuildTimes.set(newBufferRebuildTimes);
}
}
}
}
}
@@ -26,10 +26,10 @@ import java.util.ArrayList;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.platform.Window;
import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.enums.LodDirection; import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
@@ -67,11 +67,11 @@ import org.jetbrains.annotations.Nullable;
* to allow for easier movement between Minecraft versions. * to allow for easier movement between Minecraft versions.
* *
* @author James Seibel * @author James Seibel
* @version 9-16-2021 * @version 3-5-2022
*/ */
public class MinecraftWrapper implements IMinecraftWrapper public class MinecraftClientWrapper implements IMinecraftClientWrapper
{ {
public static final MinecraftWrapper INSTANCE = new MinecraftWrapper(); public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
public final Minecraft mc = Minecraft.getInstance(); public final Minecraft mc = Minecraft.getInstance();
@@ -84,7 +84,7 @@ public class MinecraftWrapper implements IMinecraftWrapper
private ProfilerWrapper profilerWrapper; private ProfilerWrapper profilerWrapper;
private MinecraftWrapper() private MinecraftClientWrapper()
{ {
} }
@@ -418,7 +418,7 @@ public class MinecraftWrapper implements IMinecraftWrapper
@Override @Override
public void crashMinecraft(String errorMessage, Throwable exception) public void crashMinecraft(String errorMessage, Throwable exception)
{ {
ClientApi.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft..."); ApiShared.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
CrashReport report = new CrashReport(errorMessage, exception); CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report); Minecraft.crash(report);
} }
@@ -5,13 +5,15 @@ import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.common.wrappers.misc.LightMapWrapper; import com.seibel.lod.common.wrappers.misc.LightMapWrapper;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.api.ModAccessorApi; import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
@@ -23,7 +25,6 @@ import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.lod.common.wrappers.McObjectConverter; import com.seibel.lod.common.wrappers.McObjectConverter;
@@ -57,7 +58,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
private static final Minecraft MC = Minecraft.getInstance(); private static final Minecraft MC = Minecraft.getInstance();
private static final GameRenderer GAME_RENDERER = MC.gameRenderer; private static final GameRenderer GAME_RENDERER = MC.gameRenderer;
private static final WrapperFactory FACTORY = WrapperFactory.INSTANCE; private static final IWrapperFactory FACTORY = WrapperFactory.INSTANCE;
@Override @Override
public Vec3f getLookAtVector() public Vec3f getLookAtVector()
@@ -129,7 +130,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override @Override
public int getRenderDistance() public int getRenderDistance()
{ {
return MC.options.renderDistance; return MC.options.getEffectiveRenderDistance();
} }
@Override @Override
@@ -143,22 +144,42 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return MC.getWindow().getHeight(); return MC.getWindow().getHeight();
} }
private RenderTarget getRenderTarget() {
RenderTarget r = null; //MC.levelRenderer.getCloudsTarget();
return r!=null ? r : MC.getMainRenderTarget();
}
@Override
public int getTargetFrameBuffer() {
return getRenderTarget().frameBufferId;
}
@Override
public int getTargetFrameBufferViewportWidth() {
return getRenderTarget().viewWidth;
}
@Override
public int getTargetFrameBufferViewportHeight() {
return getRenderTarget().viewHeight;
}
/** /**
* This method returns the ChunkPos of all chunks that Minecraft * This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br> * is going to render this frame. <br><br>
* <p> * <p>
*/ */
public boolean usingBackupGetVanillaRenderedChunks = false; public boolean usingBackupGetVanillaRenderedChunks = true;
@Override @Override
public HashSet<AbstractChunkPosWrapper> getVanillaRenderedChunks() public HashSet<AbstractChunkPosWrapper> getVanillaRenderedChunks()
{ {
ISodiumAccessor sodium = ModAccessorApi.get(ISodiumAccessor.class); ISodiumAccessor sodium = ModAccessorHandler.get(ISodiumAccessor.class);
if (sodium != null) if (sodium != null)
{ {
return sodium.getNormalRenderedChunks(); return sodium.getNormalRenderedChunks();
} }
IOptifineAccessor optifine = ModAccessorApi.get(IOptifineAccessor.class); IOptifineAccessor optifine = ModAccessorHandler.get(IOptifineAccessor.class);
if (optifine != null) if (optifine != null)
{ {
HashSet<AbstractChunkPosWrapper> pos = optifine.getNormalRenderedChunks(); HashSet<AbstractChunkPosWrapper> pos = optifine.getNormalRenderedChunks();
@@ -171,53 +192,29 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
LevelRenderer levelRenderer = MC.levelRenderer; LevelRenderer levelRenderer = MC.levelRenderer;
LinkedHashSet<LevelRenderer.RenderChunkInfo> chunks = levelRenderer.renderChunkStorage.get().renderChunks; LinkedHashSet<LevelRenderer.RenderChunkInfo> chunks = levelRenderer.renderChunkStorage.get().renderChunks;
return (chunks.stream().map((chunk) -> { return (chunks.stream().map((chunk) -> {
#if MC_VERSION_1_18_2
AABB chunkBoundingBox = chunk.chunk.getBoundingBox();
#elif MC_VERSION_1_18_1
AABB chunkBoundingBox = chunk.chunk.bb; AABB chunkBoundingBox = chunk.chunk.bb;
#endif
return FACTORY.createChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16), return FACTORY.createChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16)); Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new))); }).collect(Collectors.toCollection(HashSet::new)));
} catch (LinkageError e) { } catch (LinkageError e) {
try { try {
MinecraftWrapper.INSTANCE.sendChatMessage( MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7e\u00A7l\u00A7uWARNING: Distant Horizons: getVanillaRenderedChunks method failed." "\u00A7e\u00A7l\u00A7uWARNING: Distant Horizons: getVanillaRenderedChunks method failed."
+ " Using Backup Method."); + " Using Backup Method.");
MinecraftWrapper.INSTANCE.sendChatMessage( MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7eOverdraw prevention will be worse than normal."); "\u00A7eOverdraw prevention will be worse than normal.");
} catch (Exception e2) {} } catch (Exception e2) {}
ClientApi.LOGGER.error("getVanillaRenderedChunks Error: {}", e); ApiShared.LOGGER.error("getVanillaRenderedChunks Error: ", e);
usingBackupGetVanillaRenderedChunks = true; usingBackupGetVanillaRenderedChunks = true;
} }
} }
return getMaximumRenderedChunks(); return getMaximumRenderedChunks();
} }
@Override
public HashSet<AbstractChunkPosWrapper> getMaximumRenderedChunks()
{
//TODO: Make this a circle
IMinecraftWrapper mcWrapper = SingletonHandler.get(IMinecraftWrapper.class);
IWrapperFactory factory = SingletonHandler.get(IWrapperFactory.class);
int chunkRenderDist = this.getRenderDistance();
AbstractChunkPosWrapper centerChunkPos = mcWrapper.getPlayerChunkPos();
int startChunkX = centerChunkPos.getX() - chunkRenderDist;
int startChunkZ = centerChunkPos.getZ() - chunkRenderDist;
// add every position within render distance
HashSet<AbstractChunkPosWrapper> renderedPos = new HashSet<AbstractChunkPosWrapper>();
for (int chunkX = 0; chunkX < (chunkRenderDist * 2+1); chunkX++)
{
for(int chunkZ = 0; chunkZ < (chunkRenderDist * 2+1); chunkZ++)
{
renderedPos.add(factory.createChunkPos(startChunkX + chunkX, startChunkZ + chunkZ));
}
}
return renderedPos;
}
@Override @Override
public int[] getLightmapPixels() public int[] getLightmapPixels()
{ {
@@ -65,7 +65,7 @@ public class BiomeWrapper implements IBiomeWrapper
{ {
int colorInt; int colorInt;
switch (biome.getBiomeCategory()) switch (biome.biomeCategory)
{ {
case NETHER: case NETHER:
@@ -36,6 +36,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -128,12 +129,6 @@ public class WorldWrapper implements IWorldWrapper
return world.dimensionType().hasSkyLight(); return world.dimensionType().hasSkyLight();
} }
@Override
public boolean isEmpty()
{
return world == null;
}
@Override @Override
public int getHeight() public int getHeight()
{ {
@@ -181,5 +176,12 @@ public class WorldWrapper implements IWorldWrapper
return new ChunkWrapper(chunk, world); return new ChunkWrapper(chunk, world);
} }
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ) {
// world.hasChunk(chunkX, chunkZ); THIS DOES NOT WORK FOR CLIENT LEVEL CAUSE MOJANG ALWAYS RETURN TRUE FOR THAT!
ChunkSource source = world.getChunkSource();
return source.hasChunk(chunkX, chunkZ);
}
} }
@@ -19,17 +19,17 @@
package com.seibel.lod.common.wrappers.worldGeneration; package com.seibel.lod.common.wrappers.worldGeneration;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder; import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig; import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig;
import com.seibel.lod.core.enums.config.DistanceGenerationMode; import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.enums.config.LightGenerationMode; import com.seibel.lod.core.enums.config.LightGenerationMode;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.util.GridList; import com.seibel.lod.core.util.gridList.ArrayGridList;
import com.seibel.lod.core.util.LodThreadFactory; import com.seibel.lod.core.util.LodThreadFactory;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper; import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper;
@@ -190,7 +190,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (e.endNano != 0) if (e.endNano != 0)
{ {
lodTime.add(e.endNano - preTime); lodTime.add(e.endNano - preTime);
preTime = e.endNano;
} }
} }
@@ -214,7 +213,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
//=================Generation Step=================== //=================Generation Step===================
public final LinkedList<GenerationEvent> events = new LinkedList<GenerationEvent>(); public final LinkedList<GenerationEvent> events = new LinkedList<>();
public final GlobalParameters params; public final GlobalParameters params;
public final StepStructureStart stepStructureStart = new StepStructureStart(this); public final StepStructureStart stepStructureStart = new StepStructureStart(this);
public final StepStructureReference stepStructureReference = new StepStructureReference(this); public final StepStructureReference stepStructureReference = new StepStructureReference(this);
@@ -226,15 +225,16 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
public boolean unsafeThreadingRecorded = false; public boolean unsafeThreadingRecorded = false;
//public boolean safeMode = false; //public boolean safeMode = false;
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
public static final long EXCEPTION_TIMER_RESET_TIME = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS); public static final long EXCEPTION_TIMER_RESET_TIME = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
public static final int EXCEPTION_COUNTER_TRIGGER = 20; public static final int EXCEPTION_COUNTER_TRIGGER = 20;
public static final int RANGE_TO_RANGE_EMPTY_EXTENSION = 1;
public int unknownExceptionCount = 0; public int unknownExceptionCount = 0;
public long lastExceptionTriggerTime = 0; public long lastExceptionTriggerTime = 0;
public static final LodThreadFactory threadFactory = new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY); public static final LodThreadFactory threadFactory = new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY);
public static ThreadLocal<Boolean> isDistantGeneratorThread = new ThreadLocal<Boolean>(); public static ThreadLocal<Boolean> isDistantGeneratorThread = new ThreadLocal<>();
public static boolean isCurrentThreadDistantGeneratorThread() { public static boolean isCurrentThreadDistantGeneratorThread() {
return (isDistantGeneratorThread.get() != null); return (isDistantGeneratorThread.get() != null);
@@ -251,7 +251,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (!unsafeThreadingRecorded && !f.isDone()) { if (!unsafeThreadingRecorded && !f.isDone()) {
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons: Unsafe Threading in Chunk Generator Detected!"); MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons: Unsafe Threading in Chunk Generator Detected!");
MC.sendChatMessage("\u00A7eTo increase stability, it is recommended to set world generation threads count to 1."); MC.sendChatMessage("\u00A7eTo increase stability, it is recommended to set world generation threads count to 1.");
ClientApi.LOGGER.error("Unsafe Threading in Chunk Generator: ", new RuntimeException("Concurrent future")); ApiShared.LOGGER.error("Unsafe Threading in Chunk Generator: ", new RuntimeException("Concurrent future"));
unsafeThreadingRecorded = true; unsafeThreadingRecorded = true;
} }
return f.join(); return f.join();
@@ -300,8 +300,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
catch (Throwable e) catch (Throwable e)
{ {
ClientApi.LOGGER.error("Batching World Generator: Event {} gotten an exception", event); ApiShared.LOGGER.error("Batching World Generator: Event {} gotten an exception", event);
ClientApi.LOGGER.error("Exception: ", e); ApiShared.LOGGER.error("Exception: ", e);
unknownExceptionCount++; unknownExceptionCount++;
lastExceptionTriggerTime = System.nanoTime(); lastExceptionTriggerTime = System.nanoTime();
} }
@@ -312,12 +312,12 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
else if (event.hasTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)) else if (event.hasTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS))
{ {
ClientApi.LOGGER.error("Batching World Generator: " + event + " timed out and terminated!"); ApiShared.LOGGER.error("Batching World Generator: " + event + " timed out and terminated!");
ClientApi.LOGGER.info("Dump PrefEvent: " + event.pEvent); ApiShared.LOGGER.info("Dump PrefEvent: " + event.pEvent);
try try
{ {
if (!event.terminate()) if (!event.terminate())
ClientApi.LOGGER.error("Failed to terminate the stuck generation event!"); ApiShared.LOGGER.error("Failed to terminate the stuck generation event!");
} }
finally finally
{ {
@@ -329,7 +329,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
try { try {
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons: Too many exceptions in Batching World Generator! Disabling the generator."); MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons: Too many exceptions in Batching World Generator! Disabling the generator.");
} catch (Exception e) {} } catch (Exception e) {}
ClientApi.LOGGER.error("Too many exceptions in Batching World Generator! Now disabling."); ApiShared.LOGGER.error("Too many exceptions in Batching World Generator! Now disabling.");
unknownExceptionCount = 0; unknownExceptionCount = 0;
CONFIG.client().worldGenerator().setEnableDistantGeneration(false); CONFIG.client().worldGenerator().setEnableDistantGeneration(false);
} }
@@ -338,14 +338,14 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
public BatchGenerationEnvironment(IWorldWrapper serverlevel, LodBuilder lodBuilder, LodDimension lodDim) public BatchGenerationEnvironment(IWorldWrapper serverlevel, LodBuilder lodBuilder, LodDimension lodDim)
{ {
super(serverlevel, lodBuilder, lodDim); super(serverlevel, lodBuilder, lodDim);
ClientApi.LOGGER.info("================WORLD_GEN_STEP_INITING============="); ApiShared.LOGGER.info("================WORLD_GEN_STEP_INITING=============");
ChunkGenerator generator = ((WorldWrapper) serverlevel).getServerWorld().getChunkSource().getGenerator(); ChunkGenerator generator = ((WorldWrapper) serverlevel).getServerWorld().getChunkSource().getGenerator();
if (!(generator instanceof NoiseBasedChunkGenerator || if (!(generator instanceof NoiseBasedChunkGenerator ||
generator instanceof DebugLevelSource || generator instanceof DebugLevelSource ||
generator instanceof FlatLevelSource)) { generator instanceof FlatLevelSource)) {
MC.sendChatMessage("\u00A74\u00A7l\u00A7uWARNING: Distant Horizons: Unknown Chunk Generator Detected! Distant Generation May Fail!"); MC.sendChatMessage("\u00A74\u00A7l\u00A7uWARNING: Distant Horizons: Unknown Chunk Generator Detected! Distant Generation May Fail!");
MC.sendChatMessage("\u00A7eIf it does crash, set Distant Generation to OFF or Generation Mode to None."); MC.sendChatMessage("\u00A7eIf it does crash, set Distant Generation to OFF or Generation Mode to None.");
ClientApi.LOGGER.warn("Unknown Chunk Generator detected: {}", generator.getClass()); ApiShared.LOGGER.warn("Unknown Chunk Generator detected: {}", generator.getClass());
} }
params = new GlobalParameters((ServerLevel) ((WorldWrapper) serverlevel).getWorld(), lodBuilder, lodDim); params = new GlobalParameters((ServerLevel) ((WorldWrapper) serverlevel).getWorld(), lodBuilder, lodDim);
} }
@@ -360,7 +360,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
catch (Exception e) catch (Exception e)
{ {
ClientApi.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e); ApiShared.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
} }
if (chunkData == null) if (chunkData == null)
{ {
@@ -371,7 +371,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
try { try {
return ChunkLoader.read(level, lightEngine, chunkPos, chunkData); return ChunkLoader.read(level, lightEngine, chunkPos, chunkData);
} catch (Exception e) { } catch (Exception e) {
ClientApi.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e); ApiShared.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null); return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null);
} }
} }
@@ -381,24 +381,22 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
public void generateLodFromList(GenerationEvent e) public void generateLodFromList(GenerationEvent e)
{ {
if (ENABLE_EVENT_LOGGING) if (ENABLE_EVENT_LOGGING)
ClientApi.LOGGER.info("Lod Generate Event: " + e.pos); ApiShared.LOGGER.info("Lod Generate Event: " + e.pos);
e.pEvent.beginNano = System.nanoTime(); e.pEvent.beginNano = System.nanoTime();
GridList<ChunkAccess> referencedChunks; ArrayGridList<ChunkAccess> referencedChunks;
ArrayGridList<ChunkAccess> genChunks;
DistanceGenerationMode generationMode; DistanceGenerationMode generationMode;
LightedWorldGenRegion region; LightedWorldGenRegion region;
WorldGenLevelLightEngine lightEngine; WorldGenLevelLightEngine lightEngine;
LightGetterAdaptor adaptor; LightGetterAdaptor adaptor;
int refRange = e.range + RANGE_TO_RANGE_EMPTY_EXTENSION;
int refOffsetX = e.pos.x - refRange;
int refOffsetZ = e.pos.z - refRange;
try try
{ {
adaptor = new LightGetterAdaptor(params.level); adaptor = new LightGetterAdaptor(params.level);
lightEngine = new WorldGenLevelLightEngine(adaptor); lightEngine = new WorldGenLevelLightEngine(adaptor);
int cx = e.pos.x;
int cy = e.pos.z;
int rangeEmpty = e.range + 1;
GridList<ChunkAccess> chunks = new GridList<ChunkAccess>(rangeEmpty);
@SuppressWarnings("resource") @SuppressWarnings("resource")
EmptyChunkGenerator generator = (int x, int z) -> EmptyChunkGenerator generator = (int x, int z) ->
{ {
@@ -418,22 +416,18 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
return target; return target;
}; };
for (int oy = -rangeEmpty; oy <= rangeEmpty; oy++) referencedChunks = new ArrayGridList<>(refRange*2+1,
{ (x,z) -> generator.generate(x + refOffsetX,z + refOffsetZ)
for (int ox = -rangeEmpty; ox <= rangeEmpty; ox++) );
{
ChunkAccess target = generator.generate(cx + ox, cy + oy);
chunks.add(target);
}
}
e.pEvent.emptyNano = System.nanoTime(); e.pEvent.emptyNano = System.nanoTime();
e.refreshTimeout(); e.refreshTimeout();
region = new LightedWorldGenRegion(params.level, lightEngine, e.tParam.structFeat, chunks, ChunkStatus.STRUCTURE_STARTS, rangeEmpty, e.lightMode, generator); region = new LightedWorldGenRegion(params.level, lightEngine, referencedChunks,
ChunkStatus.STRUCTURE_STARTS, refRange, e.lightMode, generator);
adaptor.setRegion(region); adaptor.setRegion(region);
e.tParam.makeStructFeat(region); e.tParam.makeStructFeat(region);
referencedChunks = chunks.subGrid(e.range); genChunks = new ArrayGridList<>(referencedChunks, RANGE_TO_RANGE_EMPTY_EXTENSION,
referencedChunks = generateDirect(e, referencedChunks, e.target, region); referencedChunks.gridSize - RANGE_TO_RANGE_EMPTY_EXTENSION);
generateDirect(e, genChunks, e.target, region);
} }
catch (StepStructureStart.StructStartCorruptedException f) catch (StepStructureStart.StructStartCorruptedException f)
{ {
@@ -465,14 +459,12 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
default: default:
return; return;
} }
int centreIndex = referencedChunks.size() / 2;
for (int oy = -e.range; oy <= e.range; oy++) for (int oy = 0; oy < genChunks.gridSize; oy++)
{ {
for (int ox = -e.range; ox <= e.range; ox++) for (int ox = 0; ox < genChunks.gridSize; ox++)
{ {
int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); ChunkAccess target = genChunks.get(ox, oy);
ChunkAccess target = referencedChunks.get(targetIndex);
ChunkWrapper wrappedChunk = new ChunkWrapper(target, region); ChunkWrapper wrappedChunk = new ChunkWrapper(target, region);
if (!wrappedChunk.isLightCorrect()) { if (!wrappedChunk.isLightCorrect()) {
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false"); throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
@@ -483,14 +475,14 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (isFull) if (isFull)
{ {
if (ENABLE_LOAD_EVENT_LOGGING) if (ENABLE_LOAD_EVENT_LOGGING)
ClientApi.LOGGER.info("Detected full existing chunk at {}", target.getPos()); ApiShared.LOGGER.info("Detected full existing chunk at {}", target.getPos());
params.lodBuilder.generateLodNodeFromChunk(params.lodDim, wrappedChunk, params.lodBuilder.generateLodNodeFromChunk(params.lodDim, wrappedChunk,
new LodBuilderConfig(DistanceGenerationMode.FULL), true, e.genAllDetails); new LodBuilderConfig(DistanceGenerationMode.FULL), true, e.genAllDetails);
} }
else if (isPartial) else if (isPartial)
{ {
if (ENABLE_LOAD_EVENT_LOGGING) if (ENABLE_LOAD_EVENT_LOGGING)
ClientApi.LOGGER.info("Detected old existing chunk at {}", target.getPos()); ApiShared.LOGGER.info("Detected old existing chunk at {}", target.getPos());
params.lodBuilder.generateLodNodeFromChunk(params.lodDim, wrappedChunk, params.lodBuilder.generateLodNodeFromChunk(params.lodDim, wrappedChunk,
new LodBuilderConfig(generationMode), true, e.genAllDetails); new LodBuilderConfig(generationMode), true, e.genAllDetails);
} }
@@ -516,11 +508,11 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (ENABLE_PERF_LOGGING) if (ENABLE_PERF_LOGGING)
{ {
e.tParam.perf.recordEvent(e.pEvent); e.tParam.perf.recordEvent(e.pEvent);
ClientApi.LOGGER.info(e.tParam.perf); ApiShared.LOGGER.info(e.tParam.perf);
} }
} }
public GridList<ChunkAccess> generateDirect(GenerationEvent e, GridList<ChunkAccess> subRange, Steps step, public void generateDirect(GenerationEvent e, ArrayGridList<ChunkAccess> subRange, Steps step,
LightedWorldGenRegion region) LightedWorldGenRegion region)
{ {
try try
@@ -534,38 +526,37 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
}); });
if (step == Steps.Empty) if (step == Steps.Empty)
return subRange; return;
stepStructureStart.generateGroup(e.tParam, region, subRange); stepStructureStart.generateGroup(e.tParam, region, subRange);
e.pEvent.structStartNano = System.nanoTime(); e.pEvent.structStartNano = System.nanoTime();
e.refreshTimeout(); e.refreshTimeout();
if (step == Steps.StructureStart) if (step == Steps.StructureStart)
return subRange; return;
stepStructureReference.generateGroup(e.tParam, region, subRange); stepStructureReference.generateGroup(e.tParam, region, subRange);
e.pEvent.structRefNano = System.nanoTime(); e.pEvent.structRefNano = System.nanoTime();
e.refreshTimeout(); e.refreshTimeout();
if (step == Steps.StructureReference) if (step == Steps.StructureReference)
return subRange; return;
stepBiomes.generateGroup(e.tParam, region, subRange); stepBiomes.generateGroup(e.tParam, region, subRange);
e.pEvent.biomeNano = System.nanoTime(); e.pEvent.biomeNano = System.nanoTime();
e.refreshTimeout(); e.refreshTimeout();
if (step == Steps.Biomes) if (step == Steps.Biomes)
return subRange; return;
stepNoise.generateGroup(e.tParam, region, subRange); stepNoise.generateGroup(e.tParam, region, subRange);
e.pEvent.noiseNano = System.nanoTime(); e.pEvent.noiseNano = System.nanoTime();
e.refreshTimeout(); e.refreshTimeout();
if (step == Steps.Noise) if (step == Steps.Noise)
return subRange; return;
stepSurface.generateGroup(e.tParam, region, subRange); stepSurface.generateGroup(e.tParam, region, subRange);
e.pEvent.surfaceNano = System.nanoTime(); e.pEvent.surfaceNano = System.nanoTime();
e.refreshTimeout(); e.refreshTimeout();
if (step == Steps.Surface) if (step == Steps.Surface)
return subRange; return;
if (step == Steps.Carvers) if (step == Steps.Carvers)
return subRange; return;
stepFeatures.generateGroup(e.tParam, region, subRange); stepFeatures.generateGroup(e.tParam, region, subRange);
e.pEvent.featureNano = System.nanoTime(); e.pEvent.featureNano = System.nanoTime();
e.refreshTimeout(); e.refreshTimeout();
return subRange;
} }
finally finally
{ {
@@ -603,14 +594,14 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
@Override @Override
public void stop(boolean blocking) { public void stop(boolean blocking) {
ClientApi.LOGGER.info("Batch Chunk Generator shutting down..."); ApiShared.LOGGER.info("Batch Chunk Generator shutting down...");
executors.shutdownNow(); executors.shutdownNow();
if (blocking) try { if (blocking) try {
if (!executors.awaitTermination(10, TimeUnit.SECONDS)) { if (!executors.awaitTermination(10, TimeUnit.SECONDS)) {
ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads..."); ApiShared.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...");
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...", e); ApiShared.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...", e);
} }
} }
} }
@@ -6,9 +6,10 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.PrefEvent; import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.PrefEvent;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.enums.config.LightGenerationMode; import com.seibel.lod.core.enums.config.LightGenerationMode;
import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper.Steps; import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper.Steps;
@@ -69,9 +70,9 @@ public final class GenerationEvent
public boolean terminate() public boolean terminate()
{ {
future.cancel(true); ApiShared.LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
ClientApi.LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
BatchGenerationEnvironment.threadFactory.dumpAllThreadStacks(); BatchGenerationEnvironment.threadFactory.dumpAllThreadStacks();
future.cancel(true);
return future.isCancelled(); return future.isCancelled();
} }
@@ -99,6 +100,7 @@ public final class GenerationEvent
public void refreshTimeout() public void refreshTimeout()
{ {
nanotime = System.nanoTime(); nanotime = System.nanoTime();
LodUtil.checkInterruptsUnchecked();
} }
@Override @Override
@@ -3,9 +3,8 @@ package com.seibel.lod.common.wrappers.worldGeneration;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder; import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess; import net.minecraft.core.RegistryAccess;
@@ -1,153 +0,0 @@
package com.seibel.lod.common.wrappers.worldGeneration;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.common.wrappers.world.WorldWrapper;
import net.minecraft.world.level.chunk.*;
/* */
/* */ import net.minecraft.server.level.ServerLevel;
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/**
* @author James Seibel
* @version 11-13-2021
*/
public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper
{
public final ServerLevel serverWorld;
public final LodDimension lodDim;
public final LodBuilder lodBuilder;
public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
super(newLodBuilder, newLodDimension, worldWrapper);
lodBuilder = newLodBuilder;
lodDim = newLodDimension;
serverWorld = ((WorldWrapper) worldWrapper).getServerWorld();
}
/** takes about 2-5 ms */
@Override
public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode)
{
generate(pos.getX(), pos.getZ(), generationMode);
}
/** takes about 10 - 20 ms */
@Override
public void generateSurface(AbstractChunkPosWrapper pos)
{
generate(pos.getX(), pos.getZ(), DistanceGenerationMode.SURFACE);
}
/**
* takes about 15 - 20 ms
*/
@Override
public void generateFeatures(AbstractChunkPosWrapper pos)
{
generate(pos.getX(), pos.getZ(), DistanceGenerationMode.FEATURES);
}
/**
* Generates using MC's ServerWorld.
* <p>
* on pre generated chunks 0 - 1 ms <br>
* on un generated chunks 0 - 50 ms <br>
* with the median seeming to hover around 15 - 30 ms <br>
* and outliers in the 100 - 200 ms range <br>
* <p>
* Note this should not be multithreaded and does cause server/simulation lag
* (Higher lag for generating than loading)
*/
@Override
public void generateFull(AbstractChunkPosWrapper pos)
{
generate(pos.getX(), pos.getZ(), DistanceGenerationMode.FULL);
}
private void generate(int chunkX, int chunkZ, DistanceGenerationMode generationMode)
{
// long t = System.nanoTime();
ChunkStatus targetStatus;
switch (generationMode)
{
case BIOME_ONLY:
targetStatus = ChunkStatus.BIOMES;
break;
case BIOME_ONLY_SIMULATE_HEIGHT:
targetStatus = ChunkStatus.NOISE;
break;
case SURFACE:
targetStatus = ChunkStatus.SURFACE;
break;
case FEATURES:
targetStatus = ChunkStatus.FEATURES;
break;
case FULL:
targetStatus = ChunkStatus.FULL;
break;
case NONE:
default:
return;
}
// The bool=true means that we wants to generate chunk, and that the returned ChunkAccess must not be null
ChunkAccess ca = serverWorld.getChunkSource().getChunk(chunkX, chunkZ, targetStatus, true);
if (ca == null)
throw new RuntimeException("This should NEVER be null due to bool being true");
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(ca, serverWorld), new LodBuilderConfig(generationMode), false, true);
// long duration = System.nanoTime()-t;
// Debug print the duration
// System.out.println("LodChunkGenFull["+chunkX+","+chunkZ+"]: "+(double)(duration)/1000.);
}
/* TODO: Update this chart
* performance/generation tests related to
* serverWorld.getChunk(x, z, ChunkStatus. *** )
true/false is whether they generated blocks or not
the time is how long it took to generate
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
ChunkStatus.BIOMES 1 - 10 ms false (no height)
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
ChunkStatus.FEATURES 7 - 25 ms true
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
ChunkStatus.LIGHT 20 - 40 ms true
ChunkStatus.FULL 30 - 50 ms true
ChunkStatus.SPAWN 50 - 80 ms true
At this point I would suggest using FEATURES, as it generates snow and trees
(and any other object that are needed to make biomes distinct)
Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
*/
}
@@ -4,6 +4,7 @@ package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.Dynamic; import com.mojang.serialization.Dynamic;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ClientApi;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
@@ -14,6 +15,12 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
#if MC_VERSION_1_18_2
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@@ -49,7 +56,7 @@ import org.apache.logging.log4j.Logger;
public class ChunkLoader public class ChunkLoader
{ {
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()); private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
private static final Logger LOGGER = ClientApi.LOGGER; private static final Logger LOGGER = ApiShared.LOGGER;
private static final String TAG_UPGRADE_DATA = "UpgradeData"; private static final String TAG_UPGRADE_DATA = "UpgradeData";
private static final String BLOCK_TICKS_TAG = "block_ticks"; private static final String BLOCK_TICKS_TAG = "block_ticks";
private static final String FLUID_TICKS_TAG = "fluid_ticks"; private static final String FLUID_TICKS_TAG = "fluid_ticks";
@@ -69,8 +76,13 @@ public class ChunkLoader
private static LevelChunkSection[] readSections(LevelAccessor level, LevelLightEngine lightEngine, ChunkPos chunkPos, CompoundTag chunkData) private static LevelChunkSection[] readSections(LevelAccessor level, LevelLightEngine lightEngine, ChunkPos chunkPos, CompoundTag chunkData)
{ {
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY); Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#if MC_VERSION_1_18_1
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec( Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS)); biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif MC_VERSION_1_18_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
int i = level.getSectionsCount(); int i = level.getSectionsCount();
LevelChunkSection[] chunkSections = new LevelChunkSection[i]; LevelChunkSection[] chunkSections = new LevelChunkSection[i];
@@ -87,15 +99,26 @@ public class ChunkLoader
if (sectionId >= 0 && sectionId < chunkSections.length) if (sectionId >= 0 && sectionId < chunkSections.length)
{ {
PalettedContainer<BlockState> blockStateContainer; PalettedContainer<BlockState> blockStateContainer;
#if MC_VERSION_1_18_1
PalettedContainer<Biome> biomeContainer; PalettedContainer<Biome> biomeContainer;
#elif MC_VERSION_1_18_2
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
blockStateContainer = tagSection.contains("block_states", 10) blockStateContainer = tagSection.contains("block_states", 10)
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error) ? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); : new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VERSION_1_18_1
biomeContainer = tagSection.contains("biomes", 10) biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error) ? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); : new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#elif MC_VERSION_1_18_2
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, i, (String) string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer); chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
} }
@@ -123,6 +146,7 @@ public class ChunkLoader
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter()); Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
} }
#if MC_VERSION_1_18_1
private static Map<StructureFeature<?>, StructureStart<?>> unpackStructureStart(StructurePieceSerializationContext structurePieceSerializationContext, CompoundTag compoundTag, long l) private static Map<StructureFeature<?>, StructureStart<?>> unpackStructureStart(StructurePieceSerializationContext structurePieceSerializationContext, CompoundTag compoundTag, long l)
{ {
HashMap<StructureFeature<?>, StructureStart<?>> map = Maps.newHashMap(); HashMap<StructureFeature<?>, StructureStart<?>> map = Maps.newHashMap();
@@ -170,13 +194,65 @@ public class ChunkLoader
} }
return map; return map;
} }
#elif MC_VERSION_1_18_2
private static Map<ConfiguredStructureFeature<?, ?>, StructureStart> unpackStructureStart(StructurePieceSerializationContext structurePieceSerializationContext, CompoundTag compoundTag, long l) {
Map<ConfiguredStructureFeature<?, ?>, StructureStart> map = Maps.newHashMap();
Registry<ConfiguredStructureFeature<?, ?>> structStartRegistry = structurePieceSerializationContext.registryAccess().registryOrThrow(Registry.CONFIGURED_STRUCTURE_FEATURE_REGISTRY);
CompoundTag compoundTag2 = compoundTag.getCompound("starts");
for (String string : compoundTag2.getAllKeys()) {
ResourceLocation resourceLocation = ResourceLocation.tryParse(string);
ConfiguredStructureFeature<?, ?> structureFeature = structStartRegistry.get(resourceLocation);
// String string2 = string.toLowerCase(Locale.ROOT);
// ConfiguredStructureFeature<?, ?> structureFeature = StructureFeature.STRUCTURES_REGISTRY.get(string2);
if (structureFeature == null) {
LOGGER.error("Unknown structure start: {}", resourceLocation);
continue;
}
StructureStart structureStart = StructureFeature.loadStaticStart(structurePieceSerializationContext, compoundTag2.getCompound(string), l);
if (structureStart == null)
continue;
map.put(structureFeature, structureStart);
}
return map;
}
private static Map<ConfiguredStructureFeature<?, ?>, LongSet> unpackStructureReferences(RegistryAccess registryAccess, ChunkPos chunkPos, CompoundTag compoundTag)
{
Map<ConfiguredStructureFeature<?, ?>, LongSet> map = Maps.newHashMap();
Registry<ConfiguredStructureFeature<?, ?>> structRegistry = registryAccess.registryOrThrow(Registry.CONFIGURED_STRUCTURE_FEATURE_REGISTRY);
CompoundTag compoundTag2 = compoundTag.getCompound("References");
for (String string : compoundTag2.getAllKeys())
{
ResourceLocation resourceLocation = ResourceLocation.tryParse(string);
ConfiguredStructureFeature<?, ?> structureFeature = structRegistry.get(resourceLocation);
// String string2 = string.toLowerCase(Locale.ROOT);
// ConfiguredStructureFeature<?, ?> structureFeature = StructureFeature.STRUCTURES_REGISTRY.get(string2);
if (structureFeature == null)
{
LOGGER.warn("Found reference to unknown structure '{}' in chunk {}, discarding", resourceLocation, chunkPos);
continue;
}
map.put(structureFeature, new LongOpenHashSet(Arrays.stream(compoundTag2.getLongArray(string)).filter(l ->
{
ChunkPos chunkPos2 = new ChunkPos(l);
if (chunkPos2.getChessboardDistance(chunkPos) > 8)
{
LOGGER.warn("Found invalid structure reference [ {} @ {} ] for chunk {}.", resourceLocation, chunkPos2, chunkPos);
return false;
}
return true;
}).toArray()));
}
return map;
}
#endif
private static void readStructures(WorldGenLevel level, LevelChunk chunk, CompoundTag chunkData) private static void readStructures(WorldGenLevel level, LevelChunk chunk, CompoundTag chunkData)
{ {
CompoundTag tagStructures = chunkData.getCompound("structures"); CompoundTag tagStructures = chunkData.getCompound("structures");
chunk.setAllStarts( chunk.setAllStarts(
unpackStructureStart(StructurePieceSerializationContext.fromLevel(level.getLevel()), tagStructures, level.getSeed())); unpackStructureStart(StructurePieceSerializationContext.fromLevel(level.getLevel()), tagStructures, level.getSeed()));
chunk.setAllReferences(unpackStructureReferences(chunk.getPos(), tagStructures)); chunk.setAllReferences(unpackStructureReferences(#if MC_VERSION_1_18_2 level.registryAccess() ,#endif chunk.getPos(), tagStructures));
} }
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData) private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
@@ -1,6 +1,6 @@
package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
import com.seibel.lod.core.api.ModAccessorApi; import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
@@ -15,7 +15,7 @@ public class LightGetterAdaptor implements LightChunkGetter {
public LightGetterAdaptor(BlockGetter heightAccessor) { public LightGetterAdaptor(BlockGetter heightAccessor) {
this.heightGetter = heightAccessor; this.heightGetter = heightAccessor;
shouldReturnNull = ModAccessorApi.get(IStarlightAccessor.class) != null; shouldReturnNull = ModAccessorHandler.get(IStarlightAccessor.class) != null;
} }
public void setRegion(LightedWorldGenRegion region) { public void setRegion(LightedWorldGenRegion region) {
@@ -2,6 +2,7 @@ package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
import java.util.List; import java.util.List;
import com.seibel.lod.core.api.ApiShared;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.EmptyChunkGenerator; import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.EmptyChunkGenerator;
@@ -9,16 +10,24 @@ import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.config.LightGenerationMode; import com.seibel.lod.core.enums.config.LightGenerationMode;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.block.BlockTintCache;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.StructureFeatureManager; import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@@ -39,21 +48,25 @@ public class LightedWorldGenRegion extends WorldGenRegion {
public final int size; public final int size;
private final ChunkPos firstPos; private final ChunkPos firstPos;
private final List<ChunkAccess> cache; private final List<ChunkAccess> cache;
private final StructureFeatureManager structFeat;
Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>(); Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
public LightedWorldGenRegion(ServerLevel serverLevel, WorldGenLevelLightEngine lightEngine, public LightedWorldGenRegion(ServerLevel serverLevel, WorldGenLevelLightEngine lightEngine,
StructureFeatureManager structFeat, List<ChunkAccess> list, ChunkStatus chunkStatus, int i, List<ChunkAccess> list, ChunkStatus chunkStatus, int i,
LightGenerationMode lightMode, EmptyChunkGenerator generator) { LightGenerationMode lightMode, EmptyChunkGenerator generator) {
super(serverLevel, list, chunkStatus, i); super(serverLevel, list, chunkStatus, i);
this.lightMode = lightMode; this.lightMode = lightMode;
this.firstPos = list.get(0).getPos(); this.firstPos = list.get(0).getPos();
this.generator = generator; this.generator = generator;
this.structFeat = structFeat;
light = lightEngine; light = lightEngine;
writeRadius = i; writeRadius = i;
cache = list; cache = list;
size = Mth.floor(Math.sqrt(list.size())); size = Mth.floor(Math.sqrt(list.size()));
this.tintCaches = Util.make(new Object2ObjectArrayMap(3), object2ObjectArrayMap -> {
object2ObjectArrayMap.put(BiomeColors.GRASS_COLOR_RESOLVER, new BlockTintCache((pos) -> {return calculateBlockTint(pos, BiomeColors.GRASS_COLOR_RESOLVER);}));
object2ObjectArrayMap.put(BiomeColors.FOLIAGE_COLOR_RESOLVER, new BlockTintCache((pos) -> {return calculateBlockTint(pos, BiomeColors.FOLIAGE_COLOR_RESOLVER);}));
object2ObjectArrayMap.put(BiomeColors.WATER_COLOR_RESOLVER, new BlockTintCache((pos) -> {return calculateBlockTint(pos, BiomeColors.WATER_COLOR_RESOLVER);}));
});
} }
// Bypass BCLib mixin overrides. // Bypass BCLib mixin overrides.
@@ -77,11 +90,12 @@ public class LightedWorldGenRegion extends WorldGenRegion {
return true; return true;
} }
@Override // TODO Check this
public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos, // @Override
StructureFeature<?> structureFeature) { // public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
return structFeat.startsForFeature(sectionPos, structureFeature); // StructureFeature<?> structureFeature) {
} // return structFeat.startsForFeature(sectionPos, structureFeature);
// }
// Skip updating the related tile entities // Skip updating the related tile entities
@Override @Override
@@ -183,7 +197,7 @@ public class LightedWorldGenRegion extends WorldGenRegion {
} }
} }
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus) { if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus) {
ClientApi.LOGGER.info("WorldGen requiring " + chunkStatus ApiShared.LOGGER.info("WorldGen requiring " + chunkStatus
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works."); + " outside expected range detected. Force passing EMPTY chunk and seeing if it works.");
debugTriggeredForStatus = chunkStatus; debugTriggeredForStatus = chunkStatus;
} }
@@ -224,4 +238,44 @@ public class LightedWorldGenRegion extends WorldGenRegion {
return (getBrightness(LightLayer.SKY, blockPos) >= getMaxLightLevel()); return (getBrightness(LightLayer.SKY, blockPos) >= getMaxLightLevel());
} }
private final Object2ObjectArrayMap<ColorResolver, BlockTintCache> tintCaches;
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
BlockTintCache blockTintCache = (BlockTintCache) this.tintCaches.get(colorResolver);
return blockTintCache.getColor(blockPos);
}
private Biome _getBiome(BlockPos pos) {
#if MC_VERSION_1_18_2
return getBiome(pos).value();
#elif MC_VERSION_1_18_1
return getBiome(pos);
#endif
}
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
int i = (Minecraft.getInstance()).options.biomeBlendRadius;
if (i == 0)
return colorResolver.getColor((Biome) _getBiome(blockPos), blockPos.getX(), blockPos.getZ());
int j = (i * 2 + 1) * (i * 2 + 1);
int k = 0;
int l = 0;
int m = 0;
Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
while (cursor3D.advance())
{
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
int n = colorResolver.getColor((Biome) _getBiome((BlockPos) mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
k += (n & 0xFF0000) >> 16;
l += (n & 0xFF00) >> 8;
m += n & 0xFF;
}
return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
}
} }
@@ -52,6 +52,8 @@ public class WorldGenStructFeatManager extends StructureFeatureManager {
return chunk.hasAnyStructureReferences(); return chunk.hasAnyStructureReferences();
} }
// TODO Check this
/*
@Override @Override
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos, public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
@@ -76,4 +78,5 @@ public class WorldGenStructFeatManager extends StructureFeatureManager {
} }
return builder.build(); return builder.build();
} }
*/
} }
@@ -4,7 +4,7 @@ import java.util.ArrayList;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.lod.core.util.GridList; import com.seibel.lod.core.util.gridList.ArrayGridList;
import net.minecraft.ReportedException; import net.minecraft.ReportedException;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
@@ -30,7 +30,7 @@ public final class StepFeatures {
public final ChunkStatus STATUS = ChunkStatus.FEATURES; public final ChunkStatus STATUS = ChunkStatus.FEATURES;
public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
GridList<ChunkAccess> chunks) { ArrayGridList<ChunkAccess> chunks) {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>(); ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkAccess chunk : chunks) { for (ChunkAccess chunk : chunks) {
@@ -2,7 +2,7 @@ package com.seibel.lod.common.wrappers.worldGeneration.step;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenLevelLightEngine; import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenLevelLightEngine;
import com.seibel.lod.core.util.GridList; import com.seibel.lod.core.util.gridList.ArrayGridList;
import net.minecraft.server.level.ThreadedLevelLightEngine; import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
@@ -28,7 +28,7 @@ public final class StepLight {
public final ChunkStatus STATUS = ChunkStatus.LIGHT; public final ChunkStatus STATUS = ChunkStatus.LIGHT;
public void generateGroup(LightEventListener lightEngine, public void generateGroup(LightEventListener lightEngine,
GridList<ChunkAccess> chunks) { ArrayGridList<ChunkAccess> chunks) {
//ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>(); //ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkAccess chunk : chunks) { for (ChunkAccess chunk : chunks) {
+4 -3
View File
@@ -1,6 +1,6 @@
accessWidener v1 named accessWidener v1 named
# used when determining where to save files too # used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File; accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used when rendering # used when rendering
@@ -22,9 +22,10 @@ accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine L
# world generation # world generation
accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V
accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings; accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings;
accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Ljava/util/function/Supplier; accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory;
# accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doFill (Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;II)Lnet/minecraft/world/level/chunk/ChunkAccess; accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doFill (Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;II)Lnet/minecraft/world/level/chunk/ChunkAccess;
accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V #accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
# lod generation from save file # lod generation from save file
+1 -1
Submodule core updated: fd81a8e067...6cd0281d0e
@@ -19,17 +19,18 @@
package com.seibel.lod.fabric; package com.seibel.lod.fabric;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.api.EventApi; import com.seibel.lod.core.api.EventApi;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.platform.InputConstants;
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.common.wrappers.world.DimensionTypeWrapper; import com.seibel.lod.common.wrappers.world.DimensionTypeWrapper;
import com.seibel.lod.common.wrappers.world.WorldWrapper; import com.seibel.lod.common.wrappers.world.WorldWrapper;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.fabric.mixins.MixinUtilBackgroudThread;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@@ -44,6 +45,7 @@ import net.minecraft.world.level.chunk.LevelChunk;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@@ -60,14 +62,13 @@ public class ClientProxy
private final EventApi eventApi = EventApi.INSTANCE; private final EventApi eventApi = EventApi.INSTANCE;
private final ClientApi clientApi = ClientApi.INSTANCE; private final ClientApi clientApi = ClientApi.INSTANCE;
public static Supplier<Boolean> isGenerationThreadChecker = null;
/** /**
* Registers Fabric Events * Registers Fabric Events
* @author Ran * @author Ran
*/ */
public void registerEvents() { public void registerEvents() {
// TODO: Fix this if it's wrong
/* Registor the mod accessor*/ /* Registor the mod accessor*/
/* World Events */ /* World Events */
@@ -91,6 +92,8 @@ public class ClientProxy
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.player != null) onKeyInput(); if (client.player != null) onKeyInput();
}); });
isGenerationThreadChecker = BatchGenerationEnvironment::isCurrentThreadDistantGeneratorThread;
} }
@@ -21,18 +21,20 @@ package com.seibel.lod.fabric;
import com.seibel.lod.common.LodCommonMain; import com.seibel.lod.common.LodCommonMain;
import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.api.ModAccessorApi; import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
import com.seibel.lod.fabric.networking.NetworkHandler;
import com.seibel.lod.fabric.wrappers.modAccessor.ModChecker; import com.seibel.lod.fabric.wrappers.modAccessor.ModChecker;
import com.seibel.lod.fabric.wrappers.modAccessor.OptifineAccessor; import com.seibel.lod.fabric.wrappers.modAccessor.OptifineAccessor;
import com.seibel.lod.fabric.wrappers.modAccessor.SodiumAccessor; import com.seibel.lod.fabric.wrappers.modAccessor.SodiumAccessor;
import com.seibel.lod.fabric.wrappers.modAccessor.StarlightAccessor; import com.seibel.lod.fabric.wrappers.modAccessor.StarlightAccessor;
import com.seibel.lod.fabric.wrappers.DependencySetup; import com.seibel.lod.fabric.wrappers.FabricDependencySetup;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
@@ -63,29 +65,32 @@ public class Main implements ClientModInitializer
// This loads the mod after minecraft loads which doesn't causes a lot of issues // This loads the mod after minecraft loads which doesn't causes a lot of issues
public static void init() { public static void init() {
LodCommonMain.initConfig(); LodCommonMain.initConfig();
LodCommonMain.startup(null, false); LodCommonMain.startup(null, false, new NetworkHandler());
DependencySetup.createInitialBindings(); FabricDependencySetup.createInitialBindings();
SingletonHandler.bind(IModChecker.class, ModChecker.INSTANCE); FabricDependencySetup.finishBinding();
ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION); ApiShared.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
// Check if this works // Check if this works
client_proxy = new ClientProxy(); client_proxy = new ClientProxy();
client_proxy.registerEvents(); client_proxy.registerEvents();
if (SingletonHandler.get(IModChecker.class).isModLoaded("sodium")) { if (SingletonHandler.get(IModChecker.class).isModLoaded("sodium")) {
ModAccessorApi.bind(ISodiumAccessor.class, new SodiumAccessor()); ModAccessorHandler.bind(ISodiumAccessor.class, new SodiumAccessor());
} }
if (SingletonHandler.get(IModChecker.class).isModLoaded("starlight")) { if (SingletonHandler.get(IModChecker.class).isModLoaded("starlight")) {
ModAccessorApi.bind(IStarlightAccessor.class, new StarlightAccessor()); ModAccessorHandler.bind(IStarlightAccessor.class, new StarlightAccessor());
} }
if (SingletonHandler.get(IModChecker.class).isModLoaded("optifine")) { if (SingletonHandler.get(IModChecker.class).isModLoaded("optifine")) {
ModAccessorApi.bind(IOptifineAccessor.class, new OptifineAccessor()); ModAccessorHandler.bind(IOptifineAccessor.class, new OptifineAccessor());
} }
ModAccessorHandler.finishBinding();
} }
public static void initServer() { public static void initServer() {
LodCommonMain.initConfig(); LodCommonMain.initConfig();
LodCommonMain.startup(null, true); LodCommonMain.startup(null, true, new NetworkHandler());
DependencySetup.createInitialBindings(); FabricDependencySetup.createInitialBindings();
ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION); FabricDependencySetup.finishBinding();
ApiShared.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
} }
} }
@@ -0,0 +1,16 @@
package com.seibel.lod.fabric.mixins;
import com.seibel.lod.fabric.Main;
import net.minecraft.server.dedicated.DedicatedServer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(DedicatedServer.class)
public class MixinDedicatedServer {
@Inject(method = "initServer", at = @At("TAIL"))
public void initServer(CallbackInfoReturnable<Boolean> cir) {
Main.initServer();
}
}
@@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
@@ -3,7 +3,7 @@ package com.seibel.lod.fabric.mixins;
import com.seibel.lod.common.wrappers.config.ConfigGui; import com.seibel.lod.common.wrappers.config.ConfigGui;
import com.seibel.lod.common.wrappers.config.TexturedButtonWidget; import com.seibel.lod.common.wrappers.config.TexturedButtonWidget;
import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import net.minecraft.client.gui.screens.OptionsScreen; import net.minecraft.client.gui.screens.OptionsScreen;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
@@ -3,14 +3,12 @@ package com.seibel.lod.fabric.mixins;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.seibel.lod.fabric.ClientProxy;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.seibel.lod.common.wrappers.DependencySetupDoneCheck;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.util.DummyRunExecutorService; import com.seibel.lod.core.util.DummyRunExecutorService;
import net.minecraft.Util; import net.minecraft.Util;
@@ -23,9 +21,9 @@ public class MixinUtilBackgroudThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci) private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci)
{ {
if (DependencySetupDoneCheck.isDone && BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread()) if (ClientProxy.isGenerationThreadChecker != null && ClientProxy.isGenerationThreadChecker.get())
{ {
//ClientApi.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
} }
} }
@@ -33,9 +31,9 @@ public class MixinUtilBackgroudThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci) private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci)
{ {
if (DependencySetupDoneCheck.isDone && BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread()) if (ClientProxy.isGenerationThreadChecker != null && ClientProxy.isGenerationThreadChecker.get())
{ {
//ClientApi.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
} }
} }
@@ -43,9 +41,9 @@ public class MixinUtilBackgroudThread
@Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true) @Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true)
private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci) private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci)
{ {
if (DependencySetupDoneCheck.isDone && BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread()) if (ClientProxy.isGenerationThreadChecker != null && ClientProxy.isGenerationThreadChecker.get())
{ {
//ClientApi.LOGGER.info("util backgroundExecutor triggered"); //ApiShared.LOGGER.info("util backgroundExecutor triggered");
ci.setReturnValue(new DummyRunExecutorService()); ci.setReturnValue(new DummyRunExecutorService());
} }
} }
@@ -24,6 +24,7 @@ import com.mojang.math.Matrix4f;
import com.seibel.lod.common.wrappers.McObjectConverter; import com.seibel.lod.common.wrappers.McObjectConverter;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.objects.math.Mat4f; import com.seibel.lod.core.objects.math.Mat4f;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -61,7 +62,25 @@ public class MixinWorldRenderer
} }
// HEAD or RETURN // HEAD or RETURN
@Inject(at = @At("HEAD"), method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLcom/mojang/math/Matrix4f;)V") @Inject(at = @At("RETURN"),
method = "renderSky",
cancellable = true)
private void renderLod(PoseStack modelViewMatrixStack, Matrix4f projectionMatrix, float f,
#if MC_VERSION_1_18_2 Camera camera, boolean bl,#endif Runnable r, CallbackInfo callback) {
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks);
}
/*
// HEAD or RETURN
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLcom/mojang/math/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
{ {
// only render before solid blocks // only render before solid blocks
@@ -72,5 +91,6 @@ public class MixinWorldRenderer
ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks); ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks);
} }
} //callback.cancel();
}*/
} }
@@ -4,6 +4,9 @@ import com.seibel.lod.fabric.Main;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
#if MC_VERSION_1_18_2
import net.minecraft.core.Holder;
#endif
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.DimensionType;
@@ -18,12 +21,21 @@ import java.util.function.Supplier;
* This class is used for world loading events * This class is used for world loading events
* @author Ran * @author Ran
*/ */
@Mixin(ClientLevel.class) @Mixin(ClientLevel.class)
public class MixinClientLevel { public class MixinClientLevel {
#if MC_VERSION_1_18_2
@Inject(method = "<init>", at = @At("TAIL"))
private void loadWorldEvent(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey resourceKey, Holder holder, int i, int j, Supplier supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
Main.client_proxy.worldLoadEvent((ClientLevel) (Object) this);
}
#elif MC_VERSION_1_18_1
@Inject(method = "<init>", at = @At("TAIL")) @Inject(method = "<init>", at = @At("TAIL"))
private void loadWorldEvent(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey resourceKey, DimensionType dimensionType, int i, int j, Supplier supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) { private void loadWorldEvent(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey resourceKey, DimensionType dimensionType, int i, int j, Supplier supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
Main.client_proxy.worldLoadEvent((ClientLevel) (Object) this); Main.client_proxy.worldLoadEvent((ClientLevel) (Object) this);
} }
#endif
@Inject(method = "setLightReady", at = @At("HEAD")) @Inject(method = "setLightReady", at = @At("HEAD"))
private void onChunkLightReady(int x, int z, CallbackInfo ci) { private void onChunkLightReady(int x, int z, CallbackInfo ci) {
ClientLevel l = (ClientLevel) (Object) this; ClientLevel l = (ClientLevel) (Object) this;
@@ -0,0 +1,25 @@
package com.seibel.lod.fabric.networking;
import com.seibel.lod.common.networking.NetworkInterface;
import com.seibel.lod.common.networking.Networking;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
/**
* @author Ran
*/
public class NetworkHandler implements NetworkInterface {
@Override
public void register_Client() {
ClientPlayNetworking.registerGlobalReceiver(Networking.resourceLocation_meow, (client, handler, buf, responseSender) -> {
com.seibel.lod.common.networking.NetworkHandler.receivePacketClient(client, handler, buf);
});
}
@Override
public void register_Server() {
ServerPlayNetworking.registerGlobalReceiver(Networking.resourceLocation_meow, (server, player, handler, buf, responseSender) -> {
com.seibel.lod.common.networking.NetworkHandler.receivePacketServer(server, player, handler, buf);
});
}
}
@@ -1,28 +0,0 @@
package com.seibel.lod.fabric.wrappers;
import com.seibel.lod.common.LodCommonMain;
import com.seibel.lod.common.wrappers.config.NewLodConfigWrapperSingleton;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.common.wrappers.config.LodConfigWrapperSingleton;
/**
* Binds all necessary dependencies, so we
* can access them in Core. <br>
* This needs to be called before any Core classes
* are loaded.
*
* @author James Seibel
* @author Ran
* @version 12-1-2021
*/
public class DependencySetup
{
public static void createInitialBindings()
{
if (!LodCommonMain.IsNewConfig)
SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE);
else
SingletonHandler.bind(ILodConfigWrapperSingleton.class, NewLodConfigWrapperSingleton.INSTANCE);
}
}
@@ -0,0 +1,31 @@
package com.seibel.lod.fabric.wrappers;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.common.wrappers.config.LodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.lod.fabric.wrappers.modAccessor.ModChecker;
/**
* Binds all necessary dependencies, so we
* can access them in Core. <br>
* This needs to be called before any Core classes
* are loaded.
*
* @author James Seibel
* @author Ran
* @version 3-5-2022
*/
public class FabricDependencySetup
{
public static void createInitialBindings()
{
SingletonHandler.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE);
}
public static void finishBinding() {
SingletonHandler.finishBinding();
}
}
@@ -3,7 +3,7 @@ package com.seibel.lod.fabric.wrappers.modAccessor;
import java.util.HashSet; import java.util.HashSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
@@ -17,7 +17,9 @@
"events.MixinMinecraft", "events.MixinMinecraft",
"events.MixinBlockUpdate" "events.MixinBlockUpdate"
], ],
"server": [], "server": [
"MixinDedicatedServer"
],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
} }
+2 -2
View File
@@ -34,8 +34,8 @@
"depends": { "depends": {
"fabricloader": "*", "fabricloader": "*",
"fabric": "*", "fabric": "*",
"minecraft": "1.18.x", "minecraft": "${minecraft_version}",
"java": ">=17" "java": ">=${java_version}"
}, },
"suggests": { "suggests": {
"another-mod": "*" "another-mod": "*"
@@ -22,14 +22,15 @@ package com.seibel.lod.forge;
import com.seibel.lod.common.LodCommonMain; import com.seibel.lod.common.LodCommonMain;
import com.seibel.lod.common.forge.LodForgeMethodCaller; import com.seibel.lod.common.forge.LodForgeMethodCaller;
import com.seibel.lod.common.wrappers.config.ConfigGui; import com.seibel.lod.common.wrappers.config.ConfigGui;
import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper; import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ModAccessorApi;
import com.seibel.lod.core.handlers.ReflectionHandler; import com.seibel.lod.core.handlers.ReflectionHandler;
import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.lod.forge.networking.NetworkHandler;
import com.seibel.lod.forge.wrappers.ForgeDependencySetup; import com.seibel.lod.forge.wrappers.ForgeDependencySetup;
import com.seibel.lod.forge.wrappers.modAccessor.ModChecker; import com.seibel.lod.forge.wrappers.modAccessor.ModChecker;
@@ -42,14 +43,12 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.ModelDataMap; import net.minecraftforge.client.model.data.ModelDataMap;
import net.minecraftforge.client.ConfigGuiHandler; import net.minecraftforge.client.ConfigGuiHandler;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.forgespi.language.IModInfo;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@@ -71,29 +70,27 @@ public class ForgeMain implements LodForgeMethodCaller
{ {
// make sure the dependencies are set up before the mod needs them // make sure the dependencies are set up before the mod needs them
LodCommonMain.initConfig(); LodCommonMain.initConfig();
LodCommonMain.startup(this, !FMLLoader.getDist().isClient()); LodCommonMain.startup(this, !FMLLoader.getDist().isClient(), new NetworkHandler());
ForgeDependencySetup.createInitialBindings(); ForgeDependencySetup.createInitialBindings();
ClientApi.LOGGER.info("Distant Horizons initializing..."); ForgeDependencySetup.finishBinding();
SingletonHandler.bind(IModChecker.class, ModChecker.INSTANCE); ApiShared.LOGGER.info("Distant Horizons initializing...");
if (ReflectionHandler.instance.optifinePresent()) {
ModAccessorApi.bind(IOptifineAccessor.class, new OptifineAccessor());
} }
}
public ForgeMain() public ForgeMain()
{ {
// Register the methods // Register the methods for server and other game events we are interested in
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientStart); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientStart);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
} }
private void onClientStart(final FMLClientSetupEvent event) private void onClientStart(final FMLClientSetupEvent event)
{ {
if (ReflectionHandler.instance.optifinePresent()) {
ModAccessorHandler.bind(IOptifineAccessor.class, new OptifineAccessor());
}
ModAccessorHandler.finishBinding();
ModLoadingContext.get().registerExtensionPoint(ConfigGuiHandler.ConfigGuiFactory.class, ModLoadingContext.get().registerExtensionPoint(ConfigGuiHandler.ConfigGuiFactory.class,
() -> new ConfigGuiHandler.ConfigGuiFactory((client, parent) -> ConfigGui.getScreen(parent, ""))); () -> new ConfigGuiHandler.ConfigGuiFactory((client, parent) -> ConfigGui.getScreen(parent, "")));
forgeClientProxy = new ForgeClientProxy(); forgeClientProxy = new ForgeClientProxy();
@@ -102,7 +99,7 @@ public class ForgeMain implements LodForgeMethodCaller
private final ModelDataMap dataMap = new ModelDataMap.Builder().build(); private final ModelDataMap dataMap = new ModelDataMap.Builder().build();
@Override @Override
public List<BakedQuad> getQuads(MinecraftWrapper mc, Block block, BlockState blockState, Direction direction, Random random) { public List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random) {
return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap); return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
} }
} }
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.client.networking.v1;
import java.util.List;
import com.seibel.lod.forge.fabric.api.event.Event;
import com.seibel.lod.forge.fabric.api.event.EventFactory;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.resources.ResourceLocation;
/**
* Offers access to events related to the indication of a connected server's ability to receive packets in certain channels.
*/
public final class C2SPlayChannelEvents {
/**
* An event for the client play network handler receiving an update indicating the connected server's ability to receive packets in certain channels.
* This event may be invoked at any time after login and up to disconnection.
*/
public static final Event<Register> REGISTER = EventFactory.createArrayBacked(Register.class, callbacks -> (handler, sender, client, channels) -> {
for (Register callback : callbacks) {
callback.onChannelRegister(handler, sender, client, channels);
}
});
/**
* An event for the client play network handler receiving an update indicating the connected server's lack of ability to receive packets in certain channels.
* This event may be invoked at any time after login and up to disconnection.
*/
public static final Event<Unregister> UNREGISTER = EventFactory.createArrayBacked(Unregister.class, callbacks -> (handler, sender, client, channels) -> {
for (Unregister callback : callbacks) {
callback.onChannelUnregister(handler, sender, client, channels);
}
});
private C2SPlayChannelEvents() {
}
/**
* @see C2SPlayChannelEvents#REGISTER
*/
@FunctionalInterface
public interface Register {
void onChannelRegister(ClientPacketListener handler, PacketSender sender, Minecraft client, List<ResourceLocation> channels);
}
/**
* @see C2SPlayChannelEvents#UNREGISTER
*/
@FunctionalInterface
public interface Unregister {
void onChannelUnregister(ClientPacketListener handler, PacketSender sender, Minecraft client, List<ResourceLocation> channels);
}
}
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.client.networking.v1;
import com.seibel.lod.forge.fabric.api.event.Event;
import com.seibel.lod.forge.fabric.api.event.EventFactory;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl;
import net.minecraft.resources.ResourceLocation;
/**
* Offers access to events related to the connection to a server on the client while the server is processing the client's login request.
*/
public final class ClientLoginConnectionEvents {
/**
* Event indicating a connection entered the LOGIN state, ready for registering query request handlers.
* This event may be used by mods to prepare their client side state.
* This event does not guarantee that a login attempt will be successful.
*
* @see ClientLoginNetworking#registerReceiver(ResourceLocation, ClientLoginNetworking.LoginQueryRequestHandler)
*/
public static final Event<Init> INIT = EventFactory.createArrayBacked(Init.class, callbacks -> (handler, client) -> {
for (Init callback : callbacks) {
callback.onLoginStart(handler, client);
}
});
/**
* An event for when the client has started receiving login queries.
* A client can only start receiving login queries when a server has sent the first login query.
* Vanilla servers will typically never make the client enter this login phase, but it is not a guarantee that the
* connected server is a vanilla server since a modded server or proxy may have no login queries to send to the client
* and therefore bypass the login query phase.
* If this event is fired then it is a sign that a server is not a vanilla server or the server is behind a proxy which
* is capable of handling login queries.
*
* <p>This event may be used to {@link ClientLoginNetworking.LoginQueryRequestHandler register login query handlers}
* which may be used to send a response to a server.
*
* <p>No packets should be sent when this event is invoked.
*/
public static final Event<QueryStart> QUERY_START = EventFactory.createArrayBacked(QueryStart.class, callbacks -> (handler, client) -> {
for (QueryStart callback : callbacks) {
callback.onLoginQueryStart(handler, client);
}
});
/**
* An event for when the client's login process has ended due to disconnection.
*
* <p>No packets should be sent when this event is invoked.
*/
public static final Event<Disconnect> DISCONNECT = EventFactory.createArrayBacked(Disconnect.class, callbacks -> (handler, client) -> {
for (Disconnect callback : callbacks) {
callback.onLoginDisconnect(handler, client);
}
});
private ClientLoginConnectionEvents() {
}
/**
* @see ClientLoginConnectionEvents#INIT
*/
@FunctionalInterface
public interface Init {
void onLoginStart(ClientHandshakePacketListenerImpl handler, Minecraft client);
}
/**
* @see ClientLoginConnectionEvents#QUERY_START
*/
@FunctionalInterface
public interface QueryStart {
void onLoginQueryStart(ClientHandshakePacketListenerImpl handler, Minecraft client);
}
/**
* @see ClientLoginConnectionEvents#DISCONNECT
*/
@FunctionalInterface
public interface Disconnect {
void onLoginDisconnect(ClientHandshakePacketListenerImpl handler, Minecraft client);
}
}
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.client.networking.v1;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerLoginNetworking;
import com.seibel.lod.forge.fabric.impl.networking.client.ClientNetworkingImpl;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl;
import net.minecraft.network.Connection;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.PacketListener;
import net.minecraft.resources.ResourceLocation;
/**
* Offers access to login stage client-side networking functionalities.
*
* <p>The Minecraft login protocol only allows the client to respond to a server's request, but not initiate one of its own.
*
* @see ClientPlayNetworking
* @see ServerLoginNetworking
*/
public final class ClientLoginNetworking {
/**
* Registers a handler to a query request channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterGlobalReceiver(ResourceLocation)} to unregister the existing handler.
*
* @param channelName the id of the channel
* @param queryHandler the handler
* @return false if a handler is already registered to the channel
* @see ClientLoginNetworking#unregisterGlobalReceiver(ResourceLocation)
* @see ClientLoginNetworking#registerReceiver(ResourceLocation, LoginQueryRequestHandler)
*/
public static boolean registerGlobalReceiver(ResourceLocation channelName, LoginQueryRequestHandler queryHandler) {
return ClientNetworkingImpl.LOGIN.registerGlobalReceiver(channelName, queryHandler);
}
/**
* Removes the handler of a query request channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>The {@code channel} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel
* @see ClientLoginNetworking#registerGlobalReceiver(ResourceLocation, LoginQueryRequestHandler)
* @see ClientLoginNetworking#unregisterReceiver(ResourceLocation)
*/
@Nullable
public static ClientLoginNetworking.LoginQueryRequestHandler unregisterGlobalReceiver(ResourceLocation channelName) {
return ClientNetworkingImpl.LOGIN.unregisterGlobalReceiver(channelName);
}
/**
* Gets all query request channel names which global receivers are registered for.
* A global receiver is registered to all connections, in the present and future.
*
* @return all channel names which global receivers are registered for.
*/
public static Set<ResourceLocation> getGlobalReceivers() {
return ClientNetworkingImpl.LOGIN.getChannels();
}
/**
* Registers a handler to a query request channel.
*
* <p>If a handler is already registered to the {@code channelName}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterReceiver(ResourceLocation)} to unregister the existing handler.
*
* @param channelName the id of the channel
* @param queryHandler the handler
* @return false if a handler is already registered to the channel name
* @throws IllegalStateException if the client is not logging in
*/
public static boolean registerReceiver(ResourceLocation channelName, LoginQueryRequestHandler queryHandler) throws IllegalStateException {
final Connection connection = ClientNetworkingImpl.getLoginConnection();
if (connection != null) {
final PacketListener packetListener = connection.getPacketListener();
if (packetListener instanceof ClientHandshakePacketListenerImpl) {
return ClientNetworkingImpl.getAddon(((ClientHandshakePacketListenerImpl) packetListener)).registerChannel(channelName, queryHandler);
}
}
throw new IllegalStateException("Cannot register receiver while client is not logging in!");
}
/**
* Removes the handler of a query request channel.
*
* <p>The {@code channelName} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel name
* @throws IllegalStateException if the client is not logging in
*/
@Nullable
public static LoginQueryRequestHandler unregisterReceiver(ResourceLocation channelName) throws IllegalStateException {
final Connection connection = ClientNetworkingImpl.getLoginConnection();
if (connection != null) {
final PacketListener packetListener = connection.getPacketListener();
if (packetListener instanceof ClientHandshakePacketListenerImpl) {
return ClientNetworkingImpl.getAddon(((ClientHandshakePacketListenerImpl) packetListener)).unregisterChannel(channelName);
}
}
throw new IllegalStateException("Cannot unregister receiver while client is not logging in!");
}
private ClientLoginNetworking() {
}
@FunctionalInterface
public interface LoginQueryRequestHandler {
/**
* Handles an incoming query request from a server.
*
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
* Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft client instance.
*
* <p>The return value of this method is a completable future that may be used to delay the login process to the server until a task {@link CompletableFuture#isDone() is done}.
* The future should complete in reasonably time to prevent disconnection by the server.
* If your request processes instantly, you may use {@link CompletableFuture#completedFuture(Object)} to wrap your response for immediate sending.
*
* @param client the client
* @param handler the network handler that received this packet
* @param buf the payload of the packet
* @param listenerAdder listeners to be called when the response packet is sent to the server
* @return a completable future which contains the payload to respond to the server with.
* If the future contains {@code null}, then the server will be notified that the client did not understand the query.
*/
CompletableFuture<@Nullable FriendlyByteBuf> receive(Minecraft client, ClientHandshakePacketListenerImpl handler, FriendlyByteBuf buf, Consumer<GenericFutureListener<? extends Future<? super Void>>> listenerAdder);
}
}
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.client.networking.v1;
import com.seibel.lod.forge.fabric.api.event.Event;
import com.seibel.lod.forge.fabric.api.event.EventFactory;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.resources.ResourceLocation;
/**
* Offers access to events related to the connection to a server on a logical client.
*/
public final class ClientPlayConnectionEvents {
/**
* Event indicating a connection entered the PLAY state, ready for registering channel handlers.
*
* @see ClientPlayNetworking#registerReceiver(ResourceLocation, ClientPlayNetworking.PlayChannelHandler)
*/
public static final Event<Init> INIT = EventFactory.createArrayBacked(Init.class, callbacks -> (handler, client) -> {
for (Init callback : callbacks) {
callback.onPlayInit(handler, client);
}
});
/**
* An event for notification when the client play network handler is ready to send packets to the server.
*
* <p>At this stage, the network handler is ready to send packets to the server.
* Since the client's local state has been setup.
*/
public static final Event<Join> JOIN = EventFactory.createArrayBacked(Join.class, callbacks -> (handler, sender, client) -> {
for (Join callback : callbacks) {
callback.onPlayReady(handler, sender, client);
}
});
/**
* An event for the disconnection of the client play network handler.
*
* <p>No packets should be sent when this event is invoked.
*/
public static final Event<Disconnect> DISCONNECT = EventFactory.createArrayBacked(Disconnect.class, callbacks -> (handler, client) -> {
for (Disconnect callback : callbacks) {
callback.onPlayDisconnect(handler, client);
}
});
private ClientPlayConnectionEvents() {
}
@FunctionalInterface
public interface Init {
void onPlayInit(ClientPacketListener handler, Minecraft client);
}
@FunctionalInterface
public interface Join {
void onPlayReady(ClientPacketListener handler, PacketSender sender, Minecraft client);
}
@FunctionalInterface
public interface Disconnect {
void onPlayDisconnect(ClientPacketListener handler, Minecraft client);
}
}
@@ -0,0 +1,256 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.client.networking.v1;
import java.util.Objects;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketSender;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerPlayNetworking;
import com.seibel.lod.forge.fabric.impl.networking.client.ClientNetworkingImpl;
import com.seibel.lod.forge.fabric.impl.networking.client.ClientPlayNetworkAddon;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation;
/**
* Offers access to play stage client-side networking functionalities.
*
* <p>Client-side networking functionalities include receiving clientbound packets,
* sending serverbound packets, and events related to client-side network handlers.
*
* <p>This class should be only used on the physical client and for the logical client.
*
* @see ClientLoginNetworking
* @see ServerPlayNetworking
*/
public final class ClientPlayNetworking {
/**
* Registers a handler to a channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterGlobalReceiver(ResourceLocation)} to unregister the existing handler.
*
* @param channelName the id of the channel
* @param channelHandler the handler
* @return false if a handler is already registered to the channel
* @see ClientPlayNetworking#unregisterGlobalReceiver(ResourceLocation)
* @see ClientPlayNetworking#registerReceiver(ResourceLocation, PlayChannelHandler)
*/
public static boolean registerGlobalReceiver(ResourceLocation channelName, PlayChannelHandler channelHandler) {
return ClientNetworkingImpl.PLAY.registerGlobalReceiver(channelName, channelHandler);
}
/**
* Removes the handler of a channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>The {@code channel} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel
* @see ClientPlayNetworking#registerGlobalReceiver(ResourceLocation, PlayChannelHandler)
* @see ClientPlayNetworking#unregisterReceiver(ResourceLocation)
*/
@Nullable
public static PlayChannelHandler unregisterGlobalReceiver(ResourceLocation channelName) {
return ClientNetworkingImpl.PLAY.unregisterGlobalReceiver(channelName);
}
/**
* Gets all channel names which global receivers are registered for.
* A global receiver is registered to all connections, in the present and future.
*
* @return all channel names which global receivers are registered for.
*/
public static Set<ResourceLocation> getGlobalReceivers() {
return ClientNetworkingImpl.PLAY.getChannels();
}
/**
* Registers a handler to a channel.
*
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterReceiver(ResourceLocation)} to unregister the existing handler.
*
* <p>For example, if you only register a receiver using this method when a {@linkplain ClientLoginNetworking#registerGlobalReceiver(ResourceLocation, ClientLoginNetworking.LoginQueryRequestHandler)}
* login query has been received, you should use {@link ClientPlayConnectionEvents#INIT} to register the channel handler.
*
* @param channelName the id of the channel
* @return false if a handler is already registered to the channel
* @throws IllegalStateException if the client is not connected to a server
* @see ClientPlayConnectionEvents#INIT
*/
public static boolean registerReceiver(ResourceLocation channelName, PlayChannelHandler channelHandler) {
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
if (addon != null) {
return addon.registerChannel(channelName, channelHandler);
}
throw new IllegalStateException("Cannot register receiver while not in game!");
}
/**
* Removes the handler of a channel.
*
* <p>The {@code channelName} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel
* @throws IllegalStateException if the client is not connected to a server
*/
@Nullable
public static PlayChannelHandler unregisterReceiver(ResourceLocation channelName) throws IllegalStateException {
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
if (addon != null) {
return addon.unregisterChannel(channelName);
}
throw new IllegalStateException("Cannot unregister receiver while not in game!");
}
/**
* Gets all the channel names that the client can receive packets on.
*
* @return All the channel names that the client can receive packets on
* @throws IllegalStateException if the client is not connected to a server
*/
public static Set<ResourceLocation> getReceived() throws IllegalStateException {
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
if (addon != null) {
return addon.getReceivableChannels();
}
throw new IllegalStateException("Cannot get a list of channels the client can receive packets on while not in game!");
}
/**
* Gets all channel names that the connected server declared the ability to receive a packets on.
*
* @return All the channel names the connected server declared the ability to receive a packets on
* @throws IllegalStateException if the client is not connected to a server
*/
public static Set<ResourceLocation> getSendable() throws IllegalStateException {
final ClientPlayNetworkAddon addon = ClientNetworkingImpl.getClientPlayAddon();
if (addon != null) {
return addon.getSendableChannels();
}
throw new IllegalStateException("Cannot get a list of channels the server can receive packets on while not in game!");
}
/**
* Checks if the connected server declared the ability to receive a packet on a specified channel name.
*
* @param channelName the channel name
* @return True if the connected server has declared the ability to receive a packet on the specified channel.
* False if the client is not in game.
*/
public static boolean canSend(ResourceLocation channelName) throws IllegalArgumentException {
// You cant send without a client player, so this is fine
if (Minecraft.getInstance().getConnection() != null) {
return ClientNetworkingImpl.getAddon(Minecraft.getInstance().getConnection()).getSendableChannels().contains(channelName);
}
return false;
}
/**
* Creates a packet which may be sent to a the connected server.
*
* @param channelName the channel name
* @param buf the packet byte buf which represents the payload of the packet
* @return a new packet
*/
public static Packet<?> createC2SPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
Objects.requireNonNull(channelName, "Channel name cannot be null");
Objects.requireNonNull(buf, "Buf cannot be null");
return ClientNetworkingImpl.createPlayC2SPacket(channelName, buf);
}
/**
* Gets the packet sender which sends packets to the connected server.
*
* @return the client's packet sender
* @throws IllegalStateException if the client is not connected to a server
*/
public static PacketSender getSender() throws IllegalStateException {
// You cant send without a client player, so this is fine
if (Minecraft.getInstance().getConnection() != null) {
return ClientNetworkingImpl.getAddon(Minecraft.getInstance().getConnection());
}
throw new IllegalStateException("Cannot get packet sender when not in game!");
}
/**
* Sends a packet to the connected server.
*
* @param channelName the channel of the packet
* @param buf the payload of the packet
* @throws IllegalStateException if the client is not connected to a server
*/
public static void send(ResourceLocation channelName, FriendlyByteBuf buf) throws IllegalStateException {
// You cant send without a client player, so this is fine
if (Minecraft.getInstance().getConnection() != null) {
Minecraft.getInstance().getConnection().send(createC2SPacket(channelName, buf));
return;
}
throw new IllegalStateException("Cannot send packets when not in game!");
}
private ClientPlayNetworking() {
}
@FunctionalInterface
public interface PlayChannelHandler {
/**
* Handles an incoming packet.
*
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
* Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft client instance.
*
* <p>An example usage of this is to display an overlay message:
* <pre>{@code
* ClientPlayNetworking.registerReceiver(new Identifier("mymod", "overlay"), (client, handler, buf, responseSender) -&rt; {
* String message = buf.readString(32767);
*
* // All operations on the server or world must be executed on the server thread
* client.execute(() -&rt; {
* client.inGameHud.setOverlayMessage(message, true);
* });
* });
* }</pre>
* @param client the client
* @param handler the network handler that received this packet
* @param buf the payload of the packet
* @param responseSender the packet sender
*/
void receive(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* The Networking API (client side), version 1.
*
* <p>For login stage networking see {@link net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking}.
* For play stage networking see {@link net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking}.
*
* <p>For events related to connection to a server see {@link net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents} for login stage
* or {@link net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents} for play stage.
*
* <p>For events related to the ability of a server to receive packets on a channel of a specific name see {@link net.fabricmc.fabric.api.client.networking.v1.C2SPlayChannelEvents}.
*/
package com.seibel.lod.forge.fabric.api.client.networking.v1;
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.event;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that this {@link Event} is auto-invoking:
* it calls the event callback implemented by a context parameter type automatically and without registration.
*
* <p>This means that this event can be listened to in two ways:
* <ul>
* <li>If the consumer is the context parameter and it implements the callback, it will be automatically invoked, don't register manually.
* <li>Otherwise, there is no invocation and the listener needs manual registration as usual.
* </ul>
*
* <p>Do note that there may be more than one context parameter.
*
* <p>A typical use case is feature augmentation, for example to expose raw clicks to slots.
* The event callback has a slot parameter - the context parameter - and the event itself is carrying this annotation.
* All the slot needs to receive slot clicks is to implement {@code SlotClickCallback} on itself.
* It shouldn't do any explicit event registration like {@code SLOT_CLICK_EVENT.register(this::onSlotClick)},
* otherwise it will see extraneous callback invocations.
*
* <p>In general, an auto-invoking event bridges the gap between the flexibility of an event with global reach,
* and the convenience of implementing an interface that gets detected automatically.
*
* <p>This is a documentation-only annotation, the event factory has to implement the functionality explicitly by checking the parameter type and invoking it.
* On top of adding this annotation, the event field or method should document which parameters are context parameters,
* and under which circumstances they are invoked.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface AutoInvokingEvent {
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.event;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.resources.ResourceLocation;
/**
* Base class for Fabric's event implementations.
*
* @param <T> The listener type.
* @see EventFactory
*/
@ApiStatus.NonExtendable // Should only be extended by fabric API.
public abstract class Event<T> {
/**
* The invoker field. This should be updated by the implementation to
* always refer to an instance containing all code that should be
* executed upon event emission.
*/
protected volatile T invoker;
/**
* Returns the invoker instance.
*
* <p>An "invoker" is an object which hides multiple registered
* listeners of type T under one instance of type T, executing
* them and leaving early as necessary.
*
* @return The invoker instance.
*/
public final T invoker() {
return invoker;
}
/**
* Register a listener to the event, in the default phase.
* Have a look at {@link #addPhaseOrdering} for an explanation of event phases.
*
* @param listener The desired listener.
*/
public abstract void register(T listener);
/**
* The ResourceLocation of the default phase.
* Have a look at {@link EventFactory#createWithPhases} for an explanation of event phases.
*/
public static final ResourceLocation DEFAULT_PHASE = new ResourceLocation("fabric", "default");
/**
* Register a listener to the event for the specified phase.
* Have a look at {@link EventFactory#createWithPhases} for an explanation of event phases.
*
* @param phase ResourceLocation of the phase this listener should be registered for. It will be created if it didn't exist yet.
* @param listener The desired listener.
*/
public void register(ResourceLocation phase, T listener) {
// This is done to keep compatibility with existing Event subclasses, but they should really not be subclassing Event.
register(listener);
}
/**
* Request that listeners registered for one phase be executed before listeners registered for another phase.
* Relying on the default phases supplied to {@link EventFactory#createWithPhases} should be preferred over manually
* registering phase ordering dependencies.
*
* <p>Incompatible ordering constraints such as cycles will lead to inconsistent behavior:
* some constraints will be respected and some will be ignored. If this happens, a warning will be logged.
*
* @param firstPhase The ResourceLocation of the phase that should run before the other. It will be created if it didn't exist yet.
* @param secondPhase The ResourceLocation of the phase that should run after the other. It will be created if it didn't exist yet.
*/
public void addPhaseOrdering(ResourceLocation firstPhase, ResourceLocation secondPhase) {
// This is not abstract to avoid breaking existing Event subclasses, but they should really not be subclassing Event.
}
}
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.event;
import java.util.function.Function;
import net.minecraft.resources.ResourceLocation;
import com.seibel.lod.forge.fabric.impl.base.event.EventFactoryImpl;
/**
* Helper for creating {@link Event} classes.
*/
public final class EventFactory {
private static boolean profilingEnabled = true;
private EventFactory() { }
/**
* @return True if events are supposed to be profiled.
*/
public static boolean isProfilingEnabled() {
return profilingEnabled;
}
/**
* Invalidate and re-create all existing "invoker" instances across
* events created by this EventFactory. Use this if, for instance,
* the profilingEnabled field changes.
*/
// TODO: Turn this into an event?
public static void invalidate() {
EventFactoryImpl.invalidate();
}
/**
* Create an "array-backed" Event instance.
*
* <p>If your factory simply delegates to the listeners without adding custom behavior,
* consider using {@linkplain #createArrayBacked(Class, Object, Function) the other overload}
* if performance of this event is critical.
*
* @param type The listener class type.
* @param invokerFactory The invoker factory, combining multiple listeners into one instance.
* @param <T> The listener type.
* @return The Event instance.
*/
public static <T> Event<T> createArrayBacked(Class<? super T> type, Function<T[], T> invokerFactory) {
return EventFactoryImpl.createArrayBacked(type, invokerFactory);
}
/**
* Create an "array-backed" Event instance with a custom empty invoker,
* for an event whose {@code invokerFactory} only delegates to the listeners.
* <ul>
* <li>If there is no listener, the custom empty invoker will be used.</li>
* <li><b>If there is only one listener, that one will be used as the invoker
* and the factory will not be called.</b></li>
* <li>Only when there are at least two listeners will the factory be used.</li>
* </ul>
*
* <p>Having a custom empty invoker (of type (...) -&gt; {}) increases performance
* relative to iterating over an empty array; however, it only really matters
* if the event is executed thousands of times a second.
*
* @param type The listener class type.
* @param emptyInvoker The custom empty invoker.
* @param invokerFactory The invoker factory, combining multiple listeners into one instance.
* @param <T> The listener type.
* @return The Event instance.
*/
public static <T> Event<T> createArrayBacked(Class<T> type, T emptyInvoker, Function<T[], T> invokerFactory) {
return createArrayBacked(type, listeners -> {
if (listeners.length == 0) {
return emptyInvoker;
} else if (listeners.length == 1) {
return listeners[0];
} else {
return invokerFactory.apply(listeners);
}
});
}
/**
* Create an array-backed event with a list of default phases that get invoked in order.
* Exposing the ResourceLocations of the default phases as {@code public static final} constants is encouraged.
*
* <p>An event phase is a named group of listeners, which may be ordered before or after other groups of listeners.
* This allows some listeners to take priority over other listeners.
* Adding separate events should be considered before making use of multiple event phases.
*
* <p>Phases may be freely added to events created with any of the factory functions,
* however using this function is preferred for widely used event phases.
* If more phases are necessary, discussion with the author of the Event is encouraged.
*
* <p>Refer to {@link Event#addPhaseOrdering} for an explanation of event phases.
*
* @param type The listener class type.
* @param invokerFactory The invoker factory, combining multiple listeners into one instance.
* @param defaultPhases The default phases of this event, in the correct order. Must contain {@link Event#DEFAULT_PHASE}.
* @param <T> The listener type.
* @return The Event instance.
*/
public static <T> Event<T> createWithPhases(Class<? super T> type, Function<T[], T> invokerFactory, ResourceLocation... defaultPhases) {
EventFactoryImpl.ensureContainsDefault(defaultPhases);
EventFactoryImpl.ensureNoDuplicates(defaultPhases);
Event<T> event = createArrayBacked(type, invokerFactory);
for (int i = 1; i < defaultPhases.length; ++i) {
event.addPhaseOrdering(defaultPhases[i-1], defaultPhases[i]);
}
return event;
}
/**
* Get the listener object name. This can be used in debugging/profiling
* scenarios.
*
* @param handler The listener object.
* @return The listener name.
*/
public static String getHandlerName(Object handler) {
return handler.getClass().getName();
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import com.seibel.lod.forge.fabric.api.event.Event;
import com.seibel.lod.forge.fabric.api.event.EventFactory;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
/**
* Events related to a tracking entities within a player's view distance.
*/
public final class EntityTrackingEvents {
/**
* An event that is called before player starts tracking an entity.
* Typically this occurs when an entity enters a client's view distance.
* This event is called before the player's client is sent the entity's {@link Entity#getAddEntityPacket() spawn packet}.
*/
public static final Event<StartTracking> START_TRACKING = EventFactory.createArrayBacked(StartTracking.class, callbacks -> (trackedEntity, player) -> {
for (StartTracking callback : callbacks) {
callback.onStartTracking(trackedEntity, player);
}
});
/**
* An event that is called after a player has stopped tracking an entity.
* The client at this point was sent a packet to {@link net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket destroy} the entity on the client.
* The entity still exists on the server.
*/
public static final Event<StopTracking> STOP_TRACKING = EventFactory.createArrayBacked(StopTracking.class, callbacks -> (trackedEntity, player) -> {
for (StopTracking callback : callbacks) {
callback.onStopTracking(trackedEntity, player);
}
});
@FunctionalInterface
public interface StartTracking {
/**
* Called before an entity starts getting tracked by a player.
*
* @param trackedEntity the entity that will be tracked
* @param player the player that will track the entity
*/
void onStartTracking(Entity trackedEntity, ServerPlayer player);
}
@FunctionalInterface
public interface StopTracking {
/**
* Called after an entity stops getting tracked by a player.
*
* @param trackedEntity the entity that is no longer being tracked
* @param player the player that is no longer tracking the entity
*/
void onStopTracking(Entity trackedEntity, ServerPlayer player);
}
private EntityTrackingEvents() {
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import java.util.Objects;
import net.minecraft.network.FriendlyByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.GenericFutureListener;
/**
* Utilities for working with netty's future listeners.
* @see FutureListener
* @see ChannelFutureListener
*/
public final class FutureListeners {
/**
* Returns a future listener that releases a packet byte buf when the buffer has been sent to a remote connection.
*
* @param buf the buffer
* @return the future listener
*/
public static ChannelFutureListener free(FriendlyByteBuf buf) {
Objects.requireNonNull(buf, "PacketByteBuf cannot be null");
return (future) -> {
if (!isLocalChannel(future.channel())) {
buf.release();
}
};
}
/**
* Returns whether a netty channel performs local transportation, or if the message objects in the channel are directly passed than written to and read from a byte buf.
*
* @param channel the channel to check
* @return whether the channel is local
*/
public static boolean isLocalChannel(Channel channel) {
return channel instanceof LocalServerChannel || channel instanceof LocalChannel;
}
/**
* Combines two future listeners.
*
* @param first the first future listener
* @param second the second future listener
* @param <A> the future type of the first listener, used for casting
* @param <B> the future type of the second listener, used for casting
* @return the combined future listener.
*/
// A, B exist just to allow casting
@SuppressWarnings("unchecked")
public static <A extends Future<? super Void>, B extends Future<? super Void>> GenericFutureListener<? extends Future<? super Void>> union(GenericFutureListener<A> first, GenericFutureListener<B> second) {
// Return an empty future listener in the case of both parameters somehow being null
if (first == null && second == null) {
return future -> { };
}
if (first == null) {
return second;
}
if (second == null) {
return first;
}
return future -> {
first.operationComplete((A) future);
second.operationComplete((B) future);
};
}
private FutureListeners() {
}
}
@@ -0,0 +1,204 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import java.util.Objects;
import net.minecraft.network.FriendlyByteBuf;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* Helper methods for working with and creating {@link FriendlyByteBuf}s.
*/
public final class PacketByteBufs {
private static final FriendlyByteBuf EMPTY_PACKET_BYTE_BUF = new FriendlyByteBuf(Unpooled.EMPTY_BUFFER);
/**
* Returns an empty instance of packet byte buf.
*
* @return an empty buf
*/
public static FriendlyByteBuf empty() {
return EMPTY_PACKET_BYTE_BUF;
}
/**
* Returns a new heap memory-backed instance of packet byte buf.
*
* @return a new buf
*/
public static FriendlyByteBuf create() {
return new FriendlyByteBuf(Unpooled.buffer());
}
// Convenience methods for byte buf methods that return a new byte buf
/**
* Wraps the newly created buf from {@code buf.readBytes} in a packet byte buf.
*
* @param buf the original buf
* @param length the number of bytes to transfer
* @return the transferred bytes
* @see ByteBuf#readBytes(int)
*/
public static FriendlyByteBuf readBytes(ByteBuf buf, int length) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.readBytes(length));
}
/**
* Wraps the newly created buf from {@code buf.readSlice} in a packet byte buf.
*
* @param buf the original buf
* @param length the size of the new slice
* @return the newly created slice
* @see ByteBuf#readSlice(int)
*/
public static FriendlyByteBuf readSlice(ByteBuf buf, int length) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.readSlice(length));
}
/**
* Wraps the newly created buf from {@code buf.readRetainedSlice} in a packet byte buf.
*
* @param buf the original buf
* @param length the size of the new slice
* @return the newly created slice
* @see ByteBuf#readRetainedSlice(int)
*/
public static FriendlyByteBuf readRetainedSlice(ByteBuf buf, int length) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.readRetainedSlice(length));
}
/**
* Wraps the newly created buf from {@code buf.copy} in a packet byte buf.
*
* @param buf the original buf
* @return a copy of the buf
* @see ByteBuf#copy()
*/
public static FriendlyByteBuf copy(ByteBuf buf) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.copy());
}
/**
* Wraps the newly created buf from {@code buf.copy} in a packet byte buf.
*
* @param buf the original buf
* @param index the starting index
* @param length the size of the copy
* @return a copy of the buf
* @see ByteBuf#copy(int, int)
*/
public static FriendlyByteBuf copy(ByteBuf buf, int index, int length) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.copy(index, length));
}
/**
* Wraps the newly created buf from {@code buf.slice} in a packet byte buf.
*
* @param buf the original buf
* @return a slice of the buf
* @see ByteBuf#slice()
*/
public static FriendlyByteBuf slice(ByteBuf buf) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.slice());
}
/**
* Wraps the newly created buf from {@code buf.retainedSlice} in a packet byte buf.
*
* @param buf the original buf
* @return a slice of the buf
* @see ByteBuf#retainedSlice()
*/
public static FriendlyByteBuf retainedSlice(ByteBuf buf) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.retainedSlice());
}
/**
* Wraps the newly created buf from {@code buf.slice} in a packet byte buf.
*
* @param buf the original buf
* @param index the starting index
* @param length the size of the copy
* @return a slice of the buf
* @see ByteBuf#slice(int, int)
*/
public static FriendlyByteBuf slice(ByteBuf buf, int index, int length) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.slice(index, length));
}
/**
* Wraps the newly created buf from {@code buf.retainedSlice} in a packet byte buf.
*
* @param buf the original buf
* @param index the starting index
* @param length the size of the copy
* @return a slice of the buf
* @see ByteBuf#retainedSlice(int, int)
*/
public static FriendlyByteBuf retainedSlice(ByteBuf buf, int index, int length) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.retainedSlice(index, length));
}
/**
* Wraps the newly created buf from {@code buf.duplicate} in a packet byte buf.
*
* @param buf the original buf
* @return a duplicate of the buf
* @see ByteBuf#duplicate()
*/
public static FriendlyByteBuf duplicate(ByteBuf buf) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.duplicate());
}
/**
* Wraps the newly created buf from {@code buf.retainedDuplicate} in a packet byte buf.
*
* @param buf the original buf
* @return a duplicate of the buf
* @see ByteBuf#retainedDuplicate()
*/
public static FriendlyByteBuf retainedDuplicate(ByteBuf buf) {
Objects.requireNonNull(buf, "ByteBuf cannot be null");
return new FriendlyByteBuf(buf.retainedDuplicate());
}
private PacketByteBufs() {
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import java.util.Objects;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.jetbrains.annotations.Nullable;
/**
* Represents something that supports sending packets to channels.
* @see PacketByteBufs
*/
public interface PacketSender {
/**
* Makes a packet for a channel.
*
* @param channelName the id of the channel
* @param buf the content of the packet
*/
Packet<?> createPacket(ResourceLocation channelName, FriendlyByteBuf buf);
/**
* Sends a packet.
*
* @param packet the packet
*/
void sendPacket(Packet<?> packet);
/**
* Sends a packet.
*
* @param packet the packet
* @param callback an optional callback to execute after the packet is sent, may be {@code null}. The callback may also accept a {@link ChannelFutureListener}.
*/
void sendPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> callback);
/**
* Sends a packet to a channel.
*
* @param channel the id of the channel
* @param buf the content of the packet
*/
default void sendPacket(ResourceLocation channel, FriendlyByteBuf buf) {
Objects.requireNonNull(channel, "Channel cannot be null");
Objects.requireNonNull(buf, "Payload cannot be null");
this.sendPacket(this.createPacket(channel, buf));
}
/**
* Sends a packet to a channel.
*
* @param channel the id of the channel
* @param buf the content of the packet
* @param callback an optional callback to execute after the packet is sent, may be {@code null}
*/
// the generic future listener can accept ChannelFutureListener
default void sendPacket(ResourceLocation channel, FriendlyByteBuf buf, @Nullable GenericFutureListener<? extends Future<? super Void>> callback) {
Objects.requireNonNull(channel, "Channel cannot be null");
Objects.requireNonNull(buf, "Payload cannot be null");
this.sendPacket(this.createPacket(channel, buf), callback);
}
}
@@ -0,0 +1,199 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.stream.Collectors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.phys.Vec3;
import com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.EntityTrackerAccessor;
import com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.ThreadedAnvilChunkStorageAccessor;
/**
* For example, a block entity may use the methods in this class to send a packet to all clients which can see the block entity in order notify clients about a change.
*
* <p>The word "tracking" means that an entity/chunk on the server is known to a player's client (within in view distance) and the (block) entity should notify tracking clients of changes.
*
* <p>These methods should only be called on the server thread and only be used on logical a server.
*/
public final class PlayerLookup {
/**
* Gets all the players on the minecraft server.
*
* <p>The returned collection is immutable.
*
* @param server the server
* @return all players on the server
*/
public static Collection<ServerPlayer> all(MinecraftServer server) {
Objects.requireNonNull(server, "The server cannot be null");
// return an immutable collection to guard against accidental removals.
if (server.getPlayerList() != null) {
return Collections.unmodifiableCollection(server.getPlayerList().getPlayers());
}
return Collections.emptyList();
}
/**
* Gets all the players in a server world.
*
* <p>The returned collection is immutable.
*
* @param world the server world
* @return the players in the server world
*/
public static Collection<ServerPlayer> world(ServerLevel world) {
Objects.requireNonNull(world, "The world cannot be null");
// return an immutable collection to guard against accidental removals.
return Collections.unmodifiableCollection(world.players());
}
/**
* Gets all players tracking a chunk in a server world.
*
* @param world the server world
* @param pos the chunk in question
* @return the players tracking the chunk
*/
public static Collection<ServerPlayer> tracking(ServerLevel world, ChunkPos pos) {
Objects.requireNonNull(world, "The world cannot be null");
Objects.requireNonNull(pos, "The chunk pos cannot be null");
return world.getChunkSource().chunkMap.getPlayers(pos, false);
}
/**
* Gets all players tracking an entity in a server world.
*
* <p>The returned collection is immutable.
*
* <p><b>Warning</b>: If the provided entity is a player, it is not
* guaranteed by the contract that said player is included in the
* resulting stream.
*
* @param entity the entity being tracked
* @return the players tracking the entity
* @throws IllegalArgumentException if the entity is not in a server world
*/
public static Collection<ServerPlayer> tracking(Entity entity) {
Objects.requireNonNull(entity, "Entity cannot be null");
ChunkSource manager = entity.level.getChunkSource();
if (manager instanceof ServerChunkCache) {
ChunkMap storage = ((ServerChunkCache) manager).chunkMap;
EntityTrackerAccessor tracker = ((ThreadedAnvilChunkStorageAccessor) storage).getEntityMap().get(entity.getId());
// return an immutable collection to guard against accidental removals.
if (tracker != null) {
return Collections.unmodifiableCollection(tracker.getPlayersTracking()
.stream().map(ServerPlayerConnection::getPlayer).collect(Collectors.toSet()));
}
return Collections.emptySet();
}
throw new IllegalArgumentException("Only supported on server worlds!");
}
/**
* Gets all players tracking a block entity in a server world.
*
* @param blockEntity the block entity
* @return the players tracking the block position
* @throws IllegalArgumentException if the block entity is not in a server world
*/
public static Collection<ServerPlayer> tracking(BlockEntity blockEntity) {
Objects.requireNonNull(blockEntity, "BlockEntity cannot be null");
//noinspection ConstantConditions - IJ intrinsics don't know hasWorld == true will result in no null
if (!blockEntity.hasLevel() || blockEntity.getLevel().isClientSide()) {
throw new IllegalArgumentException("Only supported on server worlds!");
}
return tracking((ServerLevel) blockEntity.getLevel(), blockEntity.getBlockPos());
}
/**
* Gets all players tracking a block position in a server world.
*
* @param world the server world
* @param pos the block position
* @return the players tracking the block position
*/
public static Collection<ServerPlayer> tracking(ServerLevel world, BlockPos pos) {
Objects.requireNonNull(pos, "BlockPos cannot be null");
return tracking(world, new ChunkPos(pos));
}
/**
* Gets all players around a position in a world.
*
* <p>The distance check is done in the three-dimensional space instead of in the horizontal plane.
*
* @param world the world
* @param pos the position
* @param radius the maximum distance from the position in blocks
* @return the players around the position
*/
public static Collection<ServerPlayer> around(ServerLevel world, Vec3 pos, double radius) {
double radiusSq = radius * radius;
return world(world)
.stream()
.filter((p) -> p.distanceToSqr(pos) <= radiusSq)
.collect(Collectors.toList());
}
/**
* Gets all players around a position in a world.
*
* <p>The distance check is done in the three-dimensional space instead of in the horizontal plane.
*
* @param world the world
* @param pos the position (can be a block pos)
* @param radius the maximum distance from the position in blocks
* @return the players around the position
*/
public static Collection<ServerPlayer> around(ServerLevel world, Vec3i pos, double radius) {
double radiusSq = radius * radius;
return world(world)
.stream()
.filter((p) -> p.distanceToSqr(pos.getX(), pos.getY(), pos.getZ()) <= radiusSq)
.collect(Collectors.toList());
}
private PlayerLookup() {
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import java.util.List;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import com.seibel.lod.forge.fabric.api.event.Event;
import com.seibel.lod.forge.fabric.api.event.EventFactory;
/**
* Offers access to events related to the indication of a connected client's ability to receive packets in certain channels.
*/
public final class S2CPlayChannelEvents {
/**
* An event for the server play network handler receiving an update indicating the connected client's ability to receive packets in certain channels.
* This event may be invoked at any time after login and up to disconnection.
*/
public static final Event<Register> REGISTER = EventFactory.createArrayBacked(Register.class, callbacks -> (handler, sender, server, channels) -> {
for (Register callback : callbacks) {
callback.onChannelRegister(handler, sender, server, channels);
}
});
/**
* An event for the server play network handler receiving an update indicating the connected client's lack of ability to receive packets in certain channels.
* This event may be invoked at any time after login and up to disconnection.
*/
public static final Event<Unregister> UNREGISTER = EventFactory.createArrayBacked(Unregister.class, callbacks -> (handler, sender, server, channels) -> {
for (Unregister callback : callbacks) {
callback.onChannelUnregister(handler, sender, server, channels);
}
});
private S2CPlayChannelEvents() {
}
/**
* @see S2CPlayChannelEvents#REGISTER
*/
@FunctionalInterface
public interface Register {
void onChannelRegister(ServerGamePacketListenerImpl handler, PacketSender sender, MinecraftServer server, List<ResourceLocation> channels);
}
/**
* @see S2CPlayChannelEvents#UNREGISTER
*/
@FunctionalInterface
public interface Unregister {
void onChannelUnregister(ServerGamePacketListenerImpl handler, PacketSender sender, MinecraftServer server, List<ResourceLocation> channels);
}
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
import com.seibel.lod.forge.fabric.api.event.Event;
import com.seibel.lod.forge.fabric.api.event.EventFactory;
/**
* Offers access to events related to the connection to a client on a logical server while a client is logging in.
*/
public final class ServerLoginConnectionEvents {
/**
* Event indicating a connection entered the LOGIN state, ready for registering query response handlers.
*
* @see ServerLoginNetworking#registerReceiver(ServerLoginPacketListenerImpl, ResourceLocation, ServerLoginNetworking.LoginQueryResponseHandler)
*/
public static final Event<Init> INIT = EventFactory.createArrayBacked(Init.class, callbacks -> (handler, server) -> {
for (Init callback : callbacks) {
callback.onLoginInit(handler, server);
}
});
/**
* An event for the start of login queries of the server login network handler.
* This event may be used to register {@link ServerLoginNetworking.LoginQueryResponseHandler login query response handlers}
* using {@link ServerLoginNetworking#registerReceiver(ServerLoginPacketListenerImpl, ResourceLocation, ServerLoginNetworking.LoginQueryResponseHandler)}
* since this event is fired just before the first login query response is processed.
*
* <p>You may send login queries to the connected client using the provided {@link PacketSender}.
*/
public static final Event<QueryStart> QUERY_START = EventFactory.createArrayBacked(QueryStart.class, callbacks -> (handler, server, sender, synchronizer) -> {
for (QueryStart callback : callbacks) {
callback.onLoginStart(handler, server, sender, synchronizer);
}
});
/**
* An event for the disconnection of the server login network handler.
*
* <p>No packets should be sent when this event is invoked.
*/
public static final Event<Disconnect> DISCONNECT = EventFactory.createArrayBacked(Disconnect.class, callbacks -> (handler, server) -> {
for (Disconnect callback : callbacks) {
callback.onLoginDisconnect(handler, server);
}
});
private ServerLoginConnectionEvents() {
}
/**
* @see ServerLoginConnectionEvents#INIT
*/
@FunctionalInterface
public interface Init {
void onLoginInit(ServerLoginPacketListenerImpl handler, MinecraftServer server);
}
/**
* @see ServerLoginConnectionEvents#QUERY_START
*/
@FunctionalInterface
public interface QueryStart {
void onLoginStart(ServerLoginPacketListenerImpl handler, MinecraftServer server, PacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer);
}
/**
* @see ServerLoginConnectionEvents#DISCONNECT
*/
@FunctionalInterface
public interface Disconnect {
void onLoginDisconnect(ServerLoginPacketListenerImpl handler, MinecraftServer server);
}
}
@@ -0,0 +1,196 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Future;
import org.jetbrains.annotations.Nullable;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientLoginNetworking;
import com.seibel.lod.forge.fabric.impl.networking.server.ServerNetworkingImpl;
import com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
/**
* Offers access to login stage server-side networking functionalities.
*
* <p>Server-side networking functionalities include receiving serverbound query responses and sending clientbound query requests.
*
* @see ServerPlayNetworking
* @see ClientLoginNetworking
*/
public final class ServerLoginNetworking {
/**
* Registers a handler to a query response channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterGlobalReceiver(ResourceLocation)} to unregister the existing handler.
*
* @param channelName the id of the channel
* @param channelHandler the handler
* @return false if a handler is already registered to the channel
* @see ServerLoginNetworking#unregisterGlobalReceiver(ResourceLocation)
* @see ServerLoginNetworking#registerReceiver(ServerLoginPacketListenerImpl, ResourceLocation, LoginQueryResponseHandler)
*/
public static boolean registerGlobalReceiver(ResourceLocation channelName, LoginQueryResponseHandler channelHandler) {
return ServerNetworkingImpl.LOGIN.registerGlobalReceiver(channelName, channelHandler);
}
/**
* Removes the handler of a query response channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>The {@code channel} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel
* @see ServerLoginNetworking#registerGlobalReceiver(ResourceLocation, LoginQueryResponseHandler)
* @see ServerLoginNetworking#unregisterReceiver(ServerLoginPacketListenerImpl, ResourceLocation)
*/
@Nullable
public static ServerLoginNetworking.LoginQueryResponseHandler unregisterGlobalReceiver(ResourceLocation channelName) {
return ServerNetworkingImpl.LOGIN.unregisterGlobalReceiver(channelName);
}
/**
* Gets all channel names which global receivers are registered for.
* A global receiver is registered to all connections, in the present and future.
*
* @return all channel names which global receivers are registered for.
*/
public static Set<ResourceLocation> getGlobalReceivers() {
return ServerNetworkingImpl.LOGIN.getChannels();
}
/**
* Registers a handler to a query response channel.
*
* <p>If a handler is already registered to the {@code channelName}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterReceiver(ServerLoginPacketListenerImpl, ResourceLocation)} to unregister the existing handler.
*
* @param networkHandler the handler
* @param channelName the id of the channel
* @param responseHandler the handler
* @return false if a handler is already registered to the channel name
*/
public static boolean registerReceiver(ServerLoginPacketListenerImpl networkHandler, ResourceLocation channelName, LoginQueryResponseHandler responseHandler) {
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(channelName, responseHandler);
}
/**
* Removes the handler of a query response channel.
*
* <p>The {@code channelName} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel name
*/
@Nullable
public static ServerLoginNetworking.LoginQueryResponseHandler unregisterReceiver(ServerLoginPacketListenerImpl networkHandler, ResourceLocation channelName) {
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
return ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(channelName);
}
// Helper methods
/**
* Returns the <i>Minecraft</i> Server of a server login network handler.
*
* @param handler the server login network handler
*/
public static MinecraftServer getServer(ServerLoginPacketListenerImpl handler) {
Objects.requireNonNull(handler, "Network handler cannot be null");
return ((ServerLoginNetworkHandlerAccessor) handler).getServer();
}
private ServerLoginNetworking() {
}
@FunctionalInterface
public interface LoginQueryResponseHandler {
/**
* Handles an incoming query response from a client.
*
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
* Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft client instance.
*
* <p><b>Whether the client understood the query should be checked before reading from the payload of the packet.</b>
* @param server the server
* @param handler the network handler that received this packet, representing the player/client who sent the response
* @param understood whether the client understood the packet
* @param buf the payload of the packet
* @param synchronizer the synchronizer which may be used to delay log-in till a {@link Future} is completed.
* @param responseSender the packet sender
*/
void receive(MinecraftServer server, ServerLoginPacketListenerImpl handler, boolean understood, FriendlyByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender);
}
/**
* Allows blocking client log-in until all all futures passed into {@link LoginSynchronizer#waitFor(Future)} are completed.
*
* @apiNote this interface is not intended to be implemented by users of api.
*/
@FunctionalInterface
public interface LoginSynchronizer {
/**
* Allows blocking client log-in until the {@code future} is {@link Future#isDone() done}.
*
* <p>Since packet reception happens on netty's event loops, this allows handlers to
* perform logic on the Server Thread, etc. For instance, a handler can prepare an
* upcoming query request or check necessary login data on the server thread.</p>
*
* <p>Here is an example where the player log-in is blocked so that a credential check and
* building of a followup query request can be performed properly on the logical server
* thread before the player successfully logs in:
* <pre>{@code
* ServerLoginNetworking.registerGlobalReceiver(CHECK_CHANNEL, (server, handler, understood, buf, synchronizer, responseSender) -&gt; {
* if (!understood) {
* handler.disconnect(new LiteralText("Only accept clients that can check!"));
* return;
* }
*
* String checkMessage = buf.readString(32767);
*
* // Just send the CompletableFuture returned by the server's submit method
* synchronizer.waitFor(server.submit(() -&gt; {
* LoginInfoChecker checker = LoginInfoChecker.get(server);
*
* if (!checker.check(handler.getConnectionInfo(), checkMessage)) {
* handler.disconnect(new LiteralText("Invalid credentials!"));
* return;
* }
*
* responseSender.send(UPCOMING_CHECK, checker.buildSecondQueryPacket(handler, checkMessage));
* }));
* });
* }</pre>
* Usually it is enough to pass the return value for {@link net.minecraft.util.thread.BlockableEventLoop#submit(Runnable)} for {@code future}.</p>
*
* @param future the future that must be done before the player can log in
*/
void waitFor(Future<?> future);
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import com.seibel.lod.forge.fabric.api.event.Event;
import com.seibel.lod.forge.fabric.api.event.EventFactory;
/**
* Offers access to events related to the connection to a client on a logical server while a client is in game.
*/
public final class ServerPlayConnectionEvents {
/**
* Event indicating a connection entered the PLAY state, ready for registering channel handlers.
*
* @see ServerPlayNetworking#registerReceiver(ServerGamePacketListenerImpl, ResourceLocation, ServerPlayNetworking.PlayChannelHandler)
*/
public static final Event<Init> INIT = EventFactory.createArrayBacked(Init.class, callbacks -> (handler, server) -> {
for (Init callback : callbacks) {
callback.onPlayInit(handler, server);
}
});
/**
* An event for notification when the server play network handler is ready to send packets to the client.
*
* <p>At this stage, the network handler is ready to send packets to the client.
*/
public static final Event<Join> JOIN = EventFactory.createArrayBacked(Join.class, callbacks -> (handler, sender, server) -> {
for (Join callback : callbacks) {
callback.onPlayReady(handler, sender, server);
}
});
/**
* An event for the disconnection of the server play network handler.
*
* <p>No packets should be sent when this event is invoked.
*/
public static final Event<Disconnect> DISCONNECT = EventFactory.createArrayBacked(Disconnect.class, callbacks -> (handler, server) -> {
for (Disconnect callback : callbacks) {
callback.onPlayDisconnect(handler, server);
}
});
private ServerPlayConnectionEvents() {
}
@FunctionalInterface
public interface Init {
void onPlayInit(ServerGamePacketListenerImpl handler, MinecraftServer server);
}
@FunctionalInterface
public interface Join {
void onPlayReady(ServerGamePacketListenerImpl handler, PacketSender sender, MinecraftServer server);
}
@FunctionalInterface
public interface Disconnect {
void onPlayDisconnect(ServerGamePacketListenerImpl handler, MinecraftServer server);
}
}
@@ -0,0 +1,295 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
import java.util.Objects;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientPlayNetworking;
import com.seibel.lod.forge.fabric.impl.networking.server.ServerNetworkingImpl;
/**
* Offers access to play stage server-side networking functionalities.
*
* <p>Server-side networking functionalities include receiving serverbound packets, sending clientbound packets, and events related to server-side network handlers.
*
* <p>This class should be only used for the logical server.
*
* @see ServerLoginNetworking
* @see ClientPlayNetworking
*/
public final class ServerPlayNetworking {
/**
* Registers a handler to a channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>If a handler is already registered to the {@code channel}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterReceiver(ServerGamePacketListenerImpl, ResourceLocation)} to unregister the existing handler.
*
* @param channelName the id of the channel
* @param channelHandler the handler
* @return false if a handler is already registered to the channel
* @see ServerPlayNetworking#unregisterGlobalReceiver(ResourceLocation)
* @see ServerPlayNetworking#registerReceiver(ServerGamePacketListenerImpl, ResourceLocation, PlayChannelHandler)
*/
public static boolean registerGlobalReceiver(ResourceLocation channelName, PlayChannelHandler channelHandler) {
return ServerNetworkingImpl.PLAY.registerGlobalReceiver(channelName, channelHandler);
}
/**
* Removes the handler of a channel.
* A global receiver is registered to all connections, in the present and future.
*
* <p>The {@code channel} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel
* @see ServerPlayNetworking#registerGlobalReceiver(ResourceLocation, PlayChannelHandler)
* @see ServerPlayNetworking#unregisterReceiver(ServerGamePacketListenerImpl, ResourceLocation)
*/
@Nullable
public static PlayChannelHandler unregisterGlobalReceiver(ResourceLocation channelName) {
return ServerNetworkingImpl.PLAY.unregisterGlobalReceiver(channelName);
}
/**
* Gets all channel names which global receivers are registered for.
* A global receiver is registered to all connections, in the present and future.
*
* @return all channel names which global receivers are registered for.
*/
public static Set<ResourceLocation> getGlobalReceivers() {
return ServerNetworkingImpl.PLAY.getChannels();
}
/**
* Registers a handler to a channel.
* This method differs from {@link ServerPlayNetworking#registerGlobalReceiver(ResourceLocation, PlayChannelHandler)} since
* the channel handler will only be applied to the player represented by the {@link ServerGamePacketListenerImpl}.
*
* <p>For example, if you only register a receiver using this method when a {@linkplain ServerLoginNetworking#registerGlobalReceiver(ResourceLocation, ServerLoginNetworking.LoginQueryResponseHandler)}
* login response has been received, you should use {@link ServerPlayConnectionEvents#INIT} to register the channel handler.
*
* <p>If a handler is already registered to the {@code channelName}, this method will return {@code false}, and no change will be made.
* Use {@link #unregisterReceiver(ServerGamePacketListenerImpl, ResourceLocation)} to unregister the existing handler.
*
* @param networkHandler the handler
* @param channelName the id of the channel
* @param channelHandler the handler
* @return false if a handler is already registered to the channel name
* @see ServerPlayConnectionEvents#INIT
*/
public static boolean registerReceiver(ServerGamePacketListenerImpl networkHandler, ResourceLocation channelName, PlayChannelHandler channelHandler) {
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
return ServerNetworkingImpl.getAddon(networkHandler).registerChannel(channelName, channelHandler);
}
/**
* Removes the handler of a channel.
*
* <p>The {@code channelName} is guaranteed not to have a handler after this call.
*
* @param channelName the id of the channel
* @return the previous handler, or {@code null} if no handler was bound to the channel name
*/
@Nullable
public static PlayChannelHandler unregisterReceiver(ServerGamePacketListenerImpl networkHandler, ResourceLocation channelName) {
Objects.requireNonNull(networkHandler, "Network handler cannot be null");
return ServerNetworkingImpl.getAddon(networkHandler).unregisterChannel(channelName);
}
/**
* Gets all the channel names that the server can receive packets on.
*
* @param player the player
* @return All the channel names that the server can receive packets on
*/
public static Set<ResourceLocation> getReceived(ServerPlayer player) {
Objects.requireNonNull(player, "Server player entity cannot be null");
return getReceived(player.connection);
}
/**
* Gets all the channel names that the server can receive packets on.
*
* @param handler the network handler
* @return All the channel names that the server can receive packets on
*/
public static Set<ResourceLocation> getReceived(ServerGamePacketListenerImpl handler) {
Objects.requireNonNull(handler, "Server play network handler cannot be null");
return ServerNetworkingImpl.getAddon(handler).getReceivableChannels();
}
/**
* Gets all channel names that the connected client declared the ability to receive a packets on.
*
* @param player the player
* @return All the channel names the connected client declared the ability to receive a packets on
*/
public static Set<ResourceLocation> getSendable(ServerPlayer player) {
Objects.requireNonNull(player, "Server player entity cannot be null");
return getSendable(player.connection);
}
/**
* Gets all channel names that a the connected client declared the ability to receive a packets on.
*
* @param handler the network handler
* @return True if the connected client has declared the ability to receive a packet on the specified channel
*/
public static Set<ResourceLocation> getSendable(ServerGamePacketListenerImpl handler) {
Objects.requireNonNull(handler, "Server play network handler cannot be null");
return ServerNetworkingImpl.getAddon(handler).getSendableChannels();
}
/**
* Checks if the connected client declared the ability to receive a packet on a specified channel name.
*
* @param player the player
* @param channelName the channel name
* @return True if the connected client has declared the ability to receive a packet on the specified channel
*/
public static boolean canSend(ServerPlayer player, ResourceLocation channelName) {
Objects.requireNonNull(player, "Server player entity cannot be null");
return canSend(player.connection, channelName);
}
/**
* Checks if the connected client declared the ability to receive a packet on a specified channel name.
*
* @param handler the network handler
* @param channelName the channel name
* @return True if the connected client has declared the ability to receive a packet on the specified channel
*/
public static boolean canSend(ServerGamePacketListenerImpl handler, ResourceLocation channelName) {
Objects.requireNonNull(handler, "Server play network handler cannot be null");
Objects.requireNonNull(channelName, "Channel name cannot be null");
return ServerNetworkingImpl.getAddon(handler).getSendableChannels().contains(channelName);
}
/**
* Creates a packet which may be sent to a the connected client.
*
* @param channelName the channel name
* @param buf the packet byte buf which represents the payload of the packet
* @return a new packet
*/
public static Packet<?> createS2CPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
Objects.requireNonNull(channelName, "Channel cannot be null");
Objects.requireNonNull(buf, "Buf cannot be null");
return ServerNetworkingImpl.createPlayC2SPacket(channelName, buf);
}
/**
* Gets the packet sender which sends packets to the connected client.
*
* @param player the player
* @return the packet sender
*/
public static PacketSender getSender(ServerPlayer player) {
Objects.requireNonNull(player, "Server player entity cannot be null");
return getSender(player.connection);
}
/**
* Gets the packet sender which sends packets to the connected client.
*
* @param handler the network handler, representing the connection to the player/client
* @return the packet sender
*/
public static PacketSender getSender(ServerGamePacketListenerImpl handler) {
Objects.requireNonNull(handler, "Server play network handler cannot be null");
return ServerNetworkingImpl.getAddon(handler);
}
/**
* Sends a packet to a player.
*
* @param player the player to send the packet to
* @param channelName the channel of the packet
* @param buf the payload of the packet.
*/
public static void send(ServerPlayer player, ResourceLocation channelName, FriendlyByteBuf buf) {
Objects.requireNonNull(player, "Server player entity cannot be null");
Objects.requireNonNull(channelName, "Channel name cannot be null");
Objects.requireNonNull(buf, "Packet byte buf cannot be null");
player.connection.send(createS2CPacket(channelName, buf));
}
// Helper methods
/**
* Returns the <i>Minecraft</i> Server of a server play network handler.
*
* @param handler the server play network handler
*/
public static MinecraftServer getServer(ServerGamePacketListenerImpl handler) {
Objects.requireNonNull(handler, "Network handler cannot be null");
return handler.player.server;
}
private ServerPlayNetworking() {
}
@FunctionalInterface
public interface PlayChannelHandler {
/**
* Handles an incoming packet.
*
* <p>This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}.
* Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft server instance.
*
* <p>An example usage of this is to create an explosion where the player is looking:
* <pre>{@code
* ServerPlayNetworking.registerReceiver(new Identifier("mymod", "boom"), (server, player, handler, buf, responseSender) -&rt; {
* boolean fire = buf.readBoolean();
*
* // All operations on the server or world must be executed on the server thread
* server.execute(() -&rt; {
* ModPacketHandler.createExplosion(player, fire);
* });
* });
* }</pre>
* @param server the server
* @param player the player
* @param handler the network handler that received this packet, representing the player/client who sent the packet
* @param buf the payload of the packet
* @param responseSender the packet sender
*/
void receive(MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* The Networking API, version 1.
*
* <p>For login stage networking see {@link net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking}.
* For play stage networking see {@link net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking}.
*
* <p>For events related to the connection to a client see {@link net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents} for login stage
* or {@link net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents} for play stage.
*
* <p>For events related to the ability of a client to receive packets on a channel of a specific name see {@link net.fabricmc.fabric.api.networking.v1.S2CPlayChannelEvents}.
*/
package com.seibel.lod.forge.fabric.api.networking.v1;
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.util;
/**
* Represents a function that accepts an boolean-valued argument and produces a result.
*
* <p>This is the {@code boolean}-consuming primitive specialization for {@link java.util.function.Function}.
*/
@FunctionalInterface
public interface BooleanFunction<R> {
/**
* Applies this function to the given argument.
*
* @param value the function argument
* @return the function result
*/
R apply(boolean value);
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.util;
/**
* NBT type ID constants. Useful for filtering by value type in a few cases.
*
* <p>For the current list of types, check with {@link NbtElement#TYPES}.
*
* @see NbtCompound#contains(String, int)
* @see net.minecraft.nbt.NbtTypes#byId(int)
*/
public final class NbtType {
public static final int END = 0;
public static final int BYTE = 1;
public static final int SHORT = 2;
public static final int INT = 3;
public static final int LONG = 4;
public static final int FLOAT = 5;
public static final int DOUBLE = 6;
public static final int BYTE_ARRAY = 7;
public static final int STRING = 8;
public static final int LIST = 9;
public static final int COMPOUND = 10;
public static final int INT_ARRAY = 11;
public static final int LONG_ARRAY = 12;
/**
* Any numeric value: byte, short, int, long, float, double.
*
* @see NbtCompound#contains(String, int)
*/
public static final int NUMBER = 99;
private NbtType() { }
}
@@ -0,0 +1,139 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.api.util;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
/**
* Represents a boolean value which can be true, false or refer to a default value.
*/
public enum TriState {
/**
* Represents the boolean value of {@code false}.
*/
FALSE,
/**
* Represents a value that refers to a "default" value, often as a fallback.
*/
DEFAULT,
/**
* Represents the boolean value of {@code true}.
*/
TRUE;
/**
* Gets the corresponding tri-state from a boolean value.
*
* @param bool the boolean value
* @return {@link TriState#TRUE} or {@link TriState#FALSE} depending on the value of the boolean.
*/
public static TriState of(boolean bool) {
return bool ? TRUE : FALSE;
}
/**
* Gets a tri-state from a nullable boxed boolean.
*
* @param bool the boolean value
* @return {@link TriState#DEFAULT} if {@code null}.
* Otherwise {@link TriState#TRUE} or {@link TriState#FALSE} depending on the value of the boolean.
*/
public static TriState of(@Nullable Boolean bool) {
return bool == null ? DEFAULT : of(bool.booleanValue());
}
/**
* Gets the value of the tri-state.
*
* @return true if the tri-state is {@link TriState#TRUE}.
* Otherwise false.
*/
public boolean get() {
return this == TRUE;
}
/**
* Gets the value of the tri-state as a boxed, nullable boolean.
*
* @return {@code null} if {@link TriState#DEFAULT}.
* Otherwise {@code true} if {@link TriState#TRUE} or {@code false} if {@link TriState#FALSE}.
*/
@Nullable
public Boolean getBoxed() {
return this == DEFAULT ? null : this.get();
}
/**
* Gets the value of this tri-state.
* If the value is {@link TriState#DEFAULT} then use the supplied value.
*
* @param value the value to fallback to
* @return the value of the tri-state or the supplied value if {@link TriState#DEFAULT}.
*/
public boolean orElse(boolean value) {
return this == DEFAULT ? value : this.get();
}
/**
* Gets the value of this tri-state.
* If the value is {@link TriState#DEFAULT} then use the supplied value.
*
* @param supplier the supplier used to get the value to fallback to
* @return the value of the tri-state or the value of the supplier if the tri-state is {@link TriState#DEFAULT}.
*/
public boolean orElseGet(BooleanSupplier supplier) {
return this == DEFAULT ? supplier.getAsBoolean() : this.get();
}
/**
* Maps the boolean value of this tri-state if it is {@link TriState#TRUE} or {@link TriState#FALSE}.
*
* @param mapper the mapper to use
* @param <T> the type of object being supplier by the mapper
* @return an optional containing the mapped value; {@link Optional#empty()} if the tri-state is {@link TriState#DEFAULT} or the value provided by the mapper is {@code null}.
*/
public <T> Optional<T> map(BooleanFunction<@Nullable ? extends T> mapper) {
Objects.requireNonNull(mapper, "Mapper function cannot be null");
if (this == DEFAULT) {
return Optional.empty();
}
return Optional.ofNullable(mapper.apply(this.get()));
}
/**
* Gets the value of this tri-state, or throws an exception if this tri-state's value is {@link TriState#DEFAULT}.
*
* @param exceptionSupplier the supplying function that produces an exception to be thrown
* @param <X> Type of the exception to be thrown
* @return the value
* @throws X if the value is {@link TriState#DEFAULT}
*/
public <X extends Throwable> boolean orElseThrow(Supplier<X> exceptionSupplier) throws X {
if (this != DEFAULT) {
return this.get();
}
throw exceptionSupplier.get();
}
}
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.base.event;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import net.minecraft.resources.ResourceLocation;
import com.seibel.lod.forge.fabric.api.event.Event;
class ArrayBackedEvent<T> extends Event<T> {
static final Logger LOGGER = LoggerFactory.getLogger("fabric-api-base");
private final Function<T[], T> invokerFactory;
private final Object lock = new Object();
private T[] handlers;
/**
* Registered event phases.
*/
private final Map<ResourceLocation, EventPhaseData<T>> phases = new LinkedHashMap<>();
/**
* Phases sorted in the correct dependency order.
*/
private final List<EventPhaseData<T>> sortedPhases = new ArrayList<>();
@SuppressWarnings("unchecked")
ArrayBackedEvent(Class<? super T> type, Function<T[], T> invokerFactory) {
this.invokerFactory = invokerFactory;
this.handlers = (T[]) Array.newInstance(type, 0);
update();
}
void update() {
this.invoker = invokerFactory.apply(handlers);
}
@Override
public void register(T listener) {
register(DEFAULT_PHASE, listener);
}
@Override
public void register(ResourceLocation phaseResourceLocation, T listener) {
Objects.requireNonNull(phaseResourceLocation, "Tried to register a listener for a null phase!");
Objects.requireNonNull(listener, "Tried to register a null listener!");
synchronized (lock) {
getOrCreatePhase(phaseResourceLocation, true).addListener(listener);
rebuildInvoker(handlers.length + 1);
}
}
private EventPhaseData<T> getOrCreatePhase(ResourceLocation id, boolean sortIfCreate) {
EventPhaseData<T> phase = phases.get(id);
if (phase == null) {
phase = new EventPhaseData<>(id, handlers.getClass().getComponentType());
phases.put(id, phase);
sortedPhases.add(phase);
if (sortIfCreate) {
PhaseSorting.sortPhases(sortedPhases);
}
}
return phase;
}
private void rebuildInvoker(int newLength) {
// Rebuild handlers.
if (sortedPhases.size() == 1) {
// Special case with a single phase: use the array of the phase directly.
handlers = sortedPhases.get(0).listeners;
} else {
@SuppressWarnings("unchecked")
T[] newHandlers = (T[]) Array.newInstance(handlers.getClass().getComponentType(), newLength);
int newHandlersIndex = 0;
for (EventPhaseData<T> existingPhase : sortedPhases) {
int length = existingPhase.listeners.length;
System.arraycopy(existingPhase.listeners, 0, newHandlers, newHandlersIndex, length);
newHandlersIndex += length;
}
handlers = newHandlers;
}
// Rebuild invoker.
update();
}
@Override
public void addPhaseOrdering(ResourceLocation firstPhase, ResourceLocation secondPhase) {
Objects.requireNonNull(firstPhase, "Tried to add an ordering for a null phase.");
Objects.requireNonNull(secondPhase, "Tried to add an ordering for a null phase.");
if (firstPhase.equals(secondPhase)) throw new IllegalArgumentException("Tried to add a phase that depends on itself.");
synchronized (lock) {
EventPhaseData<T> first = getOrCreatePhase(firstPhase, false);
EventPhaseData<T> second = getOrCreatePhase(secondPhase, false);
first.subsequentPhases.add(second);
second.previousPhases.add(first);
PhaseSorting.sortPhases(this.sortedPhases);
rebuildInvoker(handlers.length);
}
}
}
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.base.event;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import net.minecraft.resources.ResourceLocation;
import com.seibel.lod.forge.fabric.api.event.Event;
public final class EventFactoryImpl {
private static final List<ArrayBackedEvent<?>> ARRAY_BACKED_EVENTS = new ArrayList<>();
private EventFactoryImpl() { }
public static void invalidate() {
ARRAY_BACKED_EVENTS.forEach(ArrayBackedEvent::update);
}
public static <T> Event<T> createArrayBacked(Class<? super T> type, Function<T[], T> invokerFactory) {
ArrayBackedEvent<T> event = new ArrayBackedEvent<>(type, invokerFactory);
ARRAY_BACKED_EVENTS.add(event);
return event;
}
public static void ensureContainsDefault(ResourceLocation[] defaultPhases) {
for (ResourceLocation id : defaultPhases) {
if (id.equals(Event.DEFAULT_PHASE)) {
return;
}
}
throw new IllegalArgumentException("The event phases must contain Event.DEFAULT_PHASE.");
}
public static void ensureNoDuplicates(ResourceLocation[] defaultPhases) {
for (int i = 0; i < defaultPhases.length; ++i) {
for (int j = i+1; j < defaultPhases.length; ++j) {
if (defaultPhases[i].equals(defaultPhases[j])) {
throw new IllegalArgumentException("Duplicate event phase: " + defaultPhases[i]);
}
}
}
}
// Code originally by sfPlayer1.
// Unfortunately, it's slightly slower than just passing an empty array in the first place.
private static <T> T buildEmptyInvoker(Class<T> handlerClass, Function<T[], T> invokerSetup) {
// find the functional interface method
Method funcIfMethod = null;
for (Method m : handlerClass.getMethods()) {
if ((m.getModifiers() & (Modifier.STRICT | Modifier.PRIVATE)) == 0) {
if (funcIfMethod != null) {
throw new IllegalStateException("Multiple virtual methods in " + handlerClass + "; cannot build empty invoker!");
}
funcIfMethod = m;
}
}
if (funcIfMethod == null) {
throw new IllegalStateException("No virtual methods in " + handlerClass + "; cannot build empty invoker!");
}
Object defValue = null;
try {
// concert to mh, determine its type without the "this" reference
MethodHandle target = MethodHandles.lookup().unreflect(funcIfMethod);
MethodType type = target.type().dropParameterTypes(0, 1);
if (type.returnType() != void.class) {
// determine default return value by invoking invokerSetup.apply(T[0]) with all-jvm-default args (null for refs, false for boolean, etc.)
// explicitCastArguments is being used to cast Object=null to the jvm default value for the correct type
// construct method desc (TLjava/lang/Object;Ljava/lang/Object;...)R where T = invoker ref ("this"), R = invoker ret type and args 1+ are Object for each non-"this" invoker arg
MethodType objTargetType = MethodType.genericMethodType(type.parameterCount()).changeReturnType(type.returnType()).insertParameterTypes(0, target.type().parameterType(0));
// explicit cast to translate to the invoker args from Object to their real type, inferring jvm default values
MethodHandle objTarget = MethodHandles.explicitCastArguments(target, objTargetType);
// build invocation args with 0 = "this", 1+ = null
Object[] args = new Object[target.type().parameterCount()];
//noinspection unchecked
args[0] = invokerSetup.apply((T[]) Array.newInstance(handlerClass, 0));
// retrieve default by invoking invokerSetup.apply(T[0]).targetName(def,def,...)
defValue = objTarget.invokeWithArguments(args);
}
} catch (Throwable t) {
throw new RuntimeException(t);
}
final Object returnValue = defValue;
//noinspection unchecked
return (T) Proxy.newProxyInstance(EventFactoryImpl.class.getClassLoader(), new Class[]{handlerClass},
(proxy, method, args) -> returnValue);
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.base.event;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.minecraft.resources.ResourceLocation;
/**
* Data of an {@link ArrayBackedEvent} phase.
*/
class EventPhaseData<T> {
final ResourceLocation id;
T[] listeners;
final List<EventPhaseData<T>> subsequentPhases = new ArrayList<>();
final List<EventPhaseData<T>> previousPhases = new ArrayList<>();
int visitStatus = 0; // 0: not visited, 1: visiting, 2: visited
@SuppressWarnings("unchecked")
EventPhaseData(ResourceLocation id, Class<?> listenerClass) {
this.id = id;
this.listeners = (T[]) Array.newInstance(listenerClass, 0);
}
void addListener(T listener) {
int oldLength = listeners.length;
listeners = Arrays.copyOf(listeners, oldLength + 1);
listeners[oldLength] = listener;
}
}
@@ -0,0 +1,164 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.base.event;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import com.google.common.annotations.VisibleForTesting;
import org.jetbrains.annotations.ApiStatus;
/**
* Contains phase-sorting logic for {@link ArrayBackedEvent}.
*/
@ApiStatus.Internal
public class PhaseSorting {
@VisibleForTesting
public static boolean ENABLE_CYCLE_WARNING = true;
/**
* Deterministically sort a list of phases.
* 1) Compute phase SCCs (i.e. cycles).
* 2) Sort phases by id within SCCs.
* 3) Sort SCCs with respect to each other by respecting constraints, and by id in case of a tie.
*/
static <T> void sortPhases(List<EventPhaseData<T>> sortedPhases) {
// FIRST KOSARAJU SCC VISIT
List<EventPhaseData<T>> toposort = new ArrayList<>(sortedPhases.size());
for (EventPhaseData<T> phase : sortedPhases) {
forwardVisit(phase, null, toposort);
}
clearStatus(toposort);
Collections.reverse(toposort);
// SECOND KOSARAJU SCC VISIT
Map<EventPhaseData<T>, PhaseScc<T>> phaseToScc = new IdentityHashMap<>();
for (EventPhaseData<T> phase : toposort) {
if (phase.visitStatus == 0) {
List<EventPhaseData<T>> sccPhases = new ArrayList<>();
// Collect phases in SCC.
backwardVisit(phase, sccPhases);
// Sort phases by id.
sccPhases.sort(Comparator.comparing(p -> p.id));
// Mark phases as belonging to this SCC.
PhaseScc<T> scc = new PhaseScc<>(sccPhases);
for (EventPhaseData<T> phaseInScc : sccPhases) {
phaseToScc.put(phaseInScc, scc);
}
}
}
clearStatus(toposort);
// Build SCC graph
for (PhaseScc<T> scc : phaseToScc.values()) {
for (EventPhaseData<T> phase : scc.phases) {
for (EventPhaseData<T> subsequentPhase : phase.subsequentPhases) {
PhaseScc<T> subsequentScc = phaseToScc.get(subsequentPhase);
if (subsequentScc != scc) {
scc.subsequentSccs.add(subsequentScc);
subsequentScc.inDegree++;
}
}
}
}
// Order SCCs according to priorities. When there is a choice, use the SCC with the lowest id.
// The priority queue contains all SCCs that currently have 0 in-degree.
PriorityQueue<PhaseScc<T>> pq = new PriorityQueue<>(Comparator.comparing(scc -> scc.phases.get(0).id));
sortedPhases.clear();
for (PhaseScc<T> scc : phaseToScc.values()) {
if (scc.inDegree == 0) {
pq.add(scc);
// Prevent adding the same SCC multiple times, as phaseToScc may contain the same value multiple times.
scc.inDegree = -1;
}
}
while (!pq.isEmpty()) {
PhaseScc<T> scc = pq.poll();
sortedPhases.addAll(scc.phases);
for (PhaseScc<T> subsequentScc : scc.subsequentSccs) {
subsequentScc.inDegree--;
if (subsequentScc.inDegree == 0) {
pq.add(subsequentScc);
}
}
}
}
private static <T> void forwardVisit(EventPhaseData<T> phase, EventPhaseData<T> parent, List<EventPhaseData<T>> toposort) {
if (phase.visitStatus == 0) {
// Not yet visited.
phase.visitStatus = 1;
for (EventPhaseData<T> data : phase.subsequentPhases) {
forwardVisit(data, phase, toposort);
}
toposort.add(phase);
phase.visitStatus = 2;
} else if (phase.visitStatus == 1 && ENABLE_CYCLE_WARNING) {
// Already visiting, so we have found a cycle.
ArrayBackedEvent.LOGGER.warn(String.format(
"Event phase ordering conflict detected.%nEvent phase %s is ordered both before and after event phase %s.",
phase.id,
parent.id
));
}
}
private static <T> void clearStatus(List<EventPhaseData<T>> phases) {
for (EventPhaseData<T> phase : phases) {
phase.visitStatus = 0;
}
}
private static <T> void backwardVisit(EventPhaseData<T> phase, List<EventPhaseData<T>> sccPhases) {
if (phase.visitStatus == 0) {
phase.visitStatus = 1;
sccPhases.add(phase);
for (EventPhaseData<T> data : phase.previousPhases) {
backwardVisit(data, sccPhases);
}
}
}
private static class PhaseScc<T> {
final List<EventPhaseData<T>> phases;
final List<PhaseScc<T>> subsequentSccs = new ArrayList<>();
int inDegree = 0;
private PhaseScc(List<EventPhaseData<T>> phases) {
this.phases = phases;
}
}
}
@@ -0,0 +1,205 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import io.netty.util.AsciiString;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketByteBufs;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketSender;
import net.minecraft.ResourceLocationException;
import net.minecraft.network.Connection;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation;
/**
* A network addon which is aware of the channels the other side may receive.
*
* @param <H> the channel handler type
*/
public abstract class AbstractChanneledNetworkAddon<H> extends AbstractNetworkAddon<H> implements PacketSender {
protected final Connection connection;
protected final GlobalReceiverRegistry<H> receiver;
protected final Set<ResourceLocation> sendableChannels;
protected final Set<ResourceLocation> sendableChannelsView;
protected AbstractChanneledNetworkAddon(GlobalReceiverRegistry<H> receiver, Connection connection, String description) {
this(receiver, connection, new HashSet<>(), description);
}
protected AbstractChanneledNetworkAddon(GlobalReceiverRegistry<H> receiver, Connection connection, Set<ResourceLocation> sendableChannels, String description) {
super(receiver, description);
this.connection = connection;
this.receiver = receiver;
this.sendableChannels = sendableChannels;
this.sendableChannelsView = Collections.unmodifiableSet(sendableChannels);
}
public abstract void lateInit();
protected void registerPendingChannels(ChannelInfoHolder holder) {
final Collection<ResourceLocation> pending = holder.getPendingChannelsNames();
if (!pending.isEmpty()) {
register(new ArrayList<>(pending));
pending.clear();
}
}
// always supposed to handle async!
protected boolean handle(ResourceLocation channelName, FriendlyByteBuf originalBuf) {
this.logger.debug("Handling inbound packet from channel with name \"{}\"", channelName);
// Handle reserved packets
if (NetworkingImpl.REGISTER_CHANNEL.equals(channelName)) {
this.receiveRegistration(true, PacketByteBufs.slice(originalBuf));
return true;
}
if (NetworkingImpl.UNREGISTER_CHANNEL.equals(channelName)) {
this.receiveRegistration(false, PacketByteBufs.slice(originalBuf));
return true;
}
@Nullable H handler = this.getHandler(channelName);
if (handler == null) {
return false;
}
FriendlyByteBuf buf = PacketByteBufs.slice(originalBuf);
try {
this.receive(handler, buf);
} catch (Throwable ex) {
this.logger.error("Encountered exception while handling in channel with name \"{}\"", channelName, ex);
throw ex;
}
return true;
}
protected abstract void receive(H handler, FriendlyByteBuf buf);
protected void sendInitialChannelRegistrationPacket() {
final FriendlyByteBuf buf = this.createRegistrationPacket(this.getReceivableChannels());
if (buf != null) {
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
}
}
@Nullable
protected FriendlyByteBuf createRegistrationPacket(Collection<ResourceLocation> channels) {
if (channels.isEmpty()) {
return null;
}
FriendlyByteBuf buf = PacketByteBufs.create();
boolean first = true;
for (ResourceLocation channel : channels) {
if (first) {
first = false;
} else {
buf.writeByte(0);
}
buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII));
}
return buf;
}
// wrap in try with res (buf)
protected void receiveRegistration(boolean register, FriendlyByteBuf buf) {
List<ResourceLocation> ids = new ArrayList<>();
StringBuilder active = new StringBuilder();
while (buf.isReadable()) {
byte b = buf.readByte();
if (b != 0) {
active.append(AsciiString.b2c(b));
} else {
this.addId(ids, active);
active = new StringBuilder();
}
}
this.addId(ids, active);
this.schedule(register ? () -> register(ids) : () -> unregister(ids));
}
void register(List<ResourceLocation> ids) {
this.sendableChannels.addAll(ids);
this.invokeRegisterEvent(ids);
}
void unregister(List<ResourceLocation> ids) {
this.sendableChannels.removeAll(ids);
this.invokeUnregisterEvent(ids);
}
@Override
public void sendPacket(Packet<?> packet) {
Objects.requireNonNull(packet, "Packet cannot be null");
this.connection.send(packet);
}
@Override
public void sendPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> callback) {
Objects.requireNonNull(packet, "Packet cannot be null");
this.connection.send(packet, callback);
}
/**
* Schedules a task to run on the main thread.
*/
protected abstract void schedule(Runnable task);
protected abstract void invokeRegisterEvent(List<ResourceLocation> ids);
protected abstract void invokeUnregisterEvent(List<ResourceLocation> ids);
private void addId(List<ResourceLocation> ids, StringBuilder sb) {
String literal = sb.toString();
try {
ids.add(new ResourceLocation(literal));
} catch (ResourceLocationException ex) {
this.logger.warn("Received invalid channel identifier \"{}\" from connection {}", literal, this.connection);
}
}
public Set<ResourceLocation> getSendableChannels() {
return this.sendableChannelsView;
}
}
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* A network addon is a simple abstraction to hold information about a player's registered channels.
*
* @param <H> the channel handler type
*/
public abstract class AbstractNetworkAddon<H> {
protected final GlobalReceiverRegistry<H> receiver;
protected final Logger logger;
// A lock is used due to possible access on netty's event loops and game thread at same times such as during dynamic registration
private final ReadWriteLock lock = new ReentrantReadWriteLock();
// Sync map should be fine as there is little read write competition
// All access to this map is guarded by the lock
private final Map<ResourceLocation, H> handlers = new HashMap<>();
private final AtomicBoolean disconnected = new AtomicBoolean(); // blocks redundant disconnect notifications
protected AbstractNetworkAddon(GlobalReceiverRegistry<H> receiver, String description) {
this.receiver = receiver;
this.logger = LoggerFactory.getLogger(description);
}
@Nullable
public H getHandler(ResourceLocation channel) {
Lock lock = this.lock.readLock();
lock.lock();
try {
return this.handlers.get(channel);
} finally {
lock.unlock();
}
}
public boolean registerChannel(ResourceLocation channelName, H handler) {
Objects.requireNonNull(channelName, "Channel name cannot be null");
Objects.requireNonNull(handler, "Packet handler cannot be null");
if (this.isReservedChannel(channelName)) {
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
}
Lock lock = this.lock.writeLock();
lock.lock();
try {
final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null;
if (replaced) {
this.handleRegistration(channelName);
}
return replaced;
} finally {
lock.unlock();
}
}
public H unregisterChannel(ResourceLocation channelName) {
Objects.requireNonNull(channelName, "Channel name cannot be null");
if (this.isReservedChannel(channelName)) {
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
}
Lock lock = this.lock.writeLock();
lock.lock();
try {
final H removed = this.handlers.remove(channelName);
if (removed != null) {
this.handleUnregistration(channelName);
}
return removed;
} finally {
lock.unlock();
}
}
public Set<ResourceLocation> getReceivableChannels() {
Lock lock = this.lock.readLock();
lock.lock();
try {
return new HashSet<>(this.handlers.keySet());
} finally {
lock.unlock();
}
}
protected abstract void handleRegistration(ResourceLocation channelName);
protected abstract void handleUnregistration(ResourceLocation channelName);
public final void handleDisconnect() {
if (disconnected.compareAndSet(false, true)) {
invokeDisconnectEvent();
}
}
protected abstract void invokeDisconnectEvent();
/**
* Checks if a channel is considered a "reserved" channel.
* A reserved channel such as "minecraft:(un)register" has special handling and should not have any channel handlers registered for it.
*
* @param channelName the channel name
* @return whether the channel is reserved
*/
protected abstract boolean isReservedChannel(ResourceLocation channelName);
}
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
import java.util.Collection;
import net.minecraft.resources.ResourceLocation;
public interface ChannelInfoHolder {
/**
* @return Channels which are declared as receivable by the other side but have not been declared yet.
*/
Collection<ResourceLocation> getPendingChannelsNames();
}
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
public interface DisconnectPacketSource {
Packet<?> createDisconnectPacket(Component message);
}
@@ -0,0 +1,173 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
public final class GlobalReceiverRegistry<H> {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Map<ResourceLocation, H> handlers;
private final Set<AbstractNetworkAddon<H>> trackedAddons = new HashSet<>();
public GlobalReceiverRegistry() {
this(new HashMap<>()); // sync map should be fine as there is little read write competitions
}
public GlobalReceiverRegistry(Map<ResourceLocation, H> map) {
this.handlers = map;
}
@Nullable
public H getHandler(ResourceLocation channelName) {
Lock lock = this.lock.readLock();
lock.lock();
try {
return this.handlers.get(channelName);
} finally {
lock.unlock();
}
}
public boolean registerGlobalReceiver(ResourceLocation channelName, H handler) {
Objects.requireNonNull(channelName, "Channel name cannot be null");
Objects.requireNonNull(handler, "Channel handler cannot be null");
if (NetworkingImpl.isReservedPlayChannel(channelName)) {
throw new IllegalArgumentException(String.format("Cannot register handler for reserved channel with name \"%s\"", channelName));
}
Lock lock = this.lock.writeLock();
lock.lock();
try {
final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null;
if (!replaced) {
this.handleRegistration(channelName, handler);
}
return replaced;
} finally {
lock.unlock();
}
}
public H unregisterGlobalReceiver(ResourceLocation channelName) {
Objects.requireNonNull(channelName, "Channel name cannot be null");
if (NetworkingImpl.isReservedPlayChannel(channelName)) {
throw new IllegalArgumentException(String.format("Cannot unregister packet handler for reserved channel with name \"%s\"", channelName));
}
Lock lock = this.lock.writeLock();
lock.lock();
try {
final H removed = this.handlers.remove(channelName);
if (removed != null) {
this.handleUnregistration(channelName);
}
return removed;
} finally {
lock.unlock();
}
}
public Map<ResourceLocation, H> getHandlers() {
Lock lock = this.lock.writeLock();
lock.lock();
try {
return new HashMap<>(this.handlers);
} finally {
lock.unlock();
}
}
public Set<ResourceLocation> getChannels() {
Lock lock = this.lock.readLock();
lock.lock();
try {
return new HashSet<>(this.handlers.keySet());
} finally {
lock.unlock();
}
}
// State tracking methods
public void startSession(AbstractNetworkAddon<H> addon) {
Lock lock = this.lock.writeLock();
lock.lock();
try {
this.trackedAddons.add(addon);
} finally {
lock.unlock();
}
}
public void endSession(AbstractNetworkAddon<H> addon) {
Lock lock = this.lock.writeLock();
lock.lock();
try {
this.trackedAddons.remove(addon);
} finally {
lock.unlock();
}
}
private void handleRegistration(ResourceLocation channelName, H handler) {
Lock lock = this.lock.writeLock();
lock.lock();
try {
for (AbstractNetworkAddon<H> addon : this.trackedAddons) {
addon.registerChannel(channelName, handler);
}
} finally {
lock.unlock();
}
}
private void handleUnregistration(ResourceLocation channelName) {
Lock lock = this.lock.writeLock();
lock.lock();
try {
for (AbstractNetworkAddon<H> addon : this.trackedAddons) {
addon.unregisterChannel(channelName);
}
} finally {
lock.unlock();
}
}
}
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
public interface NetworkHandlerExtensions {
AbstractNetworkAddon<?> getAddon();
}
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import net.minecraft.network.FriendlyByteBuf;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketByteBufs;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerLoginConnectionEvents;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerLoginNetworking;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.resources.ResourceLocation;
public final class NetworkingImpl {
public static final String MOD_ID = "fabric-networking-api-v1";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
/**
* Id of packet used to register supported channels.
*/
public static final ResourceLocation REGISTER_CHANNEL = new ResourceLocation("minecraft", "register");
/**
* Id of packet used to unregister supported channels.
*/
public static final ResourceLocation UNREGISTER_CHANNEL = new ResourceLocation("minecraft", "unregister");
/**
* Id of the packet used to declare all currently supported channels.
* Dynamic registration of supported channels is still allowed using {@link NetworkingImpl#REGISTER_CHANNEL} and {@link NetworkingImpl#UNREGISTER_CHANNEL}.
*/
public static final ResourceLocation EARLY_REGISTRATION_CHANNEL = new ResourceLocation(MOD_ID, "early_registration");
public static void init() {
// Login setup
ServerLoginConnectionEvents.QUERY_START.register((handler, server, sender, synchronizer) -> {
// Send early registration packet
FriendlyByteBuf buf = PacketByteBufs.create();
Collection<ResourceLocation> channelsNames = ServerPlayNetworking.getGlobalReceivers();
buf.writeVarInt(channelsNames.size());
for (ResourceLocation id : channelsNames) {
buf.writeResourceLocation(id);
}
sender.sendPacket(EARLY_REGISTRATION_CHANNEL, buf);
NetworkingImpl.LOGGER.debug("Sent accepted channels to the client for \"{}\"", handler.getUserName());
});
ServerLoginNetworking.registerGlobalReceiver(EARLY_REGISTRATION_CHANNEL, (server, handler, understood, buf, synchronizer, sender) -> {
if (!understood) {
// The client is likely a vanilla client.
return;
}
int n = buf.readVarInt();
List<ResourceLocation> ids = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
ids.add(buf.readResourceLocation());
}
((ChannelInfoHolder) handler.getConnection()).getPendingChannelsNames().addAll(ids);
NetworkingImpl.LOGGER.debug("Received accepted channels from the client for \"{}\"", handler.getUserName());
});
}
public static boolean isReservedPlayChannel(ResourceLocation channelName) {
return channelName.equals(REGISTER_CHANNEL) || channelName.equals(UNREGISTER_CHANNEL);
}
}
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking;
import net.minecraft.network.protocol.Packet;
public interface PacketCallbackListener {
/**
* Called after a packet has been sent.
*
* @param packet the packet
*/
void sent(Packet<?> packet);
}
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking.client;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientLoginNetworking;
import com.seibel.lod.forge.fabric.api.networking.v1.FutureListeners;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketByteBufs;
import com.seibel.lod.forge.fabric.impl.networking.AbstractNetworkAddon;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket;
import net.minecraft.network.protocol.login.ServerboundCustomQueryPacket;
import net.minecraft.resources.ResourceLocation;
public final class ClientLoginNetworkAddon extends AbstractNetworkAddon<ClientLoginNetworking.LoginQueryRequestHandler> {
private final ClientHandshakePacketListenerImpl handler;
private final Minecraft client;
private boolean firstResponse = true;
public ClientLoginNetworkAddon(ClientHandshakePacketListenerImpl handler, Minecraft client) {
super(ClientNetworkingImpl.LOGIN, "ClientLoginNetworkAddon for Client");
this.handler = handler;
this.client = client;
ClientLoginConnectionEvents.INIT.invoker().onLoginStart(this.handler, this.client);
this.receiver.startSession(this);
}
public boolean handlePacket(ClientboundCustomQueryPacket packet) {
return handlePacket(packet.getTransactionId(), packet.getIdentifier(), packet.getData());
}
private boolean handlePacket(int queryId, ResourceLocation channelName, FriendlyByteBuf originalBuf) {
this.logger.debug("Handling inbound login response with id {} and channel with name {}", queryId, channelName);
if (this.firstResponse) {
// Register global handlers
for (Map.Entry<ResourceLocation, ClientLoginNetworking.LoginQueryRequestHandler> entry : ClientNetworkingImpl.LOGIN.getHandlers().entrySet()) {
ClientLoginNetworking.registerReceiver(entry.getKey(), entry.getValue());
}
ClientLoginConnectionEvents.QUERY_START.invoker().onLoginQueryStart(this.handler, this.client);
this.firstResponse = false;
}
@Nullable ClientLoginNetworking.LoginQueryRequestHandler handler = this.getHandler(channelName);
if (handler == null) {
return false;
}
FriendlyByteBuf buf = PacketByteBufs.slice(originalBuf);
List<GenericFutureListener<? extends Future<? super Void>>> futureListeners = new ArrayList<>();
try {
CompletableFuture<@Nullable FriendlyByteBuf> future = handler.receive(this.client, this.handler, buf, futureListeners::add);
future.thenAccept(result -> {
ServerboundCustomQueryPacket packet = new ServerboundCustomQueryPacket(queryId, result);
GenericFutureListener<? extends Future<? super Void>> listener = null;
for (GenericFutureListener<? extends Future<? super Void>> each : futureListeners) {
listener = FutureListeners.union(listener, each);
}
this.handler.getConnection().send(packet, listener);
});
} catch (Throwable ex) {
this.logger.error("Encountered exception while handling in channel with name \"{}\"", channelName, ex);
throw ex;
}
return true;
}
@Override
protected void handleRegistration(ResourceLocation channelName) {
}
@Override
protected void handleUnregistration(ResourceLocation channelName) {
}
@Override
protected void invokeDisconnectEvent() {
ClientLoginConnectionEvents.DISCONNECT.invoker().onLoginDisconnect(this.handler, this.client);
this.receiver.endSession(this);
}
public void handlePlayTransition() {
this.receiver.endSession(this);
}
@Override
protected boolean isReservedChannel(ResourceLocation channelName) {
return false;
}
}
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking.client;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientLoginNetworking;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientPlayNetworking;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketByteBufs;
import com.seibel.lod.forge.fabric.impl.networking.ChannelInfoHolder;
import com.seibel.lod.forge.fabric.impl.networking.GlobalReceiverRegistry;
import com.seibel.lod.forge.fabric.impl.networking.NetworkHandlerExtensions;
import com.seibel.lod.forge.fabric.impl.networking.NetworkingImpl;
import com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.ConnectScreenAccessor;
import com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.MinecraftClientAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.ConnectScreen;
import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.Connection;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
public final class ClientNetworkingImpl {
public static final GlobalReceiverRegistry<ClientLoginNetworking.LoginQueryRequestHandler> LOGIN = new GlobalReceiverRegistry<>();
public static final GlobalReceiverRegistry<ClientPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>();
private static ClientPlayNetworkAddon currentPlayAddon;
public static ClientPlayNetworkAddon getAddon(ClientPacketListener handler) {
return (ClientPlayNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
}
public static ClientLoginNetworkAddon getAddon(ClientHandshakePacketListenerImpl handler) {
return (ClientLoginNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
}
public static Packet<?> createPlayC2SPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
return new ServerboundCustomPayloadPacket(channelName, buf);
}
/**
* Due to the way logging into a integrated or remote dedicated server will differ, we need to obtain the login client connection differently.
*/
@Nullable
public static Connection getLoginConnection() {
final Connection connection = ((MinecraftClientAccessor) Minecraft.getInstance()).getConnection();
// Check if we are connecting to an integrated server. This will set the field on MinecraftClient
if (connection != null) {
return connection;
} else {
// We are probably connecting to a remote server.
// Check if the ConnectScreen is the currentScreen to determine that:
if (Minecraft.getInstance().screen instanceof ConnectScreen) {
return ((ConnectScreenAccessor) Minecraft.getInstance().screen).getConnection();
}
}
// We are not connected to a server at all.
return null;
}
@Nullable
public static ClientPlayNetworkAddon getClientPlayAddon() {
// Since Minecraft can be a bit weird, we need to check for the play addon in a few ways:
// If the client's player is set this will work
if (Minecraft.getInstance().getConnection() != null) {
currentPlayAddon = null; // Shouldn't need this anymore
return ClientNetworkingImpl.getAddon(Minecraft.getInstance().getConnection());
}
// We haven't hit the end of onGameJoin yet, use our backing field here to access the network handler
if (currentPlayAddon != null) {
return currentPlayAddon;
}
// We are not in play stage
return null;
}
public static void setClientPlayAddon(ClientPlayNetworkAddon addon) {
currentPlayAddon = addon;
}
public static void clientInit() {
// Reference cleanup for the locally stored addon if we are disconnected
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
currentPlayAddon = null;
});
// Register a login query handler for early channel registration.
ClientLoginNetworking.registerGlobalReceiver(NetworkingImpl.EARLY_REGISTRATION_CHANNEL, (client, handler, buf, listenerAdder) -> {
int n = buf.readVarInt();
List<ResourceLocation> ids = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
ids.add(buf.readResourceLocation());
}
((ChannelInfoHolder) handler.getConnection()).getPendingChannelsNames().addAll(ids);
NetworkingImpl.LOGGER.debug("Received accepted channels from the server");
FriendlyByteBuf response = PacketByteBufs.create();
Collection<ResourceLocation> channels = ClientPlayNetworking.getGlobalReceivers();
response.writeVarInt(channels.size());
for (ResourceLocation id : channels) {
response.writeResourceLocation(id);
}
NetworkingImpl.LOGGER.debug("Sent accepted channels to the server");
return CompletableFuture.completedFuture(response);
});
}
}
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking.client;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.seibel.lod.forge.fabric.api.client.networking.v1.C2SPlayChannelEvents;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import com.seibel.lod.forge.fabric.api.client.networking.v1.ClientPlayNetworking;
import com.seibel.lod.forge.fabric.impl.networking.AbstractChanneledNetworkAddon;
import com.seibel.lod.forge.fabric.impl.networking.ChannelInfoHolder;
import com.seibel.lod.forge.fabric.impl.networking.NetworkingImpl;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
public final class ClientPlayNetworkAddon extends AbstractChanneledNetworkAddon<ClientPlayNetworking.PlayChannelHandler> {
private final ClientPacketListener handler;
private final Minecraft client;
private boolean sentInitialRegisterPacket;
public ClientPlayNetworkAddon(ClientPacketListener handler, Minecraft client) {
super(ClientNetworkingImpl.PLAY, handler.getConnection(), "ClientPlayNetworkAddon for " + handler.getLocalGameProfile().getName());
this.handler = handler;
this.client = client;
// Must register pending channels via lateinit
this.registerPendingChannels((ChannelInfoHolder) this.connection);
// Register global receivers and attach to session
this.receiver.startSession(this);
}
@Override
public void lateInit() {
for (Map.Entry<ResourceLocation, ClientPlayNetworking.PlayChannelHandler> entry : this.receiver.getHandlers().entrySet()) {
this.registerChannel(entry.getKey(), entry.getValue());
}
ClientPlayConnectionEvents.INIT.invoker().onPlayInit(this.handler, this.client);
}
public void onServerReady() {
ClientPlayConnectionEvents.JOIN.invoker().onPlayReady(this.handler, this, this.client);
// The client cannot send any packets, including `minecraft:register` until after GameJoinS2CPacket is received.
this.sendInitialChannelRegistrationPacket();
this.sentInitialRegisterPacket = true;
}
/**
* Handles an incoming packet.
*
* @param packet the packet to handle
* @return true if the packet has been handled
*/
public boolean handle(ClientboundCustomPayloadPacket packet) {
// Do not handle the packet on game thread
if (this.client.isSameThread()) {
return false;
}
FriendlyByteBuf buf = packet.getData();
try {
return this.handle(packet.getIdentifier(), buf);
} finally {
buf.release();
}
}
@Override
protected void receive(ClientPlayNetworking.PlayChannelHandler handler, FriendlyByteBuf buf) {
handler.receive(this.client, this.handler, buf, this);
}
// impl details
@Override
protected void schedule(Runnable task) {
Minecraft.getInstance().execute(task);
}
@Override
public Packet<?> createPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
return ClientPlayNetworking.createC2SPacket(channelName, buf);
}
@Override
protected void invokeRegisterEvent(List<ResourceLocation> ids) {
C2SPlayChannelEvents.REGISTER.invoker().onChannelRegister(this.handler, this, this.client, ids);
}
@Override
protected void invokeUnregisterEvent(List<ResourceLocation> ids) {
C2SPlayChannelEvents.UNREGISTER.invoker().onChannelUnregister(this.handler, this, this.client, ids);
}
@Override
protected void handleRegistration(ResourceLocation channelName) {
// If we can already send packets, immediately send the register packet for this channel
if (this.sentInitialRegisterPacket) {
final FriendlyByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
if (buf != null) {
this.sendPacket(NetworkingImpl.REGISTER_CHANNEL, buf);
}
}
}
@Override
protected void handleUnregistration(ResourceLocation channelName) {
// If we can already send packets, immediately send the unregister packet for this channel
if (this.sentInitialRegisterPacket) {
final FriendlyByteBuf buf = this.createRegistrationPacket(Collections.singleton(channelName));
if (buf != null) {
this.sendPacket(NetworkingImpl.UNREGISTER_CHANNEL, buf);
}
}
}
@Override
protected void invokeDisconnectEvent() {
ClientPlayConnectionEvents.DISCONNECT.invoker().onPlayDisconnect(this.handler, this.client);
this.receiver.endSession(this);
}
@Override
protected boolean isReservedChannel(ResourceLocation channelName) {
return NetworkingImpl.isReservedPlayChannel(channelName);
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking.server;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Tracks the current query id used for login query responses.
*/
interface QueryIdFactory {
static QueryIdFactory create() {
return new QueryIdFactory() {
private final AtomicInteger currentId = new AtomicInteger();
@Override
public int nextId() {
return this.currentId.getAndIncrement();
}
};
}
// called async prob.
int nextId();
}
@@ -0,0 +1,207 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking.server;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import io.netty.util.concurrent.GenericFutureListener;
import org.jetbrains.annotations.Nullable;
import net.minecraft.network.Connection;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket;
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
import net.minecraft.network.protocol.login.ServerboundCustomQueryPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketByteBufs;
import com.seibel.lod.forge.fabric.api.networking.v1.PacketSender;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerLoginConnectionEvents;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerLoginNetworking;
import com.seibel.lod.forge.fabric.impl.networking.AbstractNetworkAddon;
import com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.LoginQueryResponseC2SPacketAccessor;
import com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.ServerLoginNetworkHandlerAccessor;
public final class ServerLoginNetworkAddon extends AbstractNetworkAddon<ServerLoginNetworking.LoginQueryResponseHandler> implements PacketSender {
private final Connection connection;
private final ServerLoginPacketListenerImpl handler;
private final MinecraftServer server;
private final QueryIdFactory queryIdFactory;
private final Collection<Future<?>> waits = new ConcurrentLinkedQueue<>();
private final Map<Integer, ResourceLocation> channels = new ConcurrentHashMap<>();
private boolean firstQueryTick = true;
public ServerLoginNetworkAddon(ServerLoginPacketListenerImpl handler) {
super(ServerNetworkingImpl.LOGIN, "ServerLoginNetworkAddon for " + handler.getUserName());
this.connection = handler.connection;
this.handler = handler;
this.server = ((ServerLoginNetworkHandlerAccessor) handler).getServer();
this.queryIdFactory = QueryIdFactory.create();
ServerLoginConnectionEvents.INIT.invoker().onLoginInit(handler, this.server);
this.receiver.startSession(this);
}
// return true if no longer ticks query
public boolean queryTick() {
if (this.firstQueryTick) {
// Send the compression packet now so clients receive compressed login queries
this.sendCompressionPacket();
// Register global receivers.
for (Map.Entry<ResourceLocation, ServerLoginNetworking.LoginQueryResponseHandler> entry : ServerNetworkingImpl.LOGIN.getHandlers().entrySet()) {
ServerLoginNetworking.registerReceiver(this.handler, entry.getKey(), entry.getValue());
}
ServerLoginConnectionEvents.QUERY_START.invoker().onLoginStart(this.handler, this.server, this, this.waits::add);
this.firstQueryTick = false;
}
AtomicReference<Throwable> error = new AtomicReference<>();
this.waits.removeIf(future -> {
if (!future.isDone()) {
return false;
}
try {
future.get();
} catch (ExecutionException ex) {
Throwable caught = ex.getCause();
error.getAndUpdate(oldEx -> {
if (oldEx == null) {
return caught;
}
oldEx.addSuppressed(caught);
return oldEx;
});
} catch (InterruptedException | CancellationException ignored) {
// ignore
}
return true;
});
return this.channels.isEmpty() && this.waits.isEmpty();
}
private void sendCompressionPacket() {
// Compression is not needed for local transport
if (this.server.getCompressionThreshold() >= 0 && !this.connection.isMemoryConnection()) {
this.connection.send(new ClientboundLoginCompressionPacket(this.server.getCompressionThreshold()), (channelFuture) ->
this.connection.setupCompression(this.server.getCompressionThreshold(), true)
);
}
}
/**
* Handles an incoming query response during login.
*
* @param packet the packet to handle
* @return true if the packet was handled
*/
public boolean handle(ServerboundCustomQueryPacket packet) {
LoginQueryResponseC2SPacketAccessor access = (LoginQueryResponseC2SPacketAccessor) packet;
return handle(access.getTransactionId(), access.getData());
}
private boolean handle(int queryId, @Nullable FriendlyByteBuf originalBuf) {
this.logger.debug("Handling inbound login query with id {}", queryId);
ResourceLocation channel = this.channels.remove(queryId);
if (channel == null) {
this.logger.warn("Query ID {} was received but no query has been associated in {}!", queryId, this.connection);
return false;
}
boolean understood = originalBuf != null;
@Nullable ServerLoginNetworking.LoginQueryResponseHandler handler = ServerNetworkingImpl.LOGIN.getHandler(channel);
if (handler == null) {
return false;
}
FriendlyByteBuf buf = understood ? PacketByteBufs.slice(originalBuf) : PacketByteBufs.empty();
try {
handler.receive(this.server, this.handler, understood, buf, this.waits::add, this);
} catch (Throwable ex) {
this.logger.error("Encountered exception while handling in channel \"{}\"", channel, ex);
throw ex;
}
return true;
}
@Override
public Packet<?> createPacket(ResourceLocation channelName, FriendlyByteBuf buf) {
int queryId = this.queryIdFactory.nextId();
ClientboundCustomQueryPacket ret = new ClientboundCustomQueryPacket(queryId, channelName, buf);
return ret;
}
@Override
public void sendPacket(Packet<?> packet) {
Objects.requireNonNull(packet, "Packet cannot be null");
this.connection.send(packet);
}
@Override
public void sendPacket(Packet<?> packet, GenericFutureListener<? extends io.netty.util.concurrent.Future<? super Void>> callback) {
Objects.requireNonNull(packet, "Packet cannot be null");
this.connection.send(packet, callback);
}
public void registerOutgoingPacket(ClientboundCustomQueryPacket packet) {
this.channels.put(packet.getTransactionId(), packet.getIdentifier());
}
@Override
protected void handleRegistration(ResourceLocation channelName) {
}
@Override
protected void handleUnregistration(ResourceLocation channelName) {
}
@Override
protected void invokeDisconnectEvent() {
ServerLoginConnectionEvents.DISCONNECT.invoker().onLoginDisconnect(this.handler, this.server);
this.receiver.endSession(this);
}
public void handlePlayTransition() {
this.receiver.endSession(this);
}
@Override
protected boolean isReservedChannel(ResourceLocation channelName) {
return false;
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seibel.lod.forge.fabric.impl.networking.server;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerLoginNetworking;
import com.seibel.lod.forge.fabric.api.networking.v1.ServerPlayNetworking;
import com.seibel.lod.forge.fabric.impl.networking.GlobalReceiverRegistry;
import com.seibel.lod.forge.fabric.impl.networking.NetworkHandlerExtensions;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
public final class ServerNetworkingImpl {
public static final GlobalReceiverRegistry<ServerLoginNetworking.LoginQueryResponseHandler> LOGIN = new GlobalReceiverRegistry<>();
public static final GlobalReceiverRegistry<ServerPlayNetworking.PlayChannelHandler> PLAY = new GlobalReceiverRegistry<>();
public static ServerPlayNetworkAddon getAddon(ServerGamePacketListenerImpl handler) {
return (ServerPlayNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
}
public static ServerLoginNetworkAddon getAddon(ServerLoginPacketListenerImpl handler) {
return (ServerLoginNetworkAddon) ((NetworkHandlerExtensions) handler).getAddon();
}
public static Packet<?> createPlayC2SPacket(ResourceLocation channel, FriendlyByteBuf buf) {
return new ClientboundCustomPayloadPacket(channel, buf);
}
}

Some files were not shown because too many files have changed in this diff Show More