Compare commits

..

11 Commits

Author SHA1 Message Date
coolGi2007 1e52ee15b5 Merge remote-tracking branch 'origin/quilt_test' into quilt_test
# Conflicts:
#	fabric/build.gradle
2022-04-25 16:38:59 +09:30
coolGi2007 b54d478de1 Just a save commit for quilt 2022-04-25 16:36:00 +09:30
Ran 8008d61636 quilt... 2022-04-23 16:47:42 +06:00
coolGi2007 cf38aef742 Fixed quilt running in gradle and updated quilt version 2022-04-23 20:21:45 +10:00
coolGi2007 8621bf631b fixed a bug with a single comma not deleting 2022-04-23 19:29:40 +10:00
coolGi2007 c31c27167f Fixed the quilt authors and added a way to run the fabric module as quilt 2022-04-23 19:19:53 +10:00
Ran 35d97fa98f Please do it like this I want things to be simple 2022-04-23 11:08:28 +06:00
Ran 8627270e5c Basically the simplest way to add quilt support 2022-04-23 11:08:27 +06:00
coolGi2007 69dd7e932d Started adding support for multiple quilt versions (we need to remove quilted fabric api as it is only for 1.18.2) 2022-04-22 18:54:32 +10:00
coolGi2007 74371bd70d Fixed building on quilt (now just the runClient to be fixed) 2022-04-22 17:42:58 +10:00
coolGi2007 4a38b15ece Testing for adding proper quilt support 2022-04-22 11:12:10 +10:00
145 changed files with 6967 additions and 2465 deletions
+14 -156
View File
@@ -3,11 +3,6 @@ image: gradle:eclipse-temurin
# all stages need to be defined here
stages:
- build_19_4
- build_19_3
- build_19_2
- build_19_1
- build_19
- build_18_2
- build_18_1
- build_17_1
@@ -29,13 +24,10 @@ before_script:
build_16_5:
stage: build_16_5
script:
- echo "Building 1.16.5..."
- ./gradlew deleteMerged -PmcVer="1.16.5" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.16.5" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.16.5" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.16.5" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.16.5" --gradle-user-home cache/;
image: eclipse-temurin:17
- ./gradlew deleteMerged --gradle-user-home cache/;
- ./gradlew build -PmcVer=1.16.5 --gradle-user-home cache/;
- ./gradlew merge --gradle-user-home cache/;
image: eclipse-temurin:16
artifacts:
name: "Merged_NightlyBuild_1_16_5-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
@@ -54,13 +46,10 @@ build_16_5:
build_17_1:
stage: build_17_1
script:
- echo "Building 1.17.1..."
- ./gradlew deleteMerged -PmcVer="1.17.1" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.17.1" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.17.7" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.17.1" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.17.1" --gradle-user-home cache/;
image: eclipse-temurin:17
- ./gradlew deleteMerged --gradle-user-home cache/;
- ./gradlew build -PmcVer=1.17.1 --gradle-user-home cache/;
- ./gradlew merge --gradle-user-home cache/;
image: eclipse-temurin:16
artifacts:
name: "Merged_NightlyBuild_1_17_1-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
@@ -80,12 +69,9 @@ build_17_1:
build_18_1:
stage: build_18_1
script:
- echo "Building 1.18.1..."
- ./gradlew deleteMerged -PmcVer="1.18.1" --gradle-user-home cache/; # make sure any previously merged jars are removed before running this job
- ./gradlew clean -PmcVer="1.18.1" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.18.1" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.18.1" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.18.1" --gradle-user-home cache/;
- ./gradlew deleteMerged --gradle-user-home cache/; # make sure any previously merged jars are removed before running this job
- ./gradlew build -PmcVer=1.18.1 --gradle-user-home cache/;
- ./gradlew merge --gradle-user-home cache/;
# build using Java 17
image: eclipse-temurin:17
artifacts:
@@ -107,12 +93,9 @@ build_18_1:
build_18_2:
stage: build_18_2
script:
- echo "Building 1.18.2..."
- ./gradlew deleteMerged -PmcVer="1.18.2" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.18.2" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.18.2" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.18.2" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.18.2" --gradle-user-home cache/;
- ./gradlew deleteMerged --gradle-user-home cache/;
- ./gradlew build -PmcVer=1.18.2 --gradle-user-home cache/;
- ./gradlew merge --gradle-user-home cache/;
image: eclipse-temurin:17
artifacts:
name: "Merged_NightlyBuild_1_18_2-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
@@ -128,131 +111,6 @@ build_18_2:
- cache/
allow_failure: true
# 1.19 build
build_19:
stage: build_19
script:
- echo "Building 1.19..."
- ./gradlew deleteMerged -PmcVer="1.19" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.19" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.19" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.19" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.19" --gradle-user-home cache/;
image: eclipse-temurin:17
artifacts:
name: "Merged_NightlyBuild_1_19-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged
expire_in: 1 day
when: always
cache:
key: "gradleCache"
policy: pull-push
paths:
- .gradle
- cache/
allow_failure: true
# 1.19.1 build
build_19_1:
stage: build_19_1
script:
- echo "Building 1.19.1..."
- ./gradlew deleteMerged -PmcVer="1.19.1" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.19.1" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.19.1" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.19.1" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.19.1" --gradle-user-home cache/;
image: eclipse-temurin:17
artifacts:
name: "Merged_NightlyBuild_1_19_1-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged
expire_in: 1 day
when: always
cache:
key: "gradleCache"
policy: pull-push
paths:
- .gradle
- cache/
allow_failure: true
# 1.19.2 build
build_19_2:
stage: build_19_2
script:
- echo "Building 1.19.2..."
- ./gradlew deleteMerged -PmcVer="1.19.2" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.19.2" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.19.2" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.19.2" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.19.2" --gradle-user-home cache/;
image: eclipse-temurin:17
artifacts:
name: "Merged_NightlyBuild_1_19_2-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged
expire_in: 1 day
when: always
cache:
key: "gradleCache"
policy: pull-push
paths:
- .gradle
- cache/
allow_failure: true
# 1.19.3 build
build_19_3:
stage: build_19_3
script:
- echo "Building 1.19.3..."
- ./gradlew deleteMerged -PmcVer="1.19.3" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.19.3" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.19.3" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.19.3" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.19.3" --gradle-user-home cache/;
image: eclipse-temurin:17
artifacts:
name: "Merged_NightlyBuild_1_19_3-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged
expire_in: 1 day
when: always
cache:
key: "gradleCache"
policy: pull-push
paths:
- .gradle
- cache/
allow_failure: true
# 1.19.4 build
build_19_4:
stage: build_19_4
script:
- echo "Building 1.19.4..."
- ./gradlew deleteMerged -PmcVer="1.19.4" --gradle-user-home cache/;
- ./gradlew clean -PmcVer="1.19.4" --gradle-user-home cache/;
- ./gradlew core:build -PmcVer="1.19.4" --gradle-user-home cache/;
- ./gradlew build -PmcVer="1.19.4" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="1.19.4" --gradle-user-home cache/;
image: eclipse-temurin:17
artifacts:
name: "Merged_NightlyBuild_1_19_4-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged
expire_in: 1 day
when: always
cache:
key: "gradleCache"
policy: pull-push
paths:
- .gradle
- cache/
allow_failure: true
# unused deployment stage
#deploy:
+4 -1
View File
@@ -13,7 +13,7 @@ fabric_api_version=0.42.0+1.16
starlight_version_fabric=
phosphor_version_fabric=
lithium_version=mc1.16.5-0.6.6
sodium_version=mc1.16.5-0.2.0
sodium_version=3488820
iris_version=1.16.x-v1.1.4
bclib_version=
immersive_portals_version =
@@ -41,3 +41,6 @@ forge_version=36.2.28
# 2 = Can be refranced in code and runs in client
enable_starlight_forge=0
enable_terraforged=2
# Quilt loader
quilt_loader_version=0.16.0-beta.10
+4 -1
View File
@@ -13,7 +13,7 @@ fabric_api_version=0.46.1+1.17
starlight_version_fabric=3442770
phosphor_version_fabric=
lithium_version=mc1.17.1-0.7.5
sodium_version=mc1.17.1-0.3.4
sodium_version=3605275
iris_version=1.17.x-v1.1.4
bclib_version=
immersive_portals_version = 0.14-1.17
@@ -41,3 +41,6 @@ forge_version=37.1.1
# 2 = Can be refranced in code and runs in client
enable_starlight_forge=0
enable_terraforged=0
# Quilt loader
quilt_loader_version=0.16.0-beta.10
+5 -2
View File
@@ -1,6 +1,6 @@
# 1.18.1 version
java_version = 17
java_version=17
minecraft_version=1.18.1
parchment_version=2022.03.06
compatible_minecraft_versions=["1.18", "1.18.1"]
@@ -13,7 +13,7 @@ fabric_api_version=0.46.6+1.18
starlight_version_fabric=3554912
phosphor_version_fabric=3573395
lithium_version=mc1.18.1-0.7.7
sodium_version=mc1.18.1-0.4.0-alpha6
sodium_version=3605309
iris_version=1.18.x-v1.1.4
bclib_version=1.2.5
immersive_portals_version = v1.0.4-1.18
@@ -41,3 +41,6 @@ forge_version=39.1.2
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
enable_terraforged=0
# Quilt loader
quilt_loader_version=0.16.0-beta.10
+5 -2
View File
@@ -1,6 +1,6 @@
# 1.18.2 version based stuff
java_version = 17
java_version=17
minecraft_version=1.18.2
parchment_version=2022.03.13
compatible_minecraft_versions=["1.18.2"]
@@ -13,7 +13,7 @@ fabric_api_version=0.48.0+1.18.2
starlight_version_fabric=3667443
phosphor_version_fabric=3573395
lithium_version=mc1.18.2-0.7.9
sodium_version=mc1.18.2-0.4.1
sodium_version=3669187
iris_version=1.18.x-v1.2.2
immersive_portals_version = v1.0.4-1.18
bclib_version=0
@@ -41,3 +41,6 @@ forge_version=40.0.18
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
enable_terraforged=0
# Quilt loader
quilt_loader_version=0.16.0-beta.10
-43
View File
@@ -1,43 +0,0 @@
# 1.18.2 version based stuff
java_version = 17
minecraft_version=1.19.1
parchment_version=2022.03.13
compatible_minecraft_versions=["1.19", "1.19.1"]
# Fabric loader
fabric_loader_version=0.14.8
fabric_api_version=0.58.5+1.19.1
# Fabric mod versions
modmenu_version=4.0.0
starlight_version_fabric=0
phosphor_version_fabric=0
lithium_version=0
sodium_version=mc1.19.2-0.4.4
iris_version=1.19.x-v1.2.6
immersive_portals_version = 0
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_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=0
enable_bclib=0
# Forge loader
forge_version=42.0.0
# Forge mod versions
starlight_version_forge=0
terraforged_version=
# Forge 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_forge=0
enable_terraforged=0
-43
View File
@@ -1,43 +0,0 @@
# 1.18.2 version based stuff
java_version = 17
minecraft_version=1.19.2
parchment_version=2022.03.13
compatible_minecraft_versions=["1.19.2"]
# Fabric loader
fabric_loader_version=0.14.8
fabric_api_version=0.58.5+1.19.1
# Fabric mod versions
modmenu_version=4.0.0
starlight_version_fabric=0
phosphor_version_fabric=0
lithium_version=0
sodium_version=mc1.19.2-0.4.4
iris_version=1.19.x-v1.2.6
immersive_portals_version = 0
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_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=0
enable_bclib=0
# Forge loader
forge_version=43.0.0
# Forge mod versions
starlight_version_forge=0
terraforged_version=
# Forge 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_forge=0
enable_terraforged=0
-43
View File
@@ -1,43 +0,0 @@
# 1.18.2 version based stuff
java_version = 17
minecraft_version=1.19.3
parchment_version=2022.03.13
compatible_minecraft_versions=["1.19.3"]
# Fabric loader
fabric_loader_version=0.14.11
fabric_api_version=0.68.1+1.19.3
# Fabric mod versions
modmenu_version=5.0.2
starlight_version_fabric=0
phosphor_version_fabric=0
lithium_version=0
sodium_version=mc1.19.3-0.4.11
iris_version=1.19.x-v1.2.6
immersive_portals_version = 0
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_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=0
enable_bclib=0
# Forge loader
forge_version=44.0.6
# Forge mod versions
starlight_version_forge=0
terraforged_version=
# Forge 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_forge=0
enable_terraforged=0
-43
View File
@@ -1,43 +0,0 @@
# 1.18.2 version based stuff
java_version = 17
minecraft_version=1.19.4
parchment_version=2022.03.13
compatible_minecraft_versions=["1.19.4"]
# Fabric loader
fabric_loader_version=0.14.17
fabric_api_version=0.75.3+1.19.4
# Fabric mod versions
modmenu_version=6.1.0-rc.1
starlight_version_fabric=0
phosphor_version_fabric=0
lithium_version=0
sodium_version=mc1.19.4-0.4.11
iris_version=1.19.x-v1.2.6
immersive_portals_version = 0
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_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=0
enable_bclib=0
# Forge loader
forge_version=45.0.0
# Forge mod versions
starlight_version_forge=0
terraforged_version=
# Forge 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_forge=0
enable_terraforged=0
-43
View File
@@ -1,43 +0,0 @@
# 1.18.2 version based stuff
java_version = 17
minecraft_version=1.19
parchment_version=2022.03.13
compatible_minecraft_versions=["1.19"]
# Fabric loader
fabric_loader_version=0.14.8
fabric_api_version=0.57.0+1.19
# Fabric mod versions
modmenu_version=4.0.0
starlight_version_fabric=0
phosphor_version_fabric=0
lithium_version=0
sodium_version=mc1.19.2-0.4.4
iris_version=1.19.x-v1.2.6
immersive_portals_version = 0
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_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=0
enable_bclib=0
# Forge loader
forge_version=41.0.94
# Forge mod versions
starlight_version_forge=0
terraforged_version=
# Forge 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_forge=0
enable_terraforged=0
-165
View File
@@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+11 -56
View File
@@ -2,7 +2,6 @@
> A mod that adds a Level of Detail System to Minecraft
## Please move to main branch, this branch is mainly used for porting to newer versions of Minecraft until main becomes stable
# What is Distant Horizons?
@@ -13,48 +12,22 @@ allowing for an increased view distance without harming performance.
Or in other words: this mod lets you see farther without turning your game into a slide show.\
If you want to see a quick demo, check out a video covering the mod here:
<a href="https://youtu.be/_04BZ8W2bDM" target="_blank">![Minecraft Level Of Detail (LOD) mod - Alpha 1.6.3](https://cdn.ko-fi.com/cdn/useruploads/png_ef4d209d-50d9-462f-b31f-92e42ec3e260cover.jpg?v=c1097a5b-029c-4484-bec3-80ff58c5d239)</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>
## About this branch
This is a test branch to add support for the quilt mod loader (https://quiltmc.org/)
### Versions
This branch is for these versions of Minecraft
- 1.19.3
- 1.19.2
- 1.19.1 & 1.19
- 1.18.2
- 1.18.1 & 1.18
- 1.17.1 & 1.17
- 1.16.5 & 1.16.4
Architectury version: 3.4-SNAPSHOT\
Architectury loom version: 0.12.0-SNAPSHOT\
Java Compiler plugin: Manifold Preprocessor
#### 1.19.4 mods
Forge version: 45.0.0\
Fabric version: 0.14.17\
Fabric API version: 0.75.3+1.19.4\
Modmenu version: 6.1.0-rc.1
#### 1.19.3 mods
Forge version: 44.0.6\
Fabric version: 0.14.11\
Fabric API version: 0.68.1+1.19.3\
Modmenu version: 5.0.2
#### 1.19.2 mods
Forge version: 43.0.0\
Fabric version: 0.14.8\
Fabric API version: 0.58.5+1.19.1\
Modmenu version: 4.0.0
#### 1.19.1 mods
Forge version: 42.0.0\
Fabric version: 0.14.8\
Fabric API version: 0.58.5+1.19.1\
Modmenu version: 4.0.0
#### 1.18.2 mods
Forge version: 40.0.18\
Fabric version: 0.13.3\
@@ -89,15 +62,10 @@ This version has been confirmed to work in IDE and Retail Minecraft with ether t
#### Nightlly builds
This mod has an autobuild system to automatically build the mod on each commit
- 1.19.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_19_4
- 1.19.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_19_3
- 1.19.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_19_2
- 1.19.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_19_1
- 1.19: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_19
- 1.18.2: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_18_2
- 1.18.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_18_1
- 1.17.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_17_1
- 1.16.5: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/stable/download?job=build_16_5
- 1.18.2: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/main/download?job=build_18_2
- 1.18.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/main/download?job=build_18_1
- 1.17.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/main/download?job=build_17_1
- 1.16.5: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/main/download?job=build_16_5
See the Fabric Documentation online for more detailed instructions:\
https://fabricmc.net/wiki/tutorial:setup
@@ -120,16 +88,11 @@ https://fabricmc.net/wiki/tutorial:setup
4. Import the project into eclipse
## Switching Versions
This branch support 9 built versions:
- 1.19.4
- 1.19.3
- 1.19.2
- 1.19.1
- 1.19
This branch support 4 built versions:
- 1.18.2
- 1.18.1 (which also runs on 1.18)
- 1.17.1 (which also runs on 1.17)
- 1.16.5 (which also runs 1.16.4)
- 1.16.5 (which also runs 1.16.5)
To switch between active versions, change `mcVer=1.?` in `gradle.properties` file.
@@ -147,19 +110,11 @@ If running on IDE, to ensure IDE pickup the changed versions, you will need to r
6. The compiled jar file will be in the folder `Merged`
**If in terminal:**
1. `git clone -b stable --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
2. `cd minecraft-lod-mod`
3. `./gradlew assemble`
4. `./gradlew mergeJars`
5. The compiled jar file will be in the folder `Merged`
> ### **WARNING:** Due to a bug at the moment, the merged jar does not contain the core.
> To add the core follow the steps below
> 1. Go into `core/build/libs` and extract the jar with the smallest name
> 2. In that extracted folder, copy the folder called `com`
> 3. Go into the `Merged` folder and extract the jar in that
> 4. Paste in the folder which you copied from step 2 into the newly extracted jar
> 5. Zip all the files that were extracted and make sure the file extension is .jar
>Note: You can add the arg: `-PmcVer=1.?` to tell gradle to build a selected MC version instead of having to manually modify the `gradle.properties` file.
+254 -311
View File
@@ -1,5 +1,6 @@
import io.github.ran.jarmerger.JarMergerPlugin
buildscript {
dependencies{
classpath files('plugins/DHJarMerger-1.0.jar')
@@ -8,25 +9,7 @@ buildscript {
plugins {
id "architectury-plugin" version "3.4-SNAPSHOT"
id "dev.architectury.loom" version "0.12.0-SNAPSHOT" apply false
}
apply plugin: "java"
apply plugin: "architectury-plugin"
apply plugin: "maven-publish"
apply plugin: JarMergerPlugin
archivesBaseName = rootProject.archives_base_name
version = rootProject.mod_version
group = rootProject.maven_group
task printConfigurations {
doLast {task ->
println "Project Name: $name configurations:"
configurations.each {
println " $it.name"
}
}
id "dev.architectury.loom" version "0.11.0-SNAPSHOT" apply false
}
def writeBuildGradlePredefine(List<String> mcVers, int mcIndex) {
@@ -53,7 +36,7 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex) {
// Sets up the variables for Manifold in the code
def loadProperties() {
def defaultMcVersion = "1.19.2"
def defaultMcVersion = "1.18.2"
def mcVersion = ""
def mcVers = mcVersions.split(",")
int mcIndex = -1
@@ -86,330 +69,290 @@ def loadProperties() {
"1.17.1": "1_17",
"1.18.1": "1_18",
"1.18.2": "1_18",
"1.19" : "1_19",
"1.19.1" : "1_19",
"1.19.2" : "1_19",
"1.19.3" : "1_19_3",
"1.19.4" : "1_19_3"
]
// Use this as sometimes multiple versions use the same access wideners
rootProject.ext.set("acsessWidenerVersion", mcVersionToAcsessWidenerVersion.get(mcVersion))
}
loadProperties()
apply plugin: JarMergerPlugin
architectury {
minecraft = rootProject.minecraft_version
}
repositories {
mavenCentral()
// For parchment mappings
maven { url "https://maven.parchmentmc.org" }
subprojects { p ->
apply plugin: "dev.architectury.loom"
// used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' }
loom {
silentMojangMappingsLicense()
// For Manifold Preprocessor
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Required for importing Modrinth mods
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
if (p != project(":core")) {
accessWidenerPath = project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener")
}
}
// Required for importing CursedForge mods
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
configurations {
common
shadowMe
implementation.extendsFrom shadowMe
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
// The following line declares the mojmap mappings & parchment mappings
mappings loom.layered() {
// Mojmap mappings
officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc)
parchment("org.parchmentmc.data:parchment-${rootProject.minecraft_version}:${rootProject.parchment_version}@zip")
}
//Manifold
annotationProcessor "systems.manifold:manifold-preprocessor:${rootProject.manifold_version}"
// Toml
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
if (p != project(":forge") && p != project(":fabric")) {
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader unless working with fabric
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
}
if (p == project(":fabric")) {
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader unless working with fabric
if (rootProject.use_quilt_rather_than_fabric == "true") {
// Quilt loader
modImplementation "org.quiltmc:quilt-loader:${rootProject.quilt_loader_version}"
modImplementation "org.quiltmc:quilt-json5:1.0.0" // Needed for quilt loader but for some reason it dosnt come with it
} else {
// Fabric loader
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
}
}
if (p != project(":core")) {
common(project(":core")) { transitive false }
shadowMe(project(":core")) { transitive false }
}
}
// These 2 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
content {
includeGroup "fabric-mod"
}
}
flatDir {
dirs "${rootDir}/mods/forge"
content {
includeGroup "forge-mod"
// Allows the jar to run standalone
jar {
manifest {
attributes 'Implementation-Title': rootProject.archives_base_name,
'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.lod.core.JarMain' // When changing the main of the jar change this line
}
}
}
processResources {
def resourceTargets = ["fabric.mod.json", "META-INF/mods.toml"] // Location of where to put
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
def replaceProperties = [
version : mod_version,
mod_name : mod_name,
authors : mod_authors,
description : mod_description,
homepage : mod_homepage,
source : mod_source,
issues : mod_issues,
minecraft_version : minecraft_version,
compatible_minecraft_versions: compatible_minecraft_versions,
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
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,42)"
allprojects { p ->
apply plugin: "java"
apply plugin: "architectury-plugin"
apply plugin: "maven-publish"
inputs.properties replaceProperties
replaceProperties.put 'project', project
filesMatching(resourceTargets) {
expand replaceProperties
}
archivesBaseName = rootProject.archives_base_name
version = rootProject.mod_version
group = rootProject.maven_group
intoTargets.each { target ->
if (file(target).exists()) {
copy {
from(sourceSets.main.resources) {
include resourceTargets
expand replaceProperties
}
into target
repositories {
mavenCentral()
// For parchment mappings
maven { url "https://maven.parchmentmc.org" }
// used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' }
// For Manifold Preprocessor
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Required for ModMenu
maven { url "https://maven.terraformersmc.com/" }
// Required for importing Modrinth mods
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
// Required for importing CursedForge mods
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
}
}
// Required for Quilt
maven {
name 'Quilt'
url 'https://maven.quiltmc.org/repository/release'
}
// These 2 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
content {
includeGroup "fabric-mod"
}
}
flatDir {
dirs "${rootDir}/mods/forge"
content {
includeGroup "forge-mod"
}
}
flatDir {
dirs "${rootDir}/mods/quilt"
content {
includeGroup "quilt-mod"
}
}
}
// Make a new author thing for quilt as quilt uses a different layout for their authors
// The output to look something like this {"James":"Developer","coolGi":"Developer}
def currentAuthor = ""
def quiltAuthors = "{"
def authorCounter = false;
for (reader in rootProject.mod_authors) {
if (authorCounter)
currentAuthor = currentAuthor + reader
if (reader == "\"") {
authorCounter = !authorCounter
if (!authorCounter) {
currentAuthor = currentAuthor.substring(0, currentAuthor.length() - 1); // Delete the qotation mark at the end of the authors name
quiltAuthors = quiltAuthors + "\"" + currentAuthor + "\":\"Developer\"," // Add the author to the string
currentAuthor = ""
}
}
}
quiltAuthors = quiltAuthors.substring(0, quiltAuthors.length() - 1) // Delete the final comma
quiltAuthors = quiltAuthors + "}"
// Put stuff from gradle.properties into the mod info
processResources {
def resourceTargets = ["fabric.mod.json", "quilt.mod.json", "META-INF/mods.toml"] // Location of where to put
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
def replaceProperties = [
version : mod_version,
mod_name : mod_name,
authors : mod_authors,
quilt_authors : quiltAuthors,
description : mod_description,
homepage : mod_homepage,
source : mod_source,
issues : mod_issues,
minecraft_version : minecraft_version,
compatible_minecraft_versions: compatible_minecraft_versions,
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
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,41)"
inputs.properties replaceProperties
replaceProperties.put 'project', project
filesMatching(resourceTargets) {
expand replaceProperties
}
intoTargets.each { target ->
if (file(target).exists()) {
copy {
from(sourceSets.main.resources) {
include resourceTargets
expand replaceProperties
}
into target
}
}
}
}
// Copies the correct accesswidener and renames it
task copyAccessWidener(type: Copy) {
from project(":common").file("src/main/resources/${rootProject.acsessWidenerVersion}.lod.accesswidener")
into(file(p.file("build/resources/main")))
rename "${rootProject.acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into p.file("build/resources/main")
}
task copyCommonResources(type: Copy) {
from fileTree(project(":common").file("src/main/resources"))
into p.file("build/resources/main")
}
// Is this necessary for running the fabric build
if (p == project(":common")) {
println "Copying [common/src/main/resources/${acsessWidenerVersion}.lod.accesswidner] to [fabric/build/resources/main]."
copy {
from project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener")
into project(":fabric").file("build/resources/main")
rename "${acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
}
tasks.withType(JavaCompile) {
// Add Manifold Preprocessor
// def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
// options.compilerArgs += ['-Xplugin:Manifold', "-AMC_VERSION_${excapedMCVersion}"]
//
//options.compilerArgs += ['-deprecation']
//options.compilerArgs += ['-verbose']
//options.compilerArgs += ['-Xlint:unchecked']
//options.compilerArgs += ['-Xdiags:verbose']
//options.compilerArgs += ['-Xprint']
//options.compilerArgs += ['-XprintProcessorInfo']
//options.compilerArgs += ['-XprintRounds']
// println options.compilerArgs
options.encoding = "UTF-8"
// Set the java version
if (p != project(":core")) {
options.compilerArgs += ['-Xplugin:Manifold']
options.release = rootProject.java_version as Integer
} else if (p == project(":core")) {
options.release = 8; // Core should use Java 8 no matter what
}
// TODO: make everything use java 8
// options.release = 8 // Use Java 8 for everything so back porting is easier
}
java {
withSourcesJar()
}
// Disable running the core and common
if (p == project(":core") || p == project(":common")) {
runClient.enabled = false
runServer.enabled = false
}
}
// Copies the correct accesswidener and renames it
task copyAccessWidener(type: Copy) {
from project(":common").file("src/main/resources/${rootProject.acsessWidenerVersion}.lod.accesswidener")
into(file("build/resources/main"))
rename "${rootProject.acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into file("build/resources/main")
}
task copyCommonResources(type: Copy) {
from fileTree(project(":common").file("src/main/resources"))
into file("build/resources/main")
}
java {
withSourcesJar()
}
//runClient.enabled = false
//runServer.enabled = false
// this deletes the merged folder so we don't carry over
// the previous merges to each new build job in the CI/CD pipeline
task deleteMerged(type: Delete) {
delete files("./Merged")
}
// ===============================CORE Gradle basically================================
subprojects { p ->
if (p == project(":core")) {
apply plugin: "java"
apply plugin: "architectury-plugin"
apply plugin: "maven-publish"
apply plugin: "dev.architectury.loom"
p.archivesBaseName = rootProject.archives_base_name
p.version = rootProject.mod_version
p.group = rootProject.maven_group
loom {
silentMojangMappingsLicense()
}
configurations {
common
shadowMe
implementation.extendsFrom shadowMe
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
// The following line declares the mojmap mappings & parchment mappings
mappings loom.layered() {
// Mojmap mappings
officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc)
if (
rootProject.minecraft_version != "1.19"
&& rootProject.minecraft_version != "1.19.1"
&& rootProject.minecraft_version != "1.19.2"
&& rootProject.minecraft_version != "1.19.3"
&& rootProject.minecraft_version != "1.19.4"
) // We are not gonna use this build script anymore so dont bother fixing this
parchment("org.parchmentmc.data:parchment-${rootProject.minecraft_version}:${rootProject.parchment_version}@zip")
else
parchment("org.parchmentmc.data:parchment-1.18.2:${rootProject.parchment_version}@zip") // As 1.19.x or higher doesnt have parchment mappings yet, we use 1.18.2 mapping
// Note: parchment may have later mappings at the time you are reading this, but at the time this was written, it didnt
// Check the main branch for the new build system, with the working mappings
}
//Manifold
annotationProcessor "systems.manifold:manifold-preprocessor:${rootProject.manifold_version}"
// Toml
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader unless working with fabric
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
}
// Allows the jar to run standalone
jar {
manifest {
attributes 'Implementation-Title': rootProject.archives_base_name,
'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.lod.core.JarMain'
// When changing the main of the jar change this line
}
}
task printConfigurations {
doLast {task ->
println "Project Name: $p.name configurations:"
configurations.each {
println " $it.name"
}
}
}
repositories {
mavenCentral()
// For parchment mappings
maven { url "https://maven.parchmentmc.org" }
// used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' }
// For Manifold Preprocessor
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Required for importing Modrinth mods
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
// Required for importing CursedForge mods
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
}
}
// These 2 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
content {
includeGroup "fabric-mod"
}
}
flatDir {
dirs "${rootDir}/mods/forge"
content {
includeGroup "forge-mod"
}
}
}
processResources {
def resourceTargets = ["fabric.mod.json", "META-INF/mods.toml"] // Location of where to put
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
def replaceProperties = [
version : mod_version,
mod_name : mod_name,
authors : mod_authors,
description : mod_description,
homepage : mod_homepage,
source : mod_source,
issues : mod_issues,
minecraft_version : minecraft_version,
compatible_minecraft_versions: compatible_minecraft_versions,
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
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,42)"
inputs.properties replaceProperties
replaceProperties.put 'project', project
filesMatching(resourceTargets) {
expand replaceProperties
}
intoTargets.each { target ->
if (file(target).exists()) {
copy {
from(sourceSets.main.resources) {
include resourceTargets
expand replaceProperties
}
into target
}
}
}
}
// Copies the correct accesswidener and renames it
task copyAccessWidener(type: Copy) {
from project(":common").file("src/main/resources/${rootProject.acsessWidenerVersion}.lod.accesswidener")
into(file(p.file("build/resources/main")))
rename "${rootProject.acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into p.file("build/resources/main")
}
task copyCommonResources(type: Copy) {
from fileTree(project(":common").file("src/main/resources"))
into p.file("build/resources/main")
}
p.tasks.withType(JavaCompile) {
// Add Manifold Preprocessor
// def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
// options.compilerArgs += ['-Xplugin:Manifold', "-AMC_VERSION_${excapedMCVersion}"]
//
//options.compilerArgs += ['-deprecation']
//options.compilerArgs += ['-verbose']
//options.compilerArgs += ['-Xlint:unchecked']
//options.compilerArgs += ['-Xdiags:verbose']
//options.compilerArgs += ['-Xprint']
//options.compilerArgs += ['-XprintProcessorInfo']
//options.compilerArgs += ['-XprintRounds']
// println options.compilerArgs
// Set the java version
options.release = 8; // Core should use Java 8 no matter what
// TODO: make everything use java 8
// options.release = 8 // Use Java 8 for everything so back porting is easier
}
java {
withSourcesJar()
}
p.runClient.enabled = false
p.runServer.enabled = false
}
}
+1 -210
View File
@@ -1,75 +1,7 @@
apply plugin: "java"
apply plugin: "architectury-plugin"
apply plugin: "maven-publish"
apply plugin: "dev.architectury.loom"
archivesBaseName = rootProject.archives_base_name
version = rootProject.mod_version
group = rootProject.maven_group
architectury {
common(rootProject.enabled_platforms.split(","))
}
loom {
silentMojangMappingsLicense()
accessWidenerPath.set(project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener"))
}
configurations {
common
shadowMe
implementation.extendsFrom shadowMe
}
java {
withSourcesJar()
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
// The following line declares the mojmap mappings & parchment mappings
mappings loom.layered() {
// Mojmap mappings
officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc)
if (
rootProject.minecraft_version != "1.19"
&& rootProject.minecraft_version != "1.19.1"
&& rootProject.minecraft_version != "1.19.2"
&& rootProject.minecraft_version != "1.19.3"
&& rootProject.minecraft_version != "1.19.4"
) // We are not gonna use this build script anymore so dont bother fixing this
parchment("org.parchmentmc.data:parchment-${rootProject.minecraft_version}:${rootProject.parchment_version}@zip")
else
parchment("org.parchmentmc.data:parchment-1.18.2:${rootProject.parchment_version}@zip") // As 1.19.x or higher doesnt have parchment mappings yet, we use 1.18.2 mapping
// Note: parchment may have later mappings at the time you are reading this, but at the time this was written, it didnt
// Check the main branch for the new build system, with the working mappings
}
//Manifold
annotationProcessor "systems.manifold:manifold-preprocessor:${rootProject.manifold_version}"
// Toml
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader unless working with fabric
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"\
common(project(":core")) { transitive false }
shadowMe(project(":core")) { transitive false }
}
// Allows the jar to run standalone
jar {
manifest {
attributes 'Implementation-Title': rootProject.archives_base_name,
'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.lod.core.JarMain' // When changing the main of the jar change this line
}
}
publishing {
publications {
mavenCommon(MavenPublication) {
@@ -82,145 +14,4 @@ publishing {
repositories {
// Add repositories to publish to here.
}
}
task printConfigurations {
doLast {task ->
println "Project Name: $name configurations:"
configurations.each {
println " $it.name"
}
}
}
repositories {
mavenCentral()
// For parchment mappings
maven { url "https://maven.parchmentmc.org" }
// used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' }
// For Manifold Preprocessor
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Required for importing Modrinth mods
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
// Required for importing CursedForge mods
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
}
}
// These 2 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
content {
includeGroup "fabric-mod"
}
}
flatDir {
dirs "${rootDir}/mods/forge"
content {
includeGroup "forge-mod"
}
}
}
// Copies the correct accesswidener and renames it
task copyAccessWidener(type: Copy) {
from project(":common").file("src/main/resources/${rootProject.acsessWidenerVersion}.lod.accesswidener")
into(file("build/resources/main"))
rename "${rootProject.acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into file("build/resources/main")
}
task copyCommonResources(type: Copy) {
from fileTree(project(":common").file("src/main/resources"))
into file("build/resources/main")
}
processResources {
def resourceTargets = ["fabric.mod.json", "META-INF/mods.toml"] // Location of where to put
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
def replaceProperties = [
version : mod_version,
mod_name : mod_name,
authors : mod_authors,
description : mod_description,
homepage : mod_homepage,
source : mod_source,
issues : mod_issues,
minecraft_version : minecraft_version,
compatible_minecraft_versions: compatible_minecraft_versions,
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
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,42)"
inputs.properties replaceProperties
replaceProperties.put 'project', project
filesMatching(resourceTargets) {
expand replaceProperties
}
intoTargets.each { target ->
if (file(target).exists()) {
copy {
from(sourceSets.main.resources) {
include resourceTargets
expand replaceProperties
}
into target
}
}
}
}
println "Copying [common/src/main/resources/${acsessWidenerVersion}.lod.accesswidner] to [fabric/build/resources/main]."
copy {
from project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener")
into project(":fabric").file("build/resources/main")
rename "${acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
tasks.withType(JavaCompile) {
// Add Manifold Preprocessor
// def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
// options.compilerArgs += ['-Xplugin:Manifold', "-AMC_VERSION_${excapedMCVersion}"]
//
//options.compilerArgs += ['-deprecation']
//options.compilerArgs += ['-verbose']
//options.compilerArgs += ['-Xlint:unchecked']
//options.compilerArgs += ['-Xdiags:verbose']
//options.compilerArgs += ['-Xprint']
//options.compilerArgs += ['-XprintProcessorInfo']
//options.compilerArgs += ['-XprintRounds']
// println options.compilerArgs
// Set the java version
options.compilerArgs += ['-Xplugin:Manifold']
options.release = rootProject.java_version as Integer
// TODO: make everything use java 8
// options.release = 8 // Use Java 8 for everything so back porting is easier
}
sourcesJar {
}
runClient.enabled = false
runServer.enabled = false
}
@@ -35,7 +35,7 @@ import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.I
/**
* This handles any configuration the user has access to.
* @author coolGi
* @author coolGi2007
* @version 12-12-2021
*/
@@ -106,7 +106,7 @@ public class Config
@ConfigAnnotations.FileComment
public static String _lodChunkRenderDistance = IQuality.LOD_CHUNK_RENDER_DISTANCE_DESC;
@ConfigAnnotations.Entry(minValue = 32, maxValue = 2048)
@ConfigAnnotations.Entry(minValue = 16, maxValue = 2048)
public static int lodChunkRenderDistance = IQuality.LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
@@ -116,7 +116,7 @@ public class Config
@ConfigAnnotations.FileComment
public static String _horizontalScale = IQuality.HORIZONTAL_SCALE_DESC;
@ConfigAnnotations.Entry(minValue = 2, maxValue = 64)
@ConfigAnnotations.Entry(minValue = 2, maxValue = 32)
public static int horizontalScale = IQuality.HORIZONTAL_SCALE_MIN_DEFAULT_MAX.defaultValue;
@ConfigAnnotations.FileComment
@@ -326,6 +326,14 @@ public class Config
public static String _generationPriority = IWorldGenerator.GENERATION_PRIORITY_DESC;
@ConfigAnnotations.Entry
public static GenerationPriority generationPriority = IWorldGenerator.GENERATION_PRIORITY_DEFAULT;
/*
@ConfigAnnotations.FileComment
public static String _allowUnstableFeatureGeneration = IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DESC;
// FIXME: Temperary override. In 1.18, the newer Unstable gnerator is more usable
@ConfigAnnotations.Entry
public static boolean allowUnstableFeatureGeneration = true;//IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT;
*/
@ConfigAnnotations.FileComment
public static String _blocksToAvoid = IWorldGenerator.BLOCKS_TO_AVOID_DESC;
@@ -32,8 +32,9 @@ public class LodCommonMain {
public static boolean forge = false;
public static boolean serverSided;
public static LodForgeMethodCaller forgeMethodCaller;
public static NetworkInterface networkInterface;
public static void startup(LodForgeMethodCaller caller, boolean serverSided) {
public static void startup(LodForgeMethodCaller caller, boolean serverSided, NetworkInterface networkInterface) {
LodCommonMain.serverSided = serverSided;
if (caller != null) {
LodCommonMain.forge = true;
@@ -41,6 +42,13 @@ public class LodCommonMain {
}
DependencySetup.createInitialBindings();
LodCommonMain.networkInterface = networkInterface;
if (!serverSided) {
networkInterface.register_Client();
} else {
networkInterface.register_Server();
}
}
@@ -22,9 +22,6 @@ package com.seibel.lod.common.forge;
import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.core.Direction;
#if POST_MC_1_19
import net.minecraft.util.RandomSource;
#endif
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
@@ -39,11 +36,7 @@ import java.util.Random;
* @author Ran
*/
public interface LodForgeMethodCaller {
#if PRE_MC_1_19
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random); // FIXME: For 1.19
#else
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random); // FIXME: For 1.19
#endif
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random);
int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z);
}
@@ -21,11 +21,7 @@ package com.seibel.lod.common.wrappers;
import java.nio.FloatBuffer;
#if PRE_MC_1_19_3
import com.mojang.math.Matrix4f;
#else
import org.joml.Matrix4f;
#endif
import com.seibel.lod.common.wrappers.block.BlockPosWrapper;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.objects.math.Mat4f;
@@ -43,45 +39,13 @@ import net.minecraft.core.Direction;
*/
public class McObjectConverter
{
private static int bufferIndex(int x, int y) {
return y * 4 + x;
}
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
public static void storeMatrix(Matrix4f matrix, FloatBuffer buffer) {
#if PRE_MC_1_19_3
matrix.store(buffer);
#else
// Mojang starts to use joml's Matrix4f libary in 1.19.3 so we copy their store method and use it here if its newer than 1.19.3
buffer.put(bufferIndex(0, 0), matrix.m00());
buffer.put(bufferIndex(0, 1), matrix.m01());
buffer.put(bufferIndex(0, 2), matrix.m02());
buffer.put(bufferIndex(0, 3), matrix.m03());
buffer.put(bufferIndex(1, 0), matrix.m10());
buffer.put(bufferIndex(1, 1), matrix.m11());
buffer.put(bufferIndex(1, 2), matrix.m12());
buffer.put(bufferIndex(1, 3), matrix.m13());
buffer.put(bufferIndex(2, 0), matrix.m20());
buffer.put(bufferIndex(2, 1), matrix.m21());
buffer.put(bufferIndex(2, 2), matrix.m22());
buffer.put(bufferIndex(2, 3), matrix.m23());
buffer.put(bufferIndex(3, 0), matrix.m30());
buffer.put(bufferIndex(3, 1), matrix.m31());
buffer.put(bufferIndex(3, 2), matrix.m32());
buffer.put(bufferIndex(3, 3), matrix.m33());
#endif
}
/** 4x4 float matrix converter */
public static Mat4f Convert(Matrix4f mcMatrix)
{
FloatBuffer buffer = FloatBuffer.allocate(16);
storeMatrix(mcMatrix, buffer);
mcMatrix.store(buffer);
Mat4f matrix = new Mat4f(buffer);
#if PRE_MC_1_19_3
matrix.transpose(); // In 1.19.3 and later, we no longer need to transpose it
#endif
matrix.transpose();
return matrix;
}
@@ -93,10 +57,7 @@ public class McObjectConverter
directions = new Direction[lodDirs.length];
lodDirections = new LodDirection[lodDirs.length];
for (LodDirection lodDir : lodDirs) {
Direction dir = Enum.valueOf(Direction.class, lodDir.name());
if (dir == null) {
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
}
Direction dir = Direction.byName(lodDir.name());
directions[lodDir.ordinal()] = dir;
lodDirections[dir.ordinal()] = lodDir;
}
@@ -68,9 +68,6 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
#if POST_MC_1_19
import net.minecraft.util.RandomSource;
#endif
/*-- WARN: This class should NEVER hold reference to anything large,
as this is never dealloc until the end of runtime!! --*/
@@ -80,11 +77,7 @@ public class BlockDetailWrapper extends IBlockDetailWrapper
public static final int FLOWER_COLOR_SCALE = 5;
#if PRE_MC_1_19
public static final Random random = new Random(0);
#else
public static final RandomSource random = RandomSource.create();
#endif
enum ColorMode {
Default,
@@ -108,21 +101,9 @@ public class BlockDetailWrapper extends IBlockDetailWrapper
{
// textures normally use u and v instead of x and y
for (int u = 0; u <
#if PRE_MC_1_19_3
texture.getWidth();
#else
texture.contents().width();
#endif
u++)
for (int u = 0; u < texture.getWidth(); u++)
{
for (int v = 0; v <
#if PRE_MC_1_19_3
texture.getHeight();
#else
texture.contents().height();
#endif
v++)
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
@@ -41,18 +41,12 @@ public class TextureAtlasSpriteWrapper {
return sprite.mainImage[0].getPixelRGBA(
x + sprite.framesX[frameIndex] * sprite.getWidth(),
y + sprite.framesY[frameIndex] * sprite.getHeight());
#elif PRE_MC_1_19_3
#else
if (sprite.animatedTexture != null) {
x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width;
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
}
return sprite.mainImage[0].getPixelRGBA(x, y);
#else
if (sprite.contents().animatedTexture != null) {
x += sprite.contents().animatedTexture.getFrameX(frameIndex) * sprite.contents().width();
y += sprite.contents().animatedTexture.getFrameY(frameIndex) * sprite.contents().width();
}
return sprite.contents().originalImage.getPixelRGBA(x, y);
#endif
}
}
@@ -47,10 +47,8 @@ import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
#if PRE_MC_1_19
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
#endif
import net.minecraft.client.resources.language.I18n; // translation
#if POST_MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry;
@@ -64,15 +62,29 @@ import net.minecraft.client.gui.narration.NarratableEntry;
*
* Credits to Motschen
*
* REMOVED IN a1.7
*
* @author coolGi
* @author coolGi2007
* @version 1-14-2022
*/
@Deprecated
@SuppressWarnings("unchecked")
public abstract class ConfigGui
{
/*
TODO list
Fix floats not working
Make a wiki
Make it so you can enable and disable buttons from showing
Make min and max not final
Move the ConfigScreenConfigs class to the config class that extends this
*/
/*
List of hacky things that are done that should be done properly
The buttons that don't show are still loaded but just not rendered
The screen with is set to double so the scroll bar doesn't show
*/
private static final Pattern INTEGER_ONLY_REGEX = Pattern.compile("(-?[0-9]*)");
private static final Pattern DECIMAL_ONLY_REGEX = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
@@ -111,11 +123,7 @@ public abstract class ConfigGui
Object value;
String tempValue;
boolean inLimits = true;
#if PRE_MC_1_19
TranslatableComponent name;
#else
Component name;
#endif
int index;
/** Hides the button */
boolean hideOption = false;
@@ -212,11 +220,8 @@ public abstract class ConfigGui
if (entry != null)
{
if (!entry.name().equals(""))
#if PRE_MC_1_19
info.name = new TranslatableComponent(entry.name());
#else
info.name = Component.translatable(entry.name());
#endif
if (fieldClass == int.class)
{
@@ -237,11 +242,7 @@ public abstract class ConfigGui
else if (fieldClass == boolean.class)
{
// For boolean
#if PRE_MC_1_19
Function<Object, Component> func = value -> new TextComponent((Boolean) value ? "True" : "False").withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
#else
Function<Object, Component> func = value -> Component.translatable((Boolean) value ? "True" : "False").withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
#endif
info.widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
info.value = !(Boolean) info.value;
button.setMessage(func.apply(info.value));
@@ -251,11 +252,7 @@ public abstract class ConfigGui
{
// For enum
List<?> values = Arrays.asList(field.getType().getEnumConstants());
#if PRE_MC_1_19
Function<Object, Component> func = value -> new TranslatableComponent(MOD_NAME + ".config." + "enum." + fieldClass.getSimpleName() + "." + info.value.toString());
#else
Function<Object, Component> func = value -> Component.translatable(MOD_NAME + ".config." + "enum." + fieldClass.getSimpleName() + "." + info.value.toString());
#endif
info.widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
int index = values.indexOf(info.value) + 1;
info.value = values.get(index >= values.size() ? 0 : index);
@@ -266,12 +263,7 @@ public abstract class ConfigGui
else if (screenEntry != null)
{
if (!screenEntry.name().equals(""))
#if PRE_MC_1_19
info.name = new TranslatableComponent(screenEntry.name());
#else
info.name = Component.translatable(screenEntry.name());
#endif
info.screenButton = true;
info.gotoScreen = (!info.category.isEmpty() ? info.category + "." : "") + field.getName();
@@ -299,11 +291,7 @@ public abstract class ConfigGui
{
value = func.apply(stringValue);
inLimits = value.doubleValue() >= minValue && value.doubleValue() <= maxValue;
#if PRE_MC_1_19
info.error = inLimits ? null : new AbstractMap.SimpleEntry<>(editBox, new TextComponent(value.doubleValue() < minValue ?
#else
info.error = inLimits ? null : new AbstractMap.SimpleEntry<>(editBox, Component.translatable(value.doubleValue() < minValue ?
#endif
"§cMinimum " + "length" + (cast ? " is " + (int) minValue : " is " + minValue) :
"§cMaximum " + "length" + (cast ? " is " + (int) maxValue : " is " + maxValue)));
}
@@ -343,8 +331,6 @@ public abstract class ConfigGui
// First try to create a config file
try {
if (!configFilePath.getParent().toFile().exists())
Files.createDirectory(configFilePath.getParent());
if (!Files.exists(configFilePath))
Files.createFile(configFilePath);
}
@@ -357,7 +343,8 @@ public abstract class ConfigGui
// Just put this here for the future
config.set("_version", 1);
config.setComment("_Version", " DONT TOUCH THIS, IF YOU DO THEN CONFIG FILE WOULD BREAK");
config.set("_Versions", ModInfo.VERSION);
for (EntryInfo info : entries) {
@@ -392,7 +379,8 @@ public abstract class ConfigGui
// Just put this here for the future
config.set("_version", 1);
config.setComment("_Version", " DONT TOUCH THIS, IF YOU DO THEN CONFIG FILE WOULD BREAK");
config.set("_Versions", ModInfo.VERSION);
// Puts everything into its variable
@@ -463,13 +451,8 @@ public abstract class ConfigGui
{
String itemPath = (info.category.isEmpty() ? "" : info.category + ".") + info.field.getName();
if (config.contains(itemPath)) {
if (info.field.getType().isEnum()) {
try {
info.value = config.getEnum(itemPath, info.varClass);
} catch (IllegalArgumentException ignored) {
return;
}
}
if (info.field.getType().isEnum())
info.value = config.getEnum(itemPath, info.varClass);
else
info.value = config.get(itemPath);
} else
@@ -515,11 +498,7 @@ public abstract class ConfigGui
{
protected ConfigScreen(Screen parent, String category)
{
#if PRE_MC_1_19
super(new TranslatableComponent(
#else
super(Component.translatable(
#endif
I18n.exists(MOD_NAME + ".config" + (category.isEmpty()? "." + category : "") + ".title") ?
MOD_NAME + ".config.title" :
MOD_NAME + ".config" + (category.isEmpty() ? "" : "." + category) + ".title")
@@ -569,11 +548,11 @@ public abstract class ConfigGui
if (!reload)
loadFromFile();
addBtn(createButton(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> {
addBtn(new Button(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> {
loadFromFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
Button done = addBtn(createButton(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> {
Button done = addBtn(new Button(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> {
saveToFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
@@ -586,13 +565,8 @@ public abstract class ConfigGui
{
if (info.category.matches(category) && !info.hideOption)
{
#if PRE_MC_1_19
TranslatableComponent name = (info.name == null ? new TranslatableComponent(translationPrefix + (!info.category.isEmpty() ? info.category + "." : "") + info.field.getName()) : info.name);
Button resetButton = new Button(this.width - ConfigScreenConfigs.SpaceFromRightScreen - info.width - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth, 0, ConfigScreenConfigs.ResetButtonWidth, 20, new TextComponent("Reset").withStyle(ChatFormatting.RED), (button -> {
#else
Component name = (info.name == null ? Component.translatable(translationPrefix + (!info.category.isEmpty() ? info.category + "." : "") + info.field.getName()) : info.name);
Button resetButton = createButton(this.width - ConfigScreenConfigs.SpaceFromRightScreen - info.width - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth, 0, ConfigScreenConfigs.ResetButtonWidth, 20, Component.translatable("Reset").withStyle(ChatFormatting.RED), (button -> {
#endif
info.value = info.defaultValue;
info.tempValue = info.defaultValue.toString();
info.index = 0;
@@ -604,12 +578,8 @@ public abstract class ConfigGui
{
Map.Entry<Button.OnPress, Function<Object, Component>> widget = (Map.Entry<Button.OnPress, Function<Object, Component>>) info.widget;
if (info.field.getType().isEnum())
#if PRE_MC_1_19
widget.setValue(value -> new TranslatableComponent(translationPrefix + "enum." + info.field.getType().getSimpleName() + "." + info.value.toString()));
#else
widget.setValue(value -> Component.translatable(translationPrefix + "enum." + info.field.getType().getSimpleName() + "." + info.value.toString()));
#endif
this.list.addButton(createButton(this.width - info.width - ConfigScreenConfigs.SpaceFromRightScreen, 0, info.width, 20, widget.getValue().apply(info.value), widget.getKey()), resetButton, null, name);
this.list.addButton(new Button(this.width - info.width - ConfigScreenConfigs.SpaceFromRightScreen, 0, info.width, 20, widget.getValue().apply(info.value), widget.getKey()), resetButton, null, name);
}
else if (info.field.getType() == List.class)
{
@@ -624,13 +594,8 @@ public abstract class ConfigGui
Predicate<String> processor = ((BiFunction<EditBox, Button, Predicate<String>>) info.widget).apply(widget, done);
widget.setFilter(processor);
resetButton.setWidth(20);
#if PRE_MC_1_19
resetButton.setMessage(new TextComponent("R").withStyle(ChatFormatting.RED));
Button cycleButton = new Button(this.width - 185, 0, 20, 20, new TextComponent(String.valueOf(info.index)).withStyle(ChatFormatting.GOLD), (button -> {
#else
resetButton.setMessage(Component.translatable("R").withStyle(ChatFormatting.RED));
Button cycleButton = createButton(this.width - 185, 0, 20, 20, Component.translatable(String.valueOf(info.index)).withStyle(ChatFormatting.GOLD), (button -> {
#endif
resetButton.setMessage(new TextComponent("R").withStyle(ChatFormatting.RED));
Button cycleButton = new Button(this.width - 185, 0, 20, 20, new TextComponent(String.valueOf(info.index)).withStyle(ChatFormatting.GOLD), (button -> {
((List<String>) info.value).remove("");
this.reload = true;
info.index = info.index + 1;
@@ -651,7 +616,7 @@ public abstract class ConfigGui
}
else if (info.screenButton)
{
Button widget = createButton(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));
}));
@@ -679,22 +644,14 @@ public abstract class ConfigGui
if (list.getHoveredButton(mouseX,mouseY).isPresent()) {
AbstractWidget buttonWidget = list.getHoveredButton(mouseX,mouseY).get();
Component text = ButtonEntry.buttonsWithText.get(buttonWidget);
#if PRE_MC_1_19
TranslatableComponent name = new TranslatableComponent(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.field.getName());
#else
Component name = Component.translatable(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.field.getName());
#endif
String key = translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.field.getName() + ".@tooltip";
if (info.error != null && text.equals(name)) renderTooltip(matrices, (Component) info.error.getValue(), mouseX, mouseY);
else if (I18n.exists(key) && (text != null && text.equals(name))) {
List<Component> list = new ArrayList<>();
for (String str : I18n.get(key).split("\n"))
#if PRE_MC_1_19
list.add(new TextComponent(str));
#else
list.add(Component.translatable(str));
#endif
renderComponentTooltip(matrices, list, mouseX, mouseY);
}
}
@@ -781,29 +738,17 @@ public abstract class ConfigGui
{
if (button != null)
{
#if PRE_MC_1_19_3
button.y = y;
#else
button.setY(y);
#endif
button.render(matrices, mouseX, mouseY, tickDelta);
}
if (resetButton != null)
{
#if PRE_MC_1_19_3
resetButton.y = y;
#else
resetButton.setY(y);
#endif
resetButton.render(matrices, mouseX, mouseY, tickDelta);
}
if (indexButton != null)
{
#if PRE_MC_1_19_3
indexButton.y = y;
#else
indexButton.setY(y);
#endif
indexButton.render(matrices, mouseX, mouseY, tickDelta);
}
if (text != null && (!text.getString().contains("spacer") || button != null))
@@ -826,16 +771,4 @@ public abstract class ConfigGui
}
#endif
}
public static Button createButton(int x, int y, int width, int height, Component component, Button.OnPress onPress) {
#if PRE_MC_1_19_3
return new Button(x, y, width, height, component, onPress);
#else
return Button.builder(component, onPress)
.bounds(x, y, width, height)
.build();
#endif
}
}
@@ -45,11 +45,10 @@ public class TexturedButtonWidget extends ImageButton {
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text);
}
// public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, OnTooltip tooltipSupplier, Component text) {
// super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, tooltipSupplier, text);
// }
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, OnTooltip tooltipSupplier, Component text) {
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, tooltipSupplier, text);
}
#if PRE_MC_1_19_4
@Override
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta) {
#if PRE_MC_1_17_1
@@ -65,35 +64,9 @@ public class TexturedButtonWidget extends ImageButton {
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
#if PRE_MC_1_19_3
this.blit(matrices, this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
this.blit(matrices, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
#else
this.blit(matrices, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
this.blit(matrices, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
#endif
super.renderButton(matrices, mouseX, mouseY, delta);
}
#else
@Override
public void renderWidget(PoseStack matrices, int mouseX, int mouseY, float delta) {
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
int i = 1;
if (!this.active)
i = 0;
else if (this.isHovered)
i = 2;
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
this.blit(matrices, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
this.blit(matrices, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
super.renderWidget(matrices, mouseX, mouseY, delta);
}
#endif
}
@@ -55,10 +55,7 @@ import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.server.IntegratedServer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
#if PRE_MC_1_19
import net.minecraft.network.chat.TextComponent;
#endif
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
@@ -368,11 +365,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper
@Override
public void sendChatMessage(String string)
{
#if PRE_MC_1_19
getPlayer().sendMessage(new TextComponent(string), getPlayer().getUUID());
#else
getPlayer().sendSystemMessage(Component.translatable(string));
#endif
}
/**
@@ -37,11 +37,8 @@ import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import net.minecraft.client.renderer.LightTexture;
#if PRE_MC_1_19_3
import com.mojang.math.Vector3f;
#else
import org.joml.Vector3f;
#endif
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.objects.math.Vec3d;
import com.seibel.lod.core.objects.math.Vec3f;
@@ -136,11 +133,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public double getGamma()
{
#if PRE_MC_1_19
return MC.options.gamma;
#else
return MC.options.gamma().get();
#endif
}
@Override
@@ -20,35 +20,14 @@
package com.seibel.lod.common.wrappers.world;
import java.awt.Color;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
#if PRE_MC_1_19_3
import net.minecraft.data.BuiltinRegistries;
#else
import net.minecraft.core.registries.Registries;
import net.minecraft.server.level.ServerLevel;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
#endif
#if POST_MC_1_19
import net.minecraft.data.worldgen.biome.EndBiomes;
import net.minecraft.data.worldgen.biome.NetherBiomes;
#endif
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.material.MaterialColor;
@@ -79,12 +58,13 @@ public class BiomeWrapper implements IBiomeWrapper
return biomeWrapper;
}
/** Returns a color int for the given biome. */
#if PRE_MC_1_19
@Override
public int getColorForBiome(int x, int z)
{
int colorInt;
switch (biome.biomeCategory)
{
@@ -141,147 +121,7 @@ public class BiomeWrapper implements IBiomeWrapper
return colorInt;
}
#else
private static int _colorEnd(Biome b) {
return Blocks.END_STONE.defaultMaterialColor().col;
}
private static int _colorNether(Biome b) {
return Blocks.NETHERRACK.defaultMaterialColor().col;
}
private static int _colorSand(Biome b) {
return Blocks.SAND.defaultMaterialColor().col;
}
private static int _colorStone(Biome b) {
return Blocks.STONE.defaultMaterialColor().col;
}
private static int _colorGravel(Biome b) {
return Blocks.GRAVEL.defaultMaterialColor().col;
}
private static int _colorDripStone(Biome b) {
return Blocks.DRIPSTONE_BLOCK.defaultMaterialColor().col;
}
private static int _colorMoss(Biome b) {
return Blocks.MOSS_BLOCK.defaultMaterialColor().col;
}
private static int _colorSculk(Biome b) {
return Blocks.SCULK.defaultMaterialColor().col;
}
private static int _colorMushoom(Biome b) {
return Blocks.MYCELIUM.defaultMaterialColor().col;
}
private static int _colorBamboo(Biome b) {
return Blocks.BAMBOO.defaultMaterialColor().col;
}
private static int _colorSnow(Biome b) {
return Blocks.SNOW.defaultMaterialColor().col;
}
private static int _colorIce(Biome b) {
return Blocks.ICE.defaultMaterialColor().col;
}
private static int _colorRedSand(Biome b) {
return Blocks.RED_SAND.defaultMaterialColor().col;
}
private static int _colorSoulSand(Biome b) {
return Blocks.SOUL_SAND.defaultMaterialColor().col;
}
private static int _colorBasalt(Biome b) {
return Blocks.BASALT.defaultMaterialColor().col;
}
private static int _colorWater(Biome b) {
return b.getWaterColor();
}
private static int _colorFoliage(Biome b) {
return b.getFoliageColor();
}
private static Biome _get(ResourceKey<Biome> r) {
#if PRE_MC_1_19_3
return BuiltinRegistries.BIOME.getOrThrow(r);
#else
// The fix below is a hacky and convoluted way of doing the thing above so...
// TODO[1.19.3]: Find a less convoluted method to get the Minecraft's registryAccess
return (((ServerLevel) ((WorldWrapper) LodUtil.getServerWorldFromDimension(ApiShared.lodWorld.getLodDimension(SingletonHandler.get(IMinecraftClientWrapper.class).getCurrentDimension()).dimension)).getWorld()).getServer().registryAccess()) // This entire mess is just to get Minecraft's registryAccess (FIND A BETTER WAY LATER)
.registryOrThrow(Registries.BIOME).getOrThrow(r); // Once youve gotten the registryAccess, the rest is easy
#endif
}
//FIXME: THIS IS HELL!
private static final ImmutableBiMap<Biome, Function<Biome, Integer>> BIOME_COLOR_MAP =
ImmutableBiMap.<Biome, Function<Biome, Integer>>builder()
.put(_get(Biomes.SNOWY_PLAINS), BiomeWrapper::_colorSnow)
.put(_get(Biomes.ICE_SPIKES), BiomeWrapper::_colorIce)
.put(_get(Biomes.DESERT), BiomeWrapper::_colorSand)
.put(_get(Biomes.SWAMP), BiomeWrapper::_colorWater)
.put(_get(Biomes.MANGROVE_SWAMP), BiomeWrapper::_colorWater)
.put(_get(Biomes.FOREST), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.FLOWER_FOREST), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.BIRCH_FOREST), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.DARK_FOREST), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.OLD_GROWTH_BIRCH_FOREST), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.OLD_GROWTH_PINE_TAIGA), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.OLD_GROWTH_SPRUCE_TAIGA), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.TAIGA), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.SNOWY_TAIGA), BiomeWrapper::_colorSnow)
.put(_get(Biomes.WINDSWEPT_GRAVELLY_HILLS), BiomeWrapper::_colorGravel)
.put(_get(Biomes.WINDSWEPT_FOREST), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.JUNGLE), BiomeWrapper::_colorFoliage)
.put(_get(Biomes.BAMBOO_JUNGLE), BiomeWrapper::_colorBamboo)
.put(_get(Biomes.BADLANDS), BiomeWrapper::_colorRedSand)
.put(_get(Biomes.ERODED_BADLANDS), BiomeWrapper::_colorRedSand)
.put(_get(Biomes.WOODED_BADLANDS), BiomeWrapper::_colorStone)
.put(_get(Biomes.GROVE), BiomeWrapper::_colorSnow)
.put(_get(Biomes.SNOWY_SLOPES), BiomeWrapper::_colorSnow)
.put(_get(Biomes.FROZEN_PEAKS), BiomeWrapper::_colorIce)
.put(_get(Biomes.JAGGED_PEAKS), BiomeWrapper::_colorSnow)
.put(_get(Biomes.STONY_PEAKS), BiomeWrapper::_colorStone)
.put(_get(Biomes.RIVER), BiomeWrapper::_colorWater)
.put(_get(Biomes.FROZEN_RIVER), BiomeWrapper::_colorIce)
.put(_get(Biomes.BEACH), BiomeWrapper::_colorSand)
.put(_get(Biomes.SNOWY_BEACH), BiomeWrapper::_colorSnow)
.put(_get(Biomes.STONY_SHORE), BiomeWrapper::_colorStone)
.put(_get(Biomes.WARM_OCEAN), BiomeWrapper::_colorWater)
.put(_get(Biomes.LUKEWARM_OCEAN), BiomeWrapper::_colorWater)
.put(_get(Biomes.DEEP_LUKEWARM_OCEAN), BiomeWrapper::_colorWater)
.put(_get(Biomes.OCEAN), BiomeWrapper::_colorWater)
.put(_get(Biomes.DEEP_OCEAN), BiomeWrapper::_colorWater)
.put(_get(Biomes.COLD_OCEAN), BiomeWrapper::_colorWater)
.put(_get(Biomes.DEEP_COLD_OCEAN), BiomeWrapper::_colorWater)
.put(_get(Biomes.FROZEN_OCEAN), BiomeWrapper::_colorIce)
.put(_get(Biomes.DEEP_FROZEN_OCEAN), BiomeWrapper::_colorIce)
.put(_get(Biomes.MUSHROOM_FIELDS), BiomeWrapper::_colorMushoom)
.put(_get(Biomes.DRIPSTONE_CAVES), BiomeWrapper::_colorDripStone)
.put(_get(Biomes.LUSH_CAVES), BiomeWrapper::_colorMoss)
.put(_get(Biomes.DEEP_DARK), BiomeWrapper::_colorSculk)
.put(_get(Biomes.NETHER_WASTES), BiomeWrapper::_colorNether)
.put(_get(Biomes.WARPED_FOREST), BiomeWrapper::_colorNether)
.put(_get(Biomes.CRIMSON_FOREST), BiomeWrapper::_colorNether)
.put(_get(Biomes.SOUL_SAND_VALLEY), BiomeWrapper::_colorSoulSand)
.put(_get(Biomes.BASALT_DELTAS), BiomeWrapper::_colorBasalt)
.put(_get(Biomes.THE_END), BiomeWrapper::_colorEnd)
.put(_get(Biomes.END_HIGHLANDS), BiomeWrapper::_colorEnd)
.put(_get(Biomes.END_MIDLANDS), BiomeWrapper::_colorEnd)
.put(_get(Biomes.SMALL_END_ISLANDS), BiomeWrapper::_colorEnd)
.put(_get(Biomes.END_BARRENS), BiomeWrapper::_colorEnd)
.build();
@Override
public int getColorForBiome(int x, int z)
{
int colorInt;
Function<Biome, Integer> colorFunction = BIOME_COLOR_MAP.get(biome);
if (colorFunction != null)
{
colorInt = colorFunction.apply(biome);
}
else
{
colorInt = biome.getGrassColor(x, z);
}
return colorInt;
}
#endif
@Override public String getName()
{
return biome.toString();
@@ -59,9 +59,6 @@ import com.seibel.lod.common.wrappers.worldGeneration.step.StepStructureReferenc
import com.seibel.lod.common.wrappers.worldGeneration.step.StepStructureStart;
import com.seibel.lod.common.wrappers.worldGeneration.step.StepSurface;
#if POST_MC_1_19_3
import net.minecraft.core.registries.Registries;
#endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
@@ -370,11 +367,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
CompoundTag chunkData = null;
try
{
#if POST_MC_1_19
chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos).get().orElse(null);
#else
chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos);
#endif
}
catch (Exception e)
{
@@ -383,14 +376,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (chunkData == null)
{
return new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if POST_MC_1_17_1, level #endif
#if POST_MC_1_18_1, level.registryAccess().registryOrThrow(
#if PRE_MC_1_19_3
Registry.BIOME_REGISTRY
#else
Registries.BIOME
#endif
), null #endif
#if POST_MC_1_17_1, level #endif
#if POST_MC_1_18_1, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null #endif
);
}
else
@@ -400,14 +387,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} catch (Exception e) {
LOAD_LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
return new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if POST_MC_1_17_1, level #endif
#if POST_MC_1_18_1, level.registryAccess().registryOrThrow(
#if PRE_MC_1_19_3
Registry.BIOME_REGISTRY
#else
Registries.BIOME
#endif
), null #endif
#if POST_MC_1_17_1, level #endif
#if POST_MC_1_18_1, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null #endif
);
}
}
@@ -35,32 +35,14 @@ import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.storage.ChunkScanAccess;
#endif
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if PRE_MC_1_19
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
#else
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.levelgen.RandomState;
#if POST_MC_1_19_3
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.core.registries.Registries;
#endif
#endif
import net.minecraft.world.level.storage.WorldData;
public final class GlobalParameters
{
public final ChunkGenerator generator;
#if PRE_MC_1_19
public final StructureManager structures;
#else
public final StructureTemplateManager structures;
public final RandomState randomState;
#endif
#if PRE_MC_1_19_3
public final WorldGenSettings worldGenSettings;
#else
public final WorldOptions worldOptions;
#endif
public final ThreadedLevelLightEngine lightEngine;
public final LodBuilder lodBuilder;
public final LodDimension lodDim;
@@ -82,16 +64,10 @@ public final class GlobalParameters
lightEngine = (ThreadedLevelLightEngine) level.getLightEngine();
MinecraftServer server = level.getServer();
WorldData worldData = server.getWorldData();
registry = server.registryAccess();
#if PRE_MC_1_19_3
worldGenSettings = worldData.worldGenSettings();
registry = server.registryAccess();
biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
worldSeed = worldGenSettings.seed();
#else
worldOptions = worldData.worldGenOptions();
biomes = registry.registryOrThrow(Registries.BIOME);
worldSeed = worldOptions.seed();
#endif
#if POST_MC_1_18_1
biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed));
chunkScanner = level.getChunkSource().chunkScanner();
@@ -99,8 +75,5 @@ public final class GlobalParameters
structures = server.getStructureManager();
generator = level.getChunkSource().getGenerator();
fixerUpper = server.getFixerUpper();
#if POST_MC_1_19
randomState = level.getChunkSource().randomState();
#endif
}
}
@@ -25,6 +25,7 @@ import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenStruct
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if POST_MC_1_18_1
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
@@ -60,22 +61,15 @@ public final class ThreadedParameters
level = param.level;
#if PRE_MC_1_18_1
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, level);
#elif PRE_MC_1_19
structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.level.dimension(), param.generator, level, param.generator.getBiomeSource(), param.worldSeed,
param.fixerUpper);
#else
structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.level.dimension(), param.generator, param.randomState, level, param.generator.getBiomeSource(), param.worldSeed,
param.level.dimension(), param.generator, level, param.generator.getBiomeSource(), param.worldSeed,
param.fixerUpper);
#endif
}
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param) {
#if PRE_MC_1_19_3
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param)
{
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if POST_MC_1_18_1, structCheck #endif);
#else
structFeat = new WorldGenStructFeatManager(param.worldOptions, genLevel #if POST_MC_1_18_1, structCheck #endif);
#endif
}
}
@@ -37,14 +37,9 @@ import java.util.Objects;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
#if POST_MC_1_19_3
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
#endif
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
@@ -57,9 +52,7 @@ import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import net.minecraft.world.level.levelgen.Heightmap;
#if POST_MC_1_18_1
import net.minecraft.world.level.levelgen.blending.BlendingData;
#if PRE_MC_1_19
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.ticks.LevelChunkTicks;
@@ -67,10 +60,8 @@ import net.minecraft.world.ticks.LevelChunkTicks;
#if POST_MC_1_18_2
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
#if PRE_MC_1_19
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
#endif
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.Fluid;
@@ -79,9 +70,7 @@ import org.apache.logging.log4j.Logger;
public class ChunkLoader
{
#if POST_MC_1_19
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#elif POST_MC_1_18_1
#if POST_MC_1_18_1
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#endif
private static final String TAG_UPGRADE_DATA = "UpgradeData";
@@ -108,20 +97,13 @@ public class ChunkLoader
private static LevelChunkSection[] readSections(LevelAccessor level, LevelLightEngine lightEngine, ChunkPos chunkPos, CompoundTag chunkData)
{
#if POST_MC_1_18_1
#if PRE_MC_1_19_3
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#else
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#endif
#if PRE_MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif PRE_MC_1_19
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
#endif
int i = #if PRE_MC_1_17_1 16; #else level.getSectionsCount(); #endif
@@ -240,13 +222,8 @@ public class ChunkLoader
return null;
#else
BlendingData blendingData = readBlendingData(tagLevel);
#if PRE_MC_1_19
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null;
#else
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || level.getChunk(chunkPos.getMiddleBlockX(),chunkPos.getMiddleBlockZ()).isOldNoiseGeneration()))
return null;
#endif
#endif
long inhabitedTime = tagLevel.getLong("InhabitedTime");
@@ -274,17 +251,10 @@ public class ChunkLoader
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
tagLevel.getList("LiquidsToBeTicked", 9)#if POST_MC_1_17_1, level #endif);
#else
#if PRE_MC_1_19_3
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
string -> Registry.BLOCK.getOptional(ResourceLocation.tryParse(string)), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
string -> Registry.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#else
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
(string -> BuiltInRegistries.BLOCK.getOptional(ResourceLocation.tryParse(string))), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
string -> BuiltInRegistries.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#endif
#endif
LevelChunkSection[] levelChunkSections = readSections(level, lightEngine, chunkPos, tagLevel);
@@ -294,9 +264,8 @@ public class ChunkLoader
LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null);
#else
LevelChunk chunk = new LevelChunk((Level) level, chunkPos, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null, blendingData);
fluidTicks, inhabitedTime, levelChunkSections, null, blendingData);
#endif
// Set some states after object creation
chunk.setLightCorrect(isLightOn);
@@ -46,10 +46,7 @@ import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.LevelHeightAccessor;
#endif
import net.minecraft.world.level.LightLayer;
#if PRE_MC_1_19
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
@@ -59,6 +56,7 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.lighting.LevelLightEngine;
@@ -292,11 +290,7 @@ public class LightedWorldGenRegion extends WorldGenRegion {
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
#if PRE_MC_1_19
int i = (Minecraft.getInstance()).options.biomeBlendRadius;
#else
int i = (Minecraft.getInstance()).options.biomeBlendRadius().get();
#endif
if (i == 0)
return colorResolver.getColor((Biome) _getBiome(blockPos), blockPos.getX(), blockPos.getZ());
int j = (i * 2 + 1) * (i * 2 + 1);
@@ -37,67 +37,37 @@ import net.minecraft.core.SectionPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if PRE_MC_1_19
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.level.StructureFeatureManager;
#else
#if POST_MC_1_19_3
import net.minecraft.world.level.levelgen.WorldOptions;
#endif
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.StructureManager;
#endif
#if POST_MC_1_18_1
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
#if PRE_MC_1_19
public class WorldGenStructFeatManager extends StructureFeatureManager {
#else
public class WorldGenStructFeatManager extends StructureManager {
#endif
final WorldGenLevel genLevel;
#if PRE_MC_1_19_3
WorldGenSettings worldGenSettings;
#else
WorldOptions worldOptions;
#endif
#if POST_MC_1_18_1
StructureCheck structureCheck;
#endif
#if PRE_MC_1_19_3
public WorldGenStructFeatManager(WorldGenSettings worldGenSettings,
WorldGenLevel genLevel #if POST_MC_1_18_1, StructureCheck structureCheck #endif) {
super(genLevel,
worldGenSettings #if POST_MC_1_19_3 .options() #endif
#if POST_MC_1_18_1, structureCheck #endif
);
this.worldGenSettings = worldGenSettings;
#else
public WorldGenStructFeatManager(WorldOptions worldOptions,
WorldGenLevel genLevel #if POST_MC_1_18_1, StructureCheck structureCheck #endif) {
super(genLevel, worldOptions, structureCheck);
this.worldOptions = worldOptions;
#endif
super(genLevel, worldGenSettings #if POST_MC_1_18_1, structureCheck #endif);
this.genLevel = genLevel;
this.worldGenSettings = worldGenSettings;
}
@Override
public WorldGenStructFeatManager forWorldGenRegion(WorldGenRegion worldGenRegion) {
if (worldGenRegion == genLevel)
return this;
return new WorldGenStructFeatManager(
#if PRE_MC_1_19_3 worldGenSettings #else worldOptions #endif
, worldGenRegion #if POST_MC_1_18_1, structureCheck #endif);
return new WorldGenStructFeatManager(worldGenSettings, worldGenRegion #if POST_MC_1_18_1, structureCheck #endif);
}
private ChunkAccess _getChunk(int x, int z, ChunkStatus status) {
@@ -152,7 +122,6 @@ public class WorldGenStructFeatManager extends StructureManager {
return builder.build();
}
#else
#if PRE_MC_1_19
@Override
public List<StructureStart> startsForFeature(SectionPos sectionPos, Predicate<ConfiguredStructureFeature<?, ?>> predicate) {
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
@@ -197,52 +166,6 @@ public class WorldGenStructFeatManager extends StructureManager {
if (chunk == null) return (Map<ConfiguredStructureFeature<?, ?>, LongSet>) Stream.empty();
return chunk.getAllReferences();
}
#else
@Override
public List<StructureStart> startsForStructure(ChunkPos sectionPos, Predicate<Structure> predicate) {
ChunkAccess chunk = _getChunk(sectionPos.x, sectionPos.z, ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return List.of();
// Copied from StructureFeatureManager::startsForFeature(...)
Map<Structure, LongSet> map = chunk.getAllReferences();
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Iterator<Map.Entry<Structure, LongSet>> var5 = map.entrySet().iterator();
while(var5.hasNext()) {
Map.Entry<Structure, LongSet> entry = var5.next();
Structure configuredStructureFeature = entry.getKey();
if (predicate.test(configuredStructureFeature)) {
LongSet var10002 = (LongSet)entry.getValue();
Objects.requireNonNull(builder);
this.fillStartsForStructure(configuredStructureFeature, var10002, builder::add);
}
}
return builder.build();
}
@Override
public List<StructureStart> startsForStructure(SectionPos sectionPos, Structure structure) {
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return (List<StructureStart>) Stream.empty();
// Copied from StructureFeatureManager::startsForFeature(...)
LongSet longSet = chunk.getReferencesForStructure(structure);
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Objects.requireNonNull(builder);
this.fillStartsForStructure(structure, longSet, builder::add);
return builder.build();
}
@Override
public Map<Structure, LongSet> getAllStructuresAt(BlockPos blockPos) {
SectionPos sectionPos = SectionPos.of(blockPos);
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return (Map<Structure, LongSet>) Stream.empty();
return chunk.getAllReferences();
}
#endif
#endif
#endif
}
@@ -27,9 +27,7 @@ import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.core.Registry;
import net.minecraft.server.level.WorldGenRegion;
#if PRE_MC_1_19
import net.minecraft.world.level.StructureFeatureManager;
#endif
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
@@ -73,14 +71,8 @@ public final class StepBiomes {
// System.out.println("StepBiomes: "+chunk.getPos());
#if PRE_MC_1_18_1
environment.params.generator.createBiomes(environment.params.biomes, chunk);
#elif PRE_MC_1_19
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif PRE_MC_1_19_3
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
chunk = environment.joinSync(environment.params.generator.createBiomes(Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif
}
@@ -34,9 +34,7 @@ import net.minecraft.util.Mth;
#if POST_MC_1_17_1
import net.minecraft.world.level.LevelHeightAccessor;
#endif
#if PRE_MC_1_19
import net.minecraft.world.level.StructureFeatureManager;
#endif
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunkSection;
@@ -81,11 +79,8 @@ public final class StepNoise {
#elif PRE_MC_1_18_1
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif PRE_MC_1_19
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), environment.params.randomState,
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif
LodUtil.checkInterruptsUnchecked(); // Speed up termination responsiveness
@@ -32,9 +32,7 @@ import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
#if PRE_MC_1_19
import net.minecraft.world.level.StructureFeatureManager;
#endif
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
@@ -66,27 +66,13 @@ public final class StepStructureStart {
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
}
#if PRE_MC_1_19
if (environment.params.worldGenSettings.generateFeatures()) {
#elif PRE_MC_1_19_3
if (environment.params.worldGenSettings.generateStructures()) {
#else
if (environment.params.worldOptions.generateStructures()) {
#endif
for (ChunkAccess chunk : chunksToDo) {
// System.out.println("StepStructureStart: "+chunk.getPos());
#if PRE_MC_1_19
environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#elif PRE_MC_1_19_3
environment.params.generator.createStructures(environment.params.registry, environment.params.randomState, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#else
environment.params.generator.createStructures(environment.params.registry,
environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, environment.params.structures);
#endif
#if POST_MC_1_18_1
try {
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
@@ -60,10 +60,9 @@ public final class StepSurface {
// System.out.println("StepSurface: "+chunk.getPos());
#if PRE_MC_1_18_1
environment.params.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
#elif PRE_MC_1_19
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
#else
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), environment.params.randomState, chunk);
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
#endif
}
}
@@ -1,47 +0,0 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used when rendering
accessible field com/mojang/blaze3d/vertex/VertexBuffer indexCount I
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer renderChunkStorage Ljava/util/concurrent/atomic/AtomicReference;
accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkStorage
accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo
accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chunk Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;
# lighting
accessible field net/minecraft/client/renderer/LightTexture lightPixels Lcom/mojang/blaze3d/platform/NativeImage;
accessible field net/minecraft/client/renderer/LightTexture lightTexture Lnet/minecraft/client/renderer/texture/DynamicTexture;
accessible field net/minecraft/world/level/lighting/LevelLightEngine blockEngine Lnet/minecraft/world/level/lighting/LayerLightEngine;
accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine Lnet/minecraft/world/level/lighting/LayerLightEngine;
# world generation
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/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible method net/minecraft/server/level/ChunkMap readChunk (Lnet/minecraft/world/level/ChunkPos;)Ljava/util/concurrent/CompletableFuture;
# lod generation from save file
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# grabbing textures
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite animatedTexture Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$AnimatedTexture;
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite width I
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite height I
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite mainImage [Lcom/mojang/blaze3d/platform/NativeImage;
accessible class net/minecraft/client/renderer/texture/TextureAtlasSprite$AnimatedTexture
accessible method net/minecraft/client/renderer/texture/TextureAtlasSprite$AnimatedTexture getFrameX (I)I
accessible method net/minecraft/client/renderer/texture/TextureAtlasSprite$AnimatedTexture getFrameY (I)I
extendable class com/mojang/math/Matrix4f
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
@@ -1,44 +0,0 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used when rendering
accessible field com/mojang/blaze3d/vertex/VertexBuffer indexCount I
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer renderChunkStorage Ljava/util/concurrent/atomic/AtomicReference;
accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkStorage
accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo
accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chunk Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;
# lighting
accessible field net/minecraft/client/renderer/LightTexture lightPixels Lcom/mojang/blaze3d/platform/NativeImage;
accessible field net/minecraft/client/renderer/LightTexture lightTexture Lnet/minecraft/client/renderer/texture/DynamicTexture;
accessible field net/minecraft/world/level/lighting/LevelLightEngine blockEngine Lnet/minecraft/world/level/lighting/LayerLightEngine;
accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine Lnet/minecraft/world/level/lighting/LayerLightEngine;
# world generation
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/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible method net/minecraft/server/level/ChunkMap readChunk (Lnet/minecraft/world/level/ChunkPos;)Ljava/util/concurrent/CompletableFuture;
# lod generation from save file
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# grabbing textures
accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameX (I)I
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameY (I)I
accessible field net/minecraft/client/renderer/texture/SpriteContents animatedTexture Lnet/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture;
accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
@@ -0,0 +1,3 @@
{
"accessWidener": "lod.accesswidener"
}
+1 -1
Submodule core updated: 131ba164cf...83a2fa86d9
+15
View File
@@ -0,0 +1,15 @@
architectury {
common(rootProject.enabled_platforms.split(","))
}
dependencies {
// Use Fabric loader as both Quilt and Fabric can use it
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
// Fabric API
fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version)
fabricApi.module("fabric-key-binding-api-v1", rootProject.fabric_api_version)
fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version)
compileClasspath(project(path: ":common", configuration: "namedElements")) { transitive false }
}
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.mixins;
package com.seibel.lod.fabric_like.mixins;
import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.world.level.chunk.ChunkGenerator;
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.mixins;
package com.seibel.lod.fabric_like.mixins;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -42,18 +42,15 @@ import net.minecraft.world.level.material.FogType;
@Mixin(FogRenderer.class)
public class MixinFogRenderer {
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
private static ILodConfigWrapperSingleton CONFIG;
// Using this instead of Float.MAX_VALUE because Sodium don't like it.
private static final float A_REALLY_REALLY_BIG_VALUE = 420694206942069.F;
private static final float A_EVEN_LARGER_VALUE = 42069420694206942069.F;
@Inject(at = @At("RETURN"), method = "setupFog")
#if PRE_MC_1_19_1
@Inject(at = @At("RETURN"), method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZ)V")
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, CallbackInfo callback) {
#else
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float g, CallbackInfo callback) {
#endif
if (CONFIG == null) CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
#if PRE_MC_1_17_1
FluidState fluidState = camera.getFluidInCamera();
boolean cameraNotInFluid = fluidState.isEmpty();
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.mixins;
package com.seibel.lod.fabric_like.mixins;
import com.seibel.lod.common.wrappers.config.ConfigGui;
import com.seibel.lod.common.wrappers.config.TexturedButtonWidget;
@@ -27,9 +27,7 @@ import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import net.minecraft.client.gui.screens.OptionsScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
#if PRE_MC_1_19
import net.minecraft.network.chat.TranslatableComponent;
#endif
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -41,7 +39,7 @@ import java.util.Objects;
/**
* Adds a button to the menu to goto the config
*
* @author coolGi
* @author coolGi2007
* @version 12-02-2021
*/
@Mixin(OptionsScreen.class)
@@ -69,10 +67,6 @@ public class MixinOptionsScreen extends Screen {
// For now it goes to the client option by default
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, "client")),
// Add a title to the screen
#if PRE_MC_1_19
new TranslatableComponent("text.autoconfig." + ModInfo.ID + ".title")));
#else
Component.translatable("text.autoconfig." + ModInfo.ID + ".title")));
#endif
}
}
@@ -17,14 +17,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.mixins;
package com.seibel.lod.fabric_like.mixins;
import com.mojang.blaze3d.vertex.PoseStack;
#if PRE_MC_1_19_3
import com.mojang.math.Matrix4f;
#else
import org.joml.Matrix4f;
#endif
import com.seibel.lod.common.Config;
import com.seibel.lod.common.wrappers.McObjectConverter;
import com.seibel.lod.core.api.ClientApi;
@@ -47,7 +43,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
*
* This is also the mixin for rendering the clouds
*
* @author coolGi
* @author coolGi2007
* @author James Seibel
* @version 12-31-2021
*/
@@ -93,7 +89,7 @@ public class MixinWorldRenderer
callback.cancel();
}
}
#elif PRE_MC_1_19_3
#else
@Inject(method = "renderClouds", at = @At("HEAD"), cancellable = true)
public void renderClouds(PoseStack poseStack, Matrix4f projectionMatrix, float tickDelta, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) {
// get the partial ticks since renderChunkLayer doesn't
@@ -118,30 +114,5 @@ public class MixinWorldRenderer
callback.cancel();
}
}
#else
@Inject(method = "renderClouds", at = @At("HEAD"), cancellable = true)
public void renderClouds(PoseStack poseStack, Matrix4f projectionMatrix, float tickDelta, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) {
// get the partial ticks since renderChunkLayer doesn't
// have access to them
previousPartialTicks = tickDelta;
}
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
{
// only render before solid blocks
if (renderType.equals(RenderType.solid()))
{
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks);
}
if (Config.Client.Advanced.lodOnlyMode) {
callback.cancel();
}
}
#endif
}
@@ -1,4 +1,4 @@
package com.seibel.lod.fabric.mixins.events;
package com.seibel.lod.fabric_like.mixins.events;
import com.mojang.blaze3d.platform.NativeImage;
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.mixins.unsafe;
package com.seibel.lod.fabric_like.mixins.unsafe;
import net.minecraft.server.level.ServerLevel;
import org.spongepowered.asm.mixin.Mixin;
@@ -0,0 +1,18 @@
{
"required": true,
"minVersion": "0.8",
"package": "com.seibel.lod.fabric_like.mixins",
"mixins": [
"unsafe.MixinThreadingDectector"
],
"client": [
"MixinOptionsScreen",
"MixinWorldRenderer",
"MixinFogRenderer",
"MixinChunkGenerator",
"events.MixinLightmap"
],
"injectors": {
"defaultRequire": 1
}
}
+5 -199
View File
@@ -1,35 +1,15 @@
plugins {
id "com.github.johnrengelman.shadow" version "7.1.0"
id "com.github.johnrengelman.shadow" version "7.1.2"
}
// From parent
apply plugin: "java"
apply plugin: "architectury-plugin"
apply plugin: "maven-publish"
apply plugin: "dev.architectury.loom"
archivesBaseName = rootProject.archives_base_name
version = rootProject.mod_version //FIXME: Redef
group = rootProject.maven_group
version = rootProject.mod_version+"-"+rootProject.minecraft_version+"-"+new Date().format("yyyy_MM_dd_HH_mm")
loom {
silentMojangMappingsLicense()
accessWidenerPath.set(project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener"))
}
architectury {
platformSetupLoomIde()
fabric()
}
configurations {
common
shadowMe
implementation.extendsFrom shadowMe
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
developmentFabric.extendsFrom common
@@ -39,60 +19,6 @@ configurations {
modImplementation.extendsFrom addModJar
}
java {
withSourcesJar()
}
jar.dependsOn(project(":core").remapJar)
remapSourcesJar.dependsOn(project(":core").remapJar)
repositories {
// Required for ModMenu
maven { url "https://maven.terraformersmc.com/" }
mavenCentral()
// For parchment mappings
maven { url "https://maven.parchmentmc.org" }
// used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' }
// For Manifold Preprocessor
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Required for importing Modrinth mods
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
// Required for importing CursedForge mods
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
}
}
// These 2 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
content {
includeGroup "fabric-mod"
}
}
flatDir {
dirs "${rootDir}/mods/forge"
content {
includeGroup "forge-mod"
}
}
}
def addMod(path, enabled) {
if (enabled == "2")
dependencies { modImplementation(path) }
@@ -101,47 +27,13 @@ def addMod(path, enabled) {
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
// The following line declares the mojmap mappings & parchment mappings
mappings loom.layered() {
// Mojmap mappings
officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc)
if (
rootProject.minecraft_version != "1.19"
&& rootProject.minecraft_version != "1.19.1"
&& rootProject.minecraft_version != "1.19.2"
&& rootProject.minecraft_version != "1.19.3"
&& rootProject.minecraft_version != "1.19.4"
) // We are not gonna use this build script anymore so dont bother fixing this
parchment("org.parchmentmc.data:parchment-${rootProject.minecraft_version}:${rootProject.parchment_version}@zip")
else
parchment("org.parchmentmc.data:parchment-1.18.2:${rootProject.parchment_version}@zip") // As 1.19.x or higher doesnt have parchment mappings yet, we use 1.18.2 mapping
// Note: parchment may have later mappings at the time you are reading this, but at the time this was written, it didnt
// Check the main branch for the new build system, with the working mappings
}
//Manifold
annotationProcessor "systems.manifold:manifold-preprocessor:${rootProject.manifold_version}"
// Toml
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader unless working with fabric
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
common(project(":core"))
// Fabric loader
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
// Fabric API
addModJar(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-key-binding-api-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version))
// Mod Menu
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}")
@@ -153,7 +45,7 @@ dependencies {
addMod("curse.maven:phosphor-372124:${rootProject.phosphor_version_fabric}", rootProject.enable_phosphor)
// Sodium
addMod("maven.modrinth:sodium:${rootProject.sodium_version}", rootProject.enable_sodium)
addMod("curse.maven:sodium-394468:${rootProject.sodium_version}", rootProject.enable_sodium)
implementation "org.joml:joml:1.10.2"
modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version))
modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version))
@@ -183,12 +75,13 @@ dependencies {
}
*/
// Toml
shadowMe("com.electronwill.night-config:toml:${rootProject.toml_version}") {}
common(project(path: ":common", configuration: "namedElements")) { transitive false }
shadowMe(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
shadowMe files(project(":core").file("build/libs/DistantHorizons-${rootProject.mod_version}.jar"))
// Compression
common 'org.tukaani:xz:1.9'
@@ -200,62 +93,11 @@ dependencies {
task deleteResources(type: Delete) {
delete file("build/resources/main")
}
// Copies the correct accesswidener and renames it
task copyAccessWidener(type: Copy) {
from project(":common").file("src/main/resources/${rootProject.acsessWidenerVersion}.lod.accesswidener")
into(file("build/resources/main"))
rename "${rootProject.acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into file("build/resources/main")
}
task copyCommonResources(type: Copy) {
from fileTree(project(":common").file("src/main/resources"))
into file("build/resources/main")
}
processResources {
dependsOn(copyCoreResources)
dependsOn(copyCommonResources)
dependsOn(copyAccessWidener)
def resourceTargets = ["fabric.mod.json", "META-INF/mods.toml"] // Location of where to put
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
def replaceProperties = [
version : mod_version,
mod_name : mod_name,
authors : mod_authors,
description : mod_description,
homepage : mod_homepage,
source : mod_source,
issues : mod_issues,
minecraft_version : minecraft_version,
compatible_minecraft_versions: compatible_minecraft_versions,
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
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,42)"
inputs.properties replaceProperties
replaceProperties.put 'project', project
filesMatching(resourceTargets) {
expand replaceProperties
}
intoTargets.each { target ->
if (file(target).exists()) {
copy {
from(sourceSets.main.resources) {
include resourceTargets
expand replaceProperties
}
into target
}
}
}
}
runClient {
@@ -267,6 +109,7 @@ runClient {
}
shadowJar {
exclude "architectury.common.json"
configurations = [project.configurations.shadowMe]
relocate 'org.tukaani', 'shaded.tukaani'
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
@@ -284,11 +127,6 @@ remapJar {
}
jar {
manifest {
attributes 'Implementation-Title': rootProject.archives_base_name,
'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.lod.core.JarMain' // When changing the main of the jar change this line
}
classifier "dev"
}
@@ -317,35 +155,3 @@ publishing {
// Add repositories to publish to here.
}
}
task printConfigurations {
doLast {task ->
println "Project Name: $name configurations:"
configurations.each {
println " $it.name"
}
}
}
tasks.withType(JavaCompile) {
// Add Manifold Preprocessor
// def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
// options.compilerArgs += ['-Xplugin:Manifold', "-AMC_VERSION_${excapedMCVersion}"]
//
//options.compilerArgs += ['-deprecation']
//options.compilerArgs += ['-verbose']
//options.compilerArgs += ['-Xlint:unchecked']
//options.compilerArgs += ['-Xdiags:verbose']
//options.compilerArgs += ['-Xprint']
//options.compilerArgs += ['-XprintProcessorInfo']
//options.compilerArgs += ['-XprintRounds']
// println options.compilerArgs
// Set the java version
options.compilerArgs += ['-Xplugin:Manifold']
options.release = rootProject.java_version as Integer
// TODO: make everything use java 8
// options.release = 8 // Use Java 8 for everything so back porting is easier
}
@@ -55,7 +55,7 @@ import org.lwjgl.glfw.GLFW;
* This handles all events sent to the client,
* and is the starting point for most of the mod.
*
* @author coolGi
* @author coolGi2007
* @author Ran
* @version 11-23-2021
*/
@@ -121,7 +121,6 @@ public class ClientProxy
public void worldLoadEvent(Level level)
{
if (Minecraft.getInstance().screen instanceof TitleScreen) return;
if (Minecraft.getInstance().getConnection() == null) return; // In 1.19.3, the multiplayer world load is called twice. The first time is before it actually connects to the multiplayer server (so stuff like ip and name arent sent out yet)
if (level != null) {
eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(level));
}
@@ -29,6 +29,7 @@ import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
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.OptifineAccessor;
import com.seibel.lod.fabric.wrappers.modAccessor.SodiumAccessor;
@@ -42,29 +43,23 @@ import net.fabricmc.api.ClientModInitializer;
* If you are looking for the real start of the mod
* check out the ClientProxy.
*
* @author coolGi
* @author coolGi2007
* @author Ran
* @version 12-1-2021
*/
public class Main implements ClientModInitializer
public class Main
{
// This is a client mod so it should implement ClientModInitializer and in fabric.mod.json it should have "environment": "client"
// Once it works on servers change the implement to ModInitializer and in fabric.mod.json it should be "environment": "*"
public static ClientProxy client_proxy;
// Do if implements ClientModInitializer
// This loads the mod before minecraft loads which causes a lot of issues
@Override
public void onInitializeClient() {
// no.
}
public static boolean isQuilt;
// This loads the mod after minecraft loads which doesn't causes a lot of issues
public static void init() {
scuffedQuiltChecker();
LodCommonMain.initConfig();
LodCommonMain.startup(null, false);
LodCommonMain.startup(null, false, new NetworkHandler());
FabricDependencySetup.createInitialBindings();
FabricDependencySetup.finishBinding();
ApiShared.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
@@ -87,9 +82,18 @@ public class Main implements ClientModInitializer
public static void initServer() {
LodCommonMain.initConfig();
LodCommonMain.startup(null, true);
LodCommonMain.startup(null, true, new NetworkHandler());
FabricDependencySetup.createInitialBindings();
FabricDependencySetup.finishBinding();
ApiShared.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
}
public static void scuffedQuiltChecker() {
try {
Class.forName("org.quiltmc.loader.api.QuiltLoader");
isQuilt = true;
} catch (ClassNotFoundException e) {
isQuilt = false;
}
}
}
@@ -1,24 +0,0 @@
package com.seibel.lod.fabric.mixins;
import com.seibel.lod.core.render.F3Screen;
import net.minecraft.client.gui.components.DebugScreenOverlay;
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;
import java.util.List;
@Mixin(DebugScreenOverlay.class)
public class MixinDebugScreenOverlay {
@Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir) {
List<String> messages = cir.getReturnValue();
for (String i: F3Screen.f3List) {
messages.add(i);
}
}
}
@@ -0,0 +1,44 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.fabric.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);
});
}
}
@@ -0,0 +1,14 @@
package com.seibel.lod.fabric.quilt;
/**
* Used to call classes that are in Quilt
* @author Ran
*/
public class QuiltUtils {
public static boolean isModLoaded(String modId) {
try {
return (boolean) Class.forName("org.quiltmc.loader.api.QuiltLoader").getDeclaredMethod("isModLoaded", String.class).invoke(null, modId);
} catch (Exception ignored) { }
return false;
}
}
@@ -20,6 +20,8 @@
package com.seibel.lod.fabric.wrappers.modAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.lod.fabric.Main;
import com.seibel.lod.fabric.quilt.QuiltUtils;
import net.fabricmc.loader.api.FabricLoader;
public class ModChecker implements IModChecker {
@@ -27,6 +29,10 @@ public class ModChecker implements IModChecker {
@Override
public boolean isModLoaded(String modid) {
return FabricLoader.getInstance().isModLoaded(modid);
if (Main.isQuilt) {
return QuiltUtils.isModLoaded(modid);
} else {
return FabricLoader.getInstance().isModLoaded(modid);
}
}
}
@@ -3,21 +3,14 @@
"minVersion": "0.8",
"package": "com.seibel.lod.fabric.mixins",
"mixins": [
"unsafe.MixinThreadingDectector",
"MixinUtilBackgroudThread",
"events.MixinServerLevel"
],
"client": [
"MixinMinecraft",
"MixinOptionsScreen",
"MixinWorldRenderer",
"MixinFogRenderer",
"MixinChunkGenerator",
"MixinDebugScreenOverlay",
"events.MixinClientLevel",
"events.MixinMinecraft",
"events.MixinBlockUpdate",
"events.MixinLightmap"
"events.MixinBlockUpdate"
],
"server": [
"MixinDedicatedServer"
+2 -4
View File
@@ -18,16 +18,14 @@
"environment": "client",
"entrypoints": {
"client": [
"com.seibel.lod.fabric.Main"
],
"modmenu": [
"com.seibel.lod.fabric.wrappers.config.ModMenuIntegration"
]
},
"mixins": [
"fabric.lod.mixins.json"
"fabric.lod.mixins.json",
"fabric-like.lod.mixins.json"
],
"accessWidener" : "lod.accesswidener",
+2 -188
View File
@@ -1,23 +1,10 @@
plugins {
id "com.github.johnrengelman.shadow" version "7.1.0"
id "com.github.johnrengelman.shadow" version "7.1.2"
}
apply plugin: "java"
apply plugin: "architectury-plugin"
apply plugin: "maven-publish"
apply plugin: "dev.architectury.loom"
archivesBaseName = rootProject.archives_base_name
version = rootProject.mod_version //FIXME: HERE repeated def
group = rootProject.maven_group
version = rootProject.mod_version+"-"+rootProject.minecraft_version+"-"+new Date().format("yyyy_MM_dd_HH_mm")
loom {
silentMojangMappingsLicense()
accessWidenerPath.set(project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener"))
forge {
convertAccessWideners.set(true)
extraAccessWideners.add("lod.accesswidener")
@@ -31,21 +18,11 @@ architectury {
}
configurations {
common
shadowMe
implementation.extendsFrom shadowMe
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
developmentForge.extendsFrom common
}
java {
withSourcesJar()
}
jar.dependsOn(project(":core").remapJar)
remapSourcesJar.dependsOn(project(":core").remapJar)
def addMod(path, enabled) {
if (enabled == "2")
dependencies { modImplementation(path) }
@@ -54,35 +31,6 @@ def addMod(path, enabled) {
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
// The following line declares the mojmap mappings & parchment mappings
mappings loom.layered() {
// Mojmap mappings
officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc)
if (
rootProject.minecraft_version != "1.19"
&& rootProject.minecraft_version != "1.19.1"
&& rootProject.minecraft_version != "1.19.2"
&& rootProject.minecraft_version != "1.19.3"
&& rootProject.minecraft_version != "1.19.4"
) // We are not gonna use this build script anymore so dont bother fixing this
parchment("org.parchmentmc.data:parchment-${rootProject.minecraft_version}:${rootProject.parchment_version}@zip")
else
parchment("org.parchmentmc.data:parchment-1.18.2:${rootProject.parchment_version}@zip") // As 1.19.x or higher doesnt have parchment mappings yet, we use 1.18.2 mapping
// Note: parchment may have later mappings at the time you are reading this, but at the time this was written, it didnt
// Check the main branch for the new build system, with the working mappings
}
//Manifold
annotationProcessor "systems.manifold:manifold-preprocessor:${rootProject.manifold_version}"
// Toml
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
common(project(":core")) { transitive false }
// Forge loader
forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}"
@@ -94,7 +42,6 @@ dependencies {
common(project(path: ":common", configuration: "namedElements")) { transitive false }
shadowMe(project(path: ":common", configuration: "transformProductionForge")) { transitive = false }
shadowMe files(project(":core").file("build/libs/DistantHorizons-${rootProject.mod_version}.jar"))
// forgeDependencies(project(":core")) { transitive false }
@@ -109,66 +56,14 @@ dependencies {
shadowMe 'org.apache.commons:commons-compress:1.21'
}
// Copies the correct accesswidener and renames it
task copyAccessWidener(type: Copy) {
from project(":common").file("src/main/resources/${rootProject.acsessWidenerVersion}.lod.accesswidener")
into(file("build/resources/main"))
rename "${rootProject.acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
}
task copyCoreResources(type: Copy) {
from fileTree(project(":core").file("src/main/resources"))
into file("build/resources/main")
}
task copyCommonResources(type: Copy) {
from fileTree(project(":common").file("src/main/resources"))
into file("build/resources/main")
}
// Put stuff from gradle.properties into the mod info
processResources {
dependsOn(copyAccessWidener)
dependsOn(copyCoreResources)
dependsOn(copyCommonResources)
def resourceTargets = ["fabric.mod.json", "META-INF/mods.toml"] // Location of where to put
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
def replaceProperties = [
version : mod_version,
mod_name : mod_name,
authors : mod_authors,
description : mod_description,
homepage : mod_homepage,
source : mod_source,
issues : mod_issues,
minecraft_version : minecraft_version,
compatible_minecraft_versions: compatible_minecraft_versions,
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
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,42)"
inputs.properties replaceProperties
replaceProperties.put 'project', project
filesMatching(resourceTargets) {
expand replaceProperties
}
intoTargets.each { target ->
if (file(target).exists()) {
copy {
from(sourceSets.main.resources) {
include resourceTargets
expand replaceProperties
}
into target
}
}
}
}
shadowJar {
exclude "architectury.common.json"
dependencies {
exclude(dependency("net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"))
}
@@ -190,11 +85,6 @@ remapJar {
}
jar {
manifest {
attributes 'Implementation-Title': rootProject.archives_base_name,
'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.lod.core.JarMain' // When changing the main of the jar change this line
}
classifier "dev"
}
@@ -223,79 +113,3 @@ publishing {
// Add repositories to publish to here.
}
}
task printConfigurations {
doLast {task ->
println "Project Name: $name configurations:"
configurations.each {
println " $it.name"
}
}
}
repositories {
mavenCentral()
// For parchment mappings
maven { url "https://maven.parchmentmc.org" }
// used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' }
// For Manifold Preprocessor
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
// Required for importing Modrinth mods
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
// Required for importing CursedForge mods
maven {
url "https://www.cursemaven.com"
content {
includeGroup "curse.maven"
}
}
// These 2 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
content {
includeGroup "fabric-mod"
}
}
flatDir {
dirs "${rootDir}/mods/forge"
content {
includeGroup "forge-mod"
}
}
}
tasks.withType(JavaCompile) {
// Add Manifold Preprocessor
// def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
// options.compilerArgs += ['-Xplugin:Manifold', "-AMC_VERSION_${excapedMCVersion}"]
//
//options.compilerArgs += ['-deprecation']
//options.compilerArgs += ['-verbose']
//options.compilerArgs += ['-Xlint:unchecked']
//options.compilerArgs += ['-Xdiags:verbose']
//options.compilerArgs += ['-Xprint']
//options.compilerArgs += ['-XprintProcessorInfo']
//options.compilerArgs += ['-XprintRounds']
// println options.compilerArgs
// Set the java version
options.compilerArgs += ['-Xplugin:Manifold']
options.release = rootProject.java_version as Integer //FIXME: This warning??
// TODO: make everything use java 8
// options.release = 8 // Use Java 8 for everything so back porting is easier?
}
@@ -35,15 +35,9 @@ import com.seibel.lod.common.wrappers.world.WorldWrapper;
import net.minecraft.client.Minecraft;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.event.TickEvent;
#if PRE_MC_1_19
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
#else
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.event.level.ChunkEvent;
import net.minecraftforge.event.level.LevelEvent;
#endif
import net.minecraftforge.eventbus.api.SubscribeEvent;
/**
@@ -70,56 +64,30 @@ public class ForgeClientProxy
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
#if PRE_MC_1_19
clientApi.clientChunkLoadEvent(new ChunkWrapper(event.getChunk(), event.getWorld()), WorldWrapper.getWorldWrapper(event.getWorld()));
#else
clientApi.clientChunkLoadEvent(new ChunkWrapper(event.getChunk(), event.getLevel()), WorldWrapper.getWorldWrapper(event.getLevel()));
#endif
}
@SubscribeEvent
#if PRE_MC_1_19
public void worldSaveEvent(WorldEvent.Save event)
#else
public void worldSaveEvent(LevelEvent.Save event)
#endif
{
eventApi.worldSaveEvent();
}
/** This is also called when a new dimension loads */
@SubscribeEvent
#if PRE_MC_1_19
public void worldLoadEvent(WorldEvent.Load event)
#else
public void worldLoadEvent(LevelEvent.Load event)
#endif
{
if (Minecraft.getInstance().screen instanceof TitleScreen) return;
if (Minecraft.getInstance().getConnection() == null) return; // In 1.19.3, the multiplayer world load is called twice. The first time is before it actually connects to the multiplayer server (so stuff like ip and name arent sent out yet)
#if PRE_MC_1_19
if (event.getWorld() != null) {
eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(event.getWorld()));
}
#else
if (event.getLevel() != null) {
eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(event.getLevel()));
}
#endif
}
@SubscribeEvent
#if PRE_MC_1_19
public void worldUnloadEvent(WorldEvent.Unload event)
{
eventApi.worldUnloadEvent(WorldWrapper.getWorldWrapper(event.getWorld()));
}
#else
public void worldUnloadEvent(LevelEvent.Unload event)
{
eventApi.worldUnloadEvent(WorldWrapper.getWorldWrapper(event.getLevel()));
}
#endif
@SubscribeEvent
public void blockChangeEvent(BlockEvent event)
@@ -131,13 +99,8 @@ public class ForgeClientProxy
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
#if PRE_MC_1_19
IChunkWrapper chunk = new ChunkWrapper(event.getWorld().getChunk(event.getPos()), event.getWorld());
DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType());
#else
IChunkWrapper chunk = new ChunkWrapper(event.getLevel().getChunk(event.getPos()), event.getLevel());
DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getLevel().dimensionType());
#endif
// recreate the LOD where the blocks were changed
eventApi.blockChangeEvent(chunk, dimType);
@@ -145,7 +108,7 @@ public class ForgeClientProxy
}
@SubscribeEvent
public void onKeyInput(#if PRE_MC_1_19 InputEvent.KeyInputEvent event #else InputEvent.Key event #endif)
public void onKeyInput(InputEvent.KeyInputEvent event)
{
if (Minecraft.getInstance().player == null) return;
if (event.getAction() != GLFW.GLFW_PRESS) return;
@@ -153,4 +116,5 @@ public class ForgeClientProxy
}
}
@@ -28,6 +28,7 @@ import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.handlers.ReflectionHandler;
import com.seibel.lod.core.handlers.dependencyInjection.ModAccessorHandler;
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.modAccessor.OptifineAccessor;
@@ -38,6 +39,7 @@ import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.ModelDataMap;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
@@ -49,24 +51,12 @@ import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.ExtensionPoint;
#elif MC_1_17_1
import net.minecraftforge.fmlclient.ConfigGuiHandler;
#elif POST_MC_1_19
import net.minecraftforge.client.ConfigScreenHandler;
#else // 1.18+ untill 1.19
#else // 1.18+
import net.minecraftforge.client.ConfigGuiHandler;
#endif
// these imports change due to forge refactoring classes in 1.19
#if POST_MC_1_19
import net.minecraft.util.RandomSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraftforge.client.model.data.ModelData;
#else
import net.minecraftforge.client.model.data.ModelDataMap;
import java.util.Random;
#endif
import java.util.List;
import java.util.Random;
/**
* Initialize and setup the Mod. <br>
@@ -85,7 +75,7 @@ public class ForgeMain implements LodForgeMethodCaller
{
// make sure the dependencies are set up before the mod needs them
LodCommonMain.initConfig();
LodCommonMain.startup(this, !FMLLoader.getDist().isClient());
LodCommonMain.startup(this, !FMLLoader.getDist().isClient(), new NetworkHandler());
ForgeDependencySetup.createInitialBindings();
ForgeDependencySetup.finishBinding();
ApiShared.LOGGER.info("Distant Horizons initializing...");
@@ -109,9 +99,6 @@ public class ForgeMain implements LodForgeMethodCaller
#if PRE_MC_1_17_1
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY,
() -> (client, parent) -> ConfigGui.getScreen(parent, ""));
#elif POST_MC_1_19
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
() -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> ConfigGui.getScreen(parent, "")));
#else
ModLoadingContext.get().registerExtensionPoint(ConfigGuiHandler.ConfigGuiFactory.class,
() -> new ConfigGuiHandler.ConfigGuiFactory((client, parent) -> ConfigGui.getScreen(parent, "")));
@@ -120,27 +107,15 @@ public class ForgeMain implements LodForgeMethodCaller
MinecraftForge.EVENT_BUS.register(forgeClientProxy);
}
#if PRE_MC_1_19_
private final ModelDataMap modelData = new ModelDataMap.Builder().build();
#else
private final ModelData modelData = ModelData.EMPTY;
#endif
private final ModelDataMap dataMap = new ModelDataMap.Builder().build();
@Override
#if PRE_MC_1_19
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, modelData);
return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
}
#else
public List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random)
{
return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, modelData #if POST_MC_1_19, RenderType.solid() #endif);
}
#endif
@Override
public int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z) {
#if MC_1_17_1_I_THINK_ITS_FIXED?
#if MC_1_17_1
return resolver.m_130045_(biome, x, z);
#else
return resolver.getColor(biome, x, z);
@@ -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,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.apache.logging.log4j.LogManager;
import net.minecraft.resources.ResourceLocation;
import com.seibel.lod.forge.fabric.api.event.Event;
import org.apache.logging.log4j.Logger;
class ArrayBackedEvent<T> extends Event<T> {
static final Logger LOGGER = LogManager.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.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.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 = LogManager.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.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.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 = LogManager.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,125 @@
/*
* 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) {
#if MC_1_16_5
return handlePacket(packet.getTransactionId(), packet.getName(), packet.getInternalData());
#else
return handlePacket(packet.getTransactionId(), packet.getIdentifier(), packet.getData());
#endif
}
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,221 @@
/*
* 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 com.seibel.lod.forge.mixins.fabric.mixin.networking.accessor.ClientboundCustomQueryPacketAccessor;
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) ->
#if MC_1_16_5
this.connection.setupCompression(this.server.getCompressionThreshold())
#else
this.connection.setupCompression(this.server.getCompressionThreshold(), true)
#endif
);
}
}
/**
* 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;
#if MC_1_16_5
ret = new ClientboundCustomQueryPacket();
ClientboundCustomQueryPacketAccessor access = (ClientboundCustomQueryPacketAccessor) ret;
access.setIdentifier(channelName);
access.setTransactionId(queryId);
access.setData(buf);
#else
ret = new ClientboundCustomQueryPacket(queryId, channelName, buf);
#endif
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(), #if MC_1_16_5 packet.getName() #else packet.getIdentifier() #endif);
}
@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;
}
}

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