Compare commits
93 Commits
porting-li
...
lithium-0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68c3c6ffa2 | ||
|
|
b5684eae6d | ||
|
|
d31b15122f | ||
|
|
ca931e842b | ||
|
|
c2cf985899 | ||
|
|
d270cf06d9 | ||
|
|
09735958c0 | ||
|
|
6ec14ff755 | ||
|
|
18e872ad12 | ||
|
|
13c6499854 | ||
|
|
b70443edd5 | ||
|
|
38bab21ddf | ||
|
|
f7d98327e0 | ||
|
|
271a58d2af | ||
|
|
a4f5ef1abd | ||
|
|
d87df61412 | ||
|
|
e917272d3a | ||
|
|
bff8caea54 | ||
|
|
97a865c532 | ||
|
|
f6541b0d91 | ||
|
|
6b4139a5ab | ||
|
|
43164acf5e | ||
|
|
73d73c935a | ||
|
|
7863c556f9 | ||
|
|
c0b02ea709 | ||
|
|
9004f12be6 | ||
|
|
b2144a55aa | ||
|
|
882d733203 | ||
|
|
9b6982bf65 | ||
|
|
5635373cff | ||
|
|
76d2c36481 | ||
|
|
ca791ddc74 | ||
|
|
7f08c11a11 | ||
|
|
93b908350e | ||
|
|
4bd7eb8b72 | ||
|
|
a6cf977c5f | ||
|
|
8ed23bc8f3 | ||
|
|
7034995878 | ||
|
|
ca3d776562 | ||
|
|
1a077389c2 | ||
|
|
49bcc60cf5 | ||
|
|
8b1f31ade8 | ||
|
|
4d88e04e3c | ||
|
|
33889c3850 | ||
|
|
37ac3003cb | ||
|
|
28128d06c1 | ||
|
|
6f9620787b | ||
|
|
cbf6c118dc | ||
|
|
d44fa1f8aa | ||
|
|
3d9ae3f018 | ||
|
|
d24f6c2874 | ||
|
|
f190cdd8cb | ||
|
|
e7510eda16 | ||
|
|
d9442c1492 | ||
|
|
93eb2786f2 | ||
|
|
1bef6823c5 | ||
|
|
01152eec95 | ||
|
|
1e39f5370a | ||
|
|
ea50ba38ea | ||
|
|
c00b9fcd7b | ||
|
|
bad5cae4d8 | ||
|
|
e3b1502bb6 | ||
|
|
54fc964987 | ||
|
|
0cbc9aa1a1 | ||
|
|
19e2136ae1 | ||
|
|
126cc03747 | ||
|
|
ceb4936d9d | ||
|
|
3cb888e894 | ||
|
|
7bedc1a7de | ||
|
|
718f6e1369 | ||
|
|
da9ab708a6 | ||
|
|
f22335f0b6 | ||
|
|
a3f2328000 | ||
|
|
529b9a44bb | ||
|
|
1e9a6504a1 | ||
|
|
9c46dcbb94 | ||
|
|
9a1e04389a | ||
|
|
29084d8e3f | ||
|
|
41790ecf1a | ||
|
|
8af7bccdfd | ||
|
|
5f9b3571f8 | ||
|
|
9adfb2514d | ||
|
|
bf2cd1c571 | ||
|
|
dad9a5c2eb | ||
|
|
a3acd46ee1 | ||
|
|
3e8cb80336 | ||
|
|
5c3e713be7 | ||
|
|
284631c321 | ||
|
|
666c4cb1a3 | ||
|
|
19b523eecd | ||
|
|
4a748778dc | ||
|
|
a70073ae3e | ||
|
|
6724814c02 |
53
.github/ISSUE_TEMPLATE/a_incompatibility.yml
vendored
Normal file
53
.github/ISSUE_TEMPLATE/a_incompatibility.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Mod Incompatibility
|
||||
description: Report an incompatibility with another mod.
|
||||
title: '[Incompatibility: <NeoForge/Fabric/NeoForge & Fabric>]: Mod Name'
|
||||
labels:
|
||||
- mod-incompatibility
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1 or 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: moonrise_ver
|
||||
attributes:
|
||||
label: Moonrise Version
|
||||
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: >-
|
||||
The mod loader(s) this conflict happens with. When selecting 'Both' it
|
||||
is expected you will submit logs for both as well. Be sure to update the
|
||||
issue title.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs_crashes
|
||||
attributes:
|
||||
label: Logs and Crash Reports
|
||||
description: >-
|
||||
Include links to all relevant logs and crash reports (prefer using
|
||||
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||
applicable fill in 'N/A' (it is expected you will provide context
|
||||
below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Provide any relevant context, including version of the conflicting mod.
|
||||
validations:
|
||||
required: true
|
||||
55
.github/ISSUE_TEMPLATE/b_bug.yml
vendored
Normal file
55
.github/ISSUE_TEMPLATE/b_bug.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Bug Report
|
||||
description: Report a bug or Vanilla behavior parity issue.
|
||||
title: '[Bug: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||
labels:
|
||||
- bug
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1 or 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: moonrise_ver
|
||||
attributes:
|
||||
label: Moonrise Version
|
||||
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: >-
|
||||
The mod loader(s) this bug happens with. When selecting 'Both' it
|
||||
is expected you will submit logs for both as well. Be sure to update the
|
||||
issue title.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs_crashes
|
||||
attributes:
|
||||
label: Logs and Crash Reports
|
||||
description: >-
|
||||
Include links to all relevant logs and crash reports (prefer using
|
||||
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||
applicable fill in 'N/A' (it is expected you will provide context
|
||||
below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: >-
|
||||
Provide any relevant context, including expected vs. actual behavior,
|
||||
and reproduction steps.
|
||||
validations:
|
||||
required: true
|
||||
65
.github/ISSUE_TEMPLATE/c_performance.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/c_performance.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Performance Problem
|
||||
description: Report a performance problem that doesn't fall under the bug category.
|
||||
title: '[Performance: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||
labels:
|
||||
- performance
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1, 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: moonrise_ver
|
||||
attributes:
|
||||
label: Moonrise Version
|
||||
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: >-
|
||||
The mod loader(s) this bug happens with. When selecting 'Both' it is
|
||||
expected you have tested both as well. Be sure to update the issue
|
||||
title.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs_crashes
|
||||
attributes:
|
||||
label: Logs and Crash Reports
|
||||
description: >-
|
||||
Include links to all relevant logs and crash reports (prefer using
|
||||
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||
applicable fill in 'N/A' (it is expected you will provide context
|
||||
below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: profiler
|
||||
attributes:
|
||||
label: Profiler Data
|
||||
description: >-
|
||||
Provide profiler data showing the problem, from Spark or other tools. In
|
||||
case this is not applicable fill in 'N/A' (it is expected you will
|
||||
provide context below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: >-
|
||||
Provide any relevant context, including reproduction steps, world data,
|
||||
videos, etc.
|
||||
validations:
|
||||
required: true
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord
|
||||
url: https://discord.gg/tuinity
|
||||
about: If you are unsure about something or have general questions, ask in our Discord before opening an issue.
|
||||
34
.github/ISSUE_TEMPLATE/d_feature.yml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/d_feature.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Feature Request
|
||||
description: Request a new feature.
|
||||
title: '[Feature Request: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||
labels:
|
||||
- enhancement
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1, 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: The mod loader(s) this feature request is relevant for.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Description
|
||||
description: >-
|
||||
Describe the feature request in detail, including alternatives
|
||||
considered.
|
||||
validations:
|
||||
required: true
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -31,8 +31,6 @@ jobs:
|
||||
key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-project-local-gradle-caches-
|
||||
- name: "setup concurrentutil"
|
||||
run: ./installConcurrentUtil.sh
|
||||
- name: "execute gradle build"
|
||||
run: ./gradlew build
|
||||
- name: Determine Snapshot Status
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -116,3 +116,6 @@ run/
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
ConcurrentUtil/
|
||||
YamlConfig/
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "ConcurrentUtil"]
|
||||
path = ConcurrentUtil
|
||||
url = https://github.com/Spottedleaf/ConcurrentUtil.git
|
||||
|
||||
Submodule ConcurrentUtil deleted from 08d3ca3241
@@ -12,20 +12,19 @@ Fabric/NeoForge mod for optimising performance of the integrated (singleplayer/L
|
||||
Moonrise aims to optimise the game *without changing Vanilla behavior*. If you find that there are changes to Vanilla behavior,
|
||||
please open an issue.
|
||||
|
||||
Moonrise ports several important [Paper](https://github.com/PaperMC/Paper/)
|
||||
Moonrise is an official port of several important [Paper](https://github.com/PaperMC/Paper/)
|
||||
patches. Listed below are notable patches:
|
||||
- [Starlight](https://github.com/PaperMC/Starlight/)
|
||||
- Chunk system rewrite
|
||||
- Collision optimisations
|
||||
- Entity tracker optimisations
|
||||
- Random ticking optimisations
|
||||
- [Starlight](https://github.com/PaperMC/Starlight/)
|
||||
|
||||
## Known Compatibility Issues
|
||||
| Mod | Status |
|
||||
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Lithium | <details><summary>✅ compatible</summary>Lithium optimises many of the same parts of the game as Moonrise, for example the chunk system. Moonrise will automatically disable conflicting parts of Lithium. This mechanism needs to be manually validated for each Moonrise and Lithium release.</details> |
|
||||
| Radium | <details><summary>✅ compatible</summary>Radium is an unofficial port of Lithium to NeoForge. Radium will automatically disable conflicting parts of itself when Moonrise is present. Any compatibility issues should be reported to Radium first.</details> |
|
||||
| FerriteCore | <details><summary>📝 requires config changes</summary>In `config/ferritecore-mixin.toml`:<br/>Set `replaceNeighborLookup` and `replacePropertyMap` to `false`</details> |
|
||||
| FerriteCore | <details><summary>✅ compatible</summary>FerriteCore optimises some of the same parts of the game as Moonrise. Moonrise will automatically disable conflicting parts of FerriteCore. This mechanism needs to be manually validated for each Moonrise and FerriteCore release.</details> |
|
||||
| C2ME | <details><summary>❌ incompatible</summary>C2ME is based around modifications to the chunk system, which Moonrise replaces wholesale. This makes them fundamentally incompatible.</details> |
|
||||
|
||||
## Configuration
|
||||
|
||||
127
build.gradle
127
build.gradle
@@ -1,40 +1,63 @@
|
||||
import me.modmuss50.mpp.ReleaseType
|
||||
|
||||
plugins {
|
||||
id("xyz.jpenilla.quiet-architectury-loom")
|
||||
id("me.modmuss50.mod-publish-plugin") version "0.7.2" apply false
|
||||
id("java-library")
|
||||
id("net.neoforged.moddev")
|
||||
id("me.modmuss50.mod-publish-plugin") version "0.8.4" apply false
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the version name from the latest Git tag
|
||||
*/
|
||||
// https://stackoverflow.com/questions/28498688/gradle-script-to-autoversion-and-include-the-commit-hash-in-android
|
||||
def getGitCommit = { ->
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
exec {
|
||||
extensions.create("runConfigCommon", RunConfigCommon.class)
|
||||
|
||||
def getGitCommit = providers.exec {
|
||||
commandLine 'git', 'rev-parse', '--short', 'HEAD'
|
||||
standardOutput = stdout
|
||||
}
|
||||
return stdout.toString().trim()
|
||||
}.standardOutput.getAsText().map { it.trim() }
|
||||
|
||||
def aw2at = Aw2AtTask.configureDefault(
|
||||
getProject(),
|
||||
layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
|
||||
sourceSets.main
|
||||
)
|
||||
|
||||
sourceSets.create("lithium")
|
||||
|
||||
neoForge {
|
||||
neoFormVersion = neoform_version
|
||||
validateAccessTransformers = true
|
||||
accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
|
||||
addModdingDependenciesTo sourceSets.lithium
|
||||
}
|
||||
|
||||
runConfigCommon {
|
||||
systemProperties.put "mixin.debug", "true"
|
||||
systemProperties.put "Moonrise.MaxViewDistance", "128"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
compileOnly "net.fabricmc:sponge-mixin:0.15.4+mixin.0.8.7"
|
||||
compileOnly "io.github.llamalad7:mixinextras-common:0.4.1"
|
||||
// work around minecraft (MDG) forcing ASM 9.3 which is incompatible with the above deps...
|
||||
components.withModule("net.neoforged:minecraft-dependencies", RemoveAsmConstraint.class)
|
||||
|
||||
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
||||
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
||||
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
|
||||
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
|
||||
// todo: does cloth publish a platform-agnostic jar in mojang mappings?
|
||||
compileOnly "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
|
||||
|
||||
lithiumCompileOnly("maven.modrinth:lithium:${rootProject.neo_lithium_version}")
|
||||
compileOnly(sourceSets.lithium.output)
|
||||
}
|
||||
|
||||
File awFile = file("src/main/resources/moonrise.accesswidener")
|
||||
tasks.jar {
|
||||
from(sourceSets.lithium.output)
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = rootProject.maven_group
|
||||
version = rootProject.mod_version + "+" + getGitCommit()
|
||||
version = rootProject.mod_version + "+" + getGitCommit.get()
|
||||
|
||||
plugins.apply("xyz.jpenilla.quiet-architectury-loom")
|
||||
plugins.apply("java-library")
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
@@ -44,25 +67,30 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation "org.junit.jupiter:junit-jupiter:${rootProject.junit_version}"
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal {
|
||||
maven {
|
||||
url = "https://repo.papermc.io/repository/maven-public/"
|
||||
mavenContent {
|
||||
includeModule("ca.spottedleaf", "concurrentutil")
|
||||
includeGroup("ca.spottedleaf")
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://api.modrinth.com/maven"
|
||||
url = "https://api.modrinth.com/maven"
|
||||
mavenContent {
|
||||
includeGroup("maven.modrinth")
|
||||
}
|
||||
}
|
||||
maven { url "https://maven.shedaniel.me/" }
|
||||
maven { url "https://maven.terraformersmc.com/releases/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
mappings loom.officialMojangMappings()
|
||||
maven { url = "https://maven.shedaniel.me/" }
|
||||
maven { url = "https://maven.terraformersmc.com/releases/" }
|
||||
}
|
||||
|
||||
// make build reproducible
|
||||
@@ -80,31 +108,22 @@ allprojects {
|
||||
rename { "${it}_${rootProject.base.archivesName.get()}"}
|
||||
}
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath = awFile
|
||||
mixin {
|
||||
useLegacyMixinAp = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
loom.mods {
|
||||
main {
|
||||
sourceSet("main")
|
||||
sourceSet("main", project.rootProject)
|
||||
}
|
||||
}
|
||||
loom.runs.all {
|
||||
ideConfigGenerated true
|
||||
// property "mixin.debug", "true"
|
||||
}
|
||||
|
||||
plugins.apply("me.modmuss50.mod-publish-plugin")
|
||||
plugins.apply 'java-library'
|
||||
plugins.apply 'com.gradleup.shadow'
|
||||
|
||||
configurations.create("libs")
|
||||
configurations.shadow {
|
||||
extendsFrom(configurations.libs)
|
||||
}
|
||||
configurations.implementation {
|
||||
extendsFrom(configurations.libs)
|
||||
}
|
||||
|
||||
publishMods {
|
||||
file = remapJar.archiveFile
|
||||
if (project.version.contains("-beta.")) {
|
||||
type = ReleaseType.BETA
|
||||
} else {
|
||||
@@ -126,8 +145,16 @@ subprojects {
|
||||
minecraftVersions = supportedMcVersions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loom.runs.all {
|
||||
ideConfigGenerated false
|
||||
// Setup a run with lithium for compatibility testing
|
||||
configurations.create("lithium")
|
||||
dependencies {
|
||||
String coordinates = "maven.modrinth:lithium:"
|
||||
if (getProject().name == "Moonrise-NeoForge") {
|
||||
coordinates += rootProject.neo_lithium_version
|
||||
} else {
|
||||
coordinates += rootProject.fabric_lithium_version
|
||||
}
|
||||
lithium coordinates
|
||||
}
|
||||
}
|
||||
|
||||
11
buildSrc/build.gradle.kts
Normal file
11
buildSrc/build.gradle.kts
Normal file
@@ -0,0 +1,11 @@
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
maven("https://maven.fabricmc.net/")
|
||||
maven("https://maven.architectury.dev/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.fabricmc:access-widener:2.1.0")
|
||||
implementation("dev.architectury:at:1.0.1")
|
||||
}
|
||||
152
buildSrc/src/main/java/Aw2AtTask.java
Normal file
152
buildSrc/src/main/java/Aw2AtTask.java
Normal file
@@ -0,0 +1,152 @@
|
||||
import dev.architectury.at.AccessChange;
|
||||
import dev.architectury.at.AccessTransform;
|
||||
import dev.architectury.at.AccessTransformSet;
|
||||
import dev.architectury.at.ModifierChange;
|
||||
import dev.architectury.at.io.AccessTransformFormat;
|
||||
import dev.architectury.at.io.AccessTransformFormats;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import net.fabricmc.accesswidener.AccessWidenerReader;
|
||||
import net.fabricmc.accesswidener.AccessWidenerVisitor;
|
||||
import org.cadixdev.bombe.type.signature.MethodSignature;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.ProjectLayout;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.CacheableTask;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
@CacheableTask
|
||||
public abstract class Aw2AtTask extends DefaultTask {
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.NONE)
|
||||
public abstract RegularFileProperty getInputFile();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
|
||||
@Inject
|
||||
public abstract ProjectLayout getLayout();
|
||||
|
||||
public static TaskProvider<Aw2AtTask> configureDefault(
|
||||
final Project project,
|
||||
final File awFile,
|
||||
final SourceSet sourceSet
|
||||
) {
|
||||
final TaskProvider<Aw2AtTask> aw2at = project.getTasks().register("aw2at", Aw2AtTask.class, task -> {
|
||||
task.getOutputFile().set(project.getLayout().getBuildDirectory().file("aw2at/files/accesstransformer.cfg"));
|
||||
task.getInputFile().set(awFile);
|
||||
});
|
||||
|
||||
final TaskProvider<CopyTask> copyTask = project.getTasks().register("copyAt", CopyTask.class, copy -> {
|
||||
copy.getInputFile().set(aw2at.flatMap(Aw2AtTask::getOutputFile));
|
||||
copy.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("aw2at/dir"));
|
||||
copy.getDestination().set("META-INF/accesstransformer.cfg");
|
||||
});
|
||||
|
||||
sourceSet.resources(resources -> {
|
||||
resources.srcDir(copyTask.flatMap(CopyTask::getOutputDirectory));
|
||||
});
|
||||
|
||||
return aw2at;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void run() {
|
||||
try (final BufferedReader reader = Files.newBufferedReader(this.getInputFile().get().getAsFile().toPath())) {
|
||||
final AccessTransformSet accessTransformSet = toAccessTransformSet(reader);
|
||||
Files.deleteIfExists(this.getOutputFile().get().getAsFile().toPath());
|
||||
Files.createDirectories(this.getOutputFile().get().getAsFile().toPath().getParent());
|
||||
writeLF(AccessTransformFormats.FML, this.getOutputFile().get().getAsFile().toPath(), accessTransformSet);
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLF(final AccessTransformFormat format, final Path path, final AccessTransformSet at) throws IOException {
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
final BufferedWriter writer = new BufferedWriter(stringWriter);
|
||||
format.write(writer, at);
|
||||
writer.close();
|
||||
final List<String> lines = Arrays.stream(stringWriter.toString()
|
||||
// unify line endings
|
||||
.replace("\r\n", "\n")
|
||||
.split("\n"))
|
||||
// skip blank lines
|
||||
.filter(it -> !it.isBlank())
|
||||
// sort
|
||||
.sorted()
|
||||
.toList();
|
||||
Files.writeString(path, String.join("\n", lines));
|
||||
}
|
||||
|
||||
// Below methods are heavily based on architectury-loom Aw2At class (MIT licensed)
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 FabricMC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
public static AccessTransformSet toAccessTransformSet(final BufferedReader reader) throws IOException {
|
||||
AccessTransformSet atSet = AccessTransformSet.create();
|
||||
|
||||
new AccessWidenerReader(new AccessWidenerVisitor() {
|
||||
@Override
|
||||
public void visitClass(final String name, final AccessWidenerReader.AccessType access, final boolean transitive) {
|
||||
atSet.getOrCreateClass(name).merge(toAt(access));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethod(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) {
|
||||
atSet.getOrCreateClass(owner).mergeMethod(MethodSignature.of(name, descriptor), toAt(access));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitField(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) {
|
||||
atSet.getOrCreateClass(owner).mergeField(name, toAt(access));
|
||||
}
|
||||
}).read(reader);
|
||||
|
||||
return atSet;
|
||||
}
|
||||
|
||||
public static AccessTransform toAt(final AccessWidenerReader.AccessType access) {
|
||||
return switch (access) {
|
||||
case ACCESSIBLE -> AccessTransform.of(AccessChange.PUBLIC);
|
||||
case EXTENDABLE, MUTABLE -> AccessTransform.of(AccessChange.PUBLIC, ModifierChange.REMOVE);
|
||||
};
|
||||
}
|
||||
}
|
||||
44
buildSrc/src/main/java/CopyTask.java
Normal file
44
buildSrc/src/main/java/CopyTask.java
Normal file
@@ -0,0 +1,44 @@
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.Stream;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
public abstract class CopyTask extends DefaultTask {
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getInputFile();
|
||||
|
||||
@Input
|
||||
public abstract Property<String> getDestination();
|
||||
|
||||
@OutputDirectory
|
||||
public abstract DirectoryProperty getOutputDirectory();
|
||||
|
||||
@TaskAction
|
||||
public void run() {
|
||||
final Path outputDirPath = this.getOutputDirectory().get().getAsFile().toPath();
|
||||
try {
|
||||
try (final Stream<Path> walk = Files.walk(outputDirPath)) {
|
||||
for (final Path path : walk.sorted(Comparator.reverseOrder()).toList()) {
|
||||
Files.delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
final Path destFile = outputDirPath.resolve(this.getDestination().get());
|
||||
Files.createDirectories(destFile.getParent());
|
||||
|
||||
Files.copy(this.getInputFile().get().getAsFile().toPath(), destFile);
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
buildSrc/src/main/java/RemoveAsmConstraint.java
Normal file
24
buildSrc/src/main/java/RemoveAsmConstraint.java
Normal file
@@ -0,0 +1,24 @@
|
||||
import java.util.Objects;
|
||||
import org.gradle.api.artifacts.CacheableRule;
|
||||
import org.gradle.api.artifacts.ComponentMetadataContext;
|
||||
import org.gradle.api.artifacts.ComponentMetadataRule;
|
||||
|
||||
@CacheableRule
|
||||
public abstract class RemoveAsmConstraint implements ComponentMetadataRule {
|
||||
@Override
|
||||
public void execute(final ComponentMetadataContext ctx) {
|
||||
ctx.getDetails().allVariants(variants -> {
|
||||
variants.withDependencies(deps -> {
|
||||
deps.forEach(dep -> {
|
||||
if (Objects.equals(dep.getGroup(), "org.ow2.asm")) {
|
||||
if (dep.getVersionConstraint().getStrictVersion() != null) {
|
||||
dep.version(v -> {
|
||||
v.require(v.getStrictVersion());
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
5
buildSrc/src/main/java/RunConfigCommon.java
Normal file
5
buildSrc/src/main/java/RunConfigCommon.java
Normal file
@@ -0,0 +1,5 @@
|
||||
import org.gradle.api.provider.MapProperty;
|
||||
|
||||
public abstract class RunConfigCommon {
|
||||
public abstract MapProperty<String, String> getSystemProperties();
|
||||
}
|
||||
@@ -1,51 +1,61 @@
|
||||
plugins {
|
||||
id("xyz.jpenilla.quiet-architectury-loom")
|
||||
id 'maven-publish'
|
||||
id 'com.gradleup.shadow'
|
||||
}
|
||||
import java.util.stream.Collectors
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
||||
|
||||
configurations.create("libs")
|
||||
configurations.shadow {
|
||||
extendsFrom(configurations.libs)
|
||||
}
|
||||
configurations.implementation {
|
||||
extendsFrom(configurations.libs)
|
||||
plugins {
|
||||
id("quiet-fabric-loom")
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add('shadow', project([path: ":", configuration: "namedElements"]))
|
||||
runtimeOnly(project(":").sourceSets.main.output)
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
mappings loom.officialMojangMappings()
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
|
||||
|
||||
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||
runtimeOnly(project(":").sourceSets.main.output)
|
||||
runtimeOnly(project(":").sourceSets.lithium.output)
|
||||
shadow(project(":"))
|
||||
compileOnly(project(":"))
|
||||
|
||||
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
||||
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
||||
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
|
||||
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||
modImplementation "com.terraformersmc:modmenu:11.0.1"
|
||||
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
|
||||
|
||||
modImplementation fabricApiLibs.fabric.api
|
||||
modImplementation platform(fabricApiLibs.bom)
|
||||
modImplementation fabricApiLibs.command.api.v2
|
||||
modImplementation fabricApiLibs.lifecycle.events.v1
|
||||
include fabricApiLibs.command.api.v2
|
||||
include fabricApiLibs.base
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
|
||||
tasks.processResources {
|
||||
def properties = [
|
||||
"version": project.version,
|
||||
"minecraft_version": minecraft_version,
|
||||
"loader_version": loader_version,
|
||||
"mod_version": mod_version
|
||||
]
|
||||
inputs.properties(properties)
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
|
||||
expand properties
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
tasks.shadowJar {
|
||||
archiveClassifier = "dev-all"
|
||||
destinationDirectory = layout.buildDirectory.dir("libs")
|
||||
configurations = [project.configurations.shadow]
|
||||
relocate 'ca.spottedleaf.concurrentutil', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.concurrentutil'
|
||||
relocate 'ca.spottedleaf.yamlconfig', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.yamlconfig'
|
||||
relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml'
|
||||
}
|
||||
|
||||
publishMods {
|
||||
file = remapJar.archiveFile
|
||||
modLoaders = ["fabric"]
|
||||
|
||||
modrinth {
|
||||
@@ -59,26 +69,57 @@ publishMods {
|
||||
incompatible(
|
||||
"not-enough-crashes",
|
||||
"starlight",
|
||||
"c2me-fabric"
|
||||
"c2me"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath.set(getRootProject().file("src/main/resources/moonrise.accesswidener"))
|
||||
mixin {
|
||||
useLegacyMixinAp = false
|
||||
}
|
||||
runs.configureEach {
|
||||
ideConfigGenerated true
|
||||
}
|
||||
mods {
|
||||
main {
|
||||
sourceSet("main")
|
||||
sourceSet("main", project.rootProject)
|
||||
sourceSet("lithium", project.rootProject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
def classPathGroups = SourceSetHelper.getClasspath(loom.mods.main, getProject()).stream()
|
||||
.map(File.&getAbsolutePath)
|
||||
.collect(Collectors.joining(File.pathSeparator))
|
||||
|
||||
systemProperty("fabric.classPathGroups", classPathGroups)
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
loom.runs.configureEach { cfg ->
|
||||
runConfigCommon.systemProperties.get().each {
|
||||
cfg.property it.key, it.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a run with lithium for compatibility testing
|
||||
sourceSets.create("lithium")
|
||||
configurations.create("lithium")
|
||||
loom {
|
||||
createRemapConfigurations(sourceSets.lithium)
|
||||
runs {
|
||||
register("lithiumClient") {
|
||||
client()
|
||||
property "mixin.debug", "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
configurations.modLithiumRuntimeOnly {
|
||||
extendsFrom configurations.lithium
|
||||
}
|
||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||
}
|
||||
dependencies {
|
||||
modLithiumRuntimeOnly "maven.modrinth:lithium:${rootProject.lithium_version}"
|
||||
}
|
||||
|
||||
@@ -1,51 +1,52 @@
|
||||
package ca.spottedleaf.moonrise.fabric;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import com.mojang.datafixers.DSL;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class FabricHooks implements PlatformHooks {
|
||||
public final class FabricHooks extends BaseChunkSystemHooks implements PlatformHooks {
|
||||
|
||||
public interface OnExplosionDetonate {
|
||||
void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
|
||||
}
|
||||
|
||||
public static final Event<OnExplosionDetonate> ON_EXPLOSION_DETONATE = EventFactory.createArrayBacked(
|
||||
OnExplosionDetonate.class,
|
||||
listeners -> (final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) -> {
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
listeners[i].onExplosion(world, explosion, possiblyAffecting, diameter);
|
||||
}
|
||||
}
|
||||
);
|
||||
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
|
||||
|
||||
@Override
|
||||
public String getBrand() {
|
||||
return "Moonrise";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModLoaded(final String modId) {
|
||||
return FabricLoader.getInstance().isModLoaded(modId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos) {
|
||||
return blockState.getLightEmission();
|
||||
@@ -58,16 +59,6 @@ public final class FabricHooks implements PlatformHooks {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
|
||||
ON_EXPLOSION_DETONATE.invoker().onExplosion(world, explosion, possiblyAffecting, diameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCurrentlyLoadingChunk() {
|
||||
return false;
|
||||
@@ -85,7 +76,12 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel)newChunk.getLevel(), newChunk);
|
||||
if (!(original instanceof ImposterProtoChunk)) {
|
||||
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,17 +90,19 @@ public final class FabricHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk) {
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel)chunk.getLevel(), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
|
||||
|
||||
}
|
||||
|
||||
@@ -121,13 +119,43 @@ public final class FabricHooks implements PlatformHooks {
|
||||
@Override
|
||||
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
|
||||
final List<Entity> into) {
|
||||
final Collection<EnderDragonPart> parts = world.dragonParts();
|
||||
if (parts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final EnderDragonPart part : parts) {
|
||||
if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
|
||||
into.add(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity> void addToGetEntities(final Level world, final EntityTypeTest<Entity, T> entityTypeTest, final AABB boundingBox,
|
||||
final Predicate<? super T> predicate, final List<? super T> into, final int maxCount) {
|
||||
if (into.size() >= maxCount) {
|
||||
// fix neoforge issue: do not add if list is already full
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<EnderDragonPart> parts = world.dragonParts();
|
||||
if (parts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final EnderDragonPart part : parts) {
|
||||
if (!part.getBoundingBox().intersects(boundingBox)) {
|
||||
continue;
|
||||
}
|
||||
final T casted = (T)entityTypeTest.tryCast(part);
|
||||
if (casted != null && (predicate == null || predicate.test(casted))) {
|
||||
into.add(casted);
|
||||
if (into.size() >= maxCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,12 +204,12 @@ public final class FabricHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long configAutoSaveInterval() {
|
||||
public long configAutoSaveInterval(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int configMaxAutoSavePerTick() {
|
||||
public int configMaxAutoSavePerTick(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
||||
}
|
||||
|
||||
@@ -189,4 +217,47 @@ public final class FabricHooks implements PlatformHooks {
|
||||
public boolean configFixMC159283() {
|
||||
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceNoSave(final ChunkAccess chunk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||
final int fromVersion, final int toVersion) {
|
||||
return (CompoundTag)dataFixer.update(
|
||||
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
|
||||
).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainChunkLoadHook() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadEntity(final Entity entity) {
|
||||
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
|
||||
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
|
||||
return currentRange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,39 +36,41 @@
|
||||
"accessWidener": "moonrise.accesswidener",
|
||||
"depends": {
|
||||
"fabricloader": ">=${loader_version}",
|
||||
"minecraft": ">=1.21 <=1.21.1",
|
||||
"minecraft": ">1.21.3 <1.21.5",
|
||||
"fabric-command-api-v2": "*"
|
||||
},
|
||||
"custom": {
|
||||
"lithium:options": {
|
||||
"mixin.ai.poi": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.alloc.entity_tracker": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.chunk.entity_class_groups": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.collections.chunk_tickets": false,
|
||||
"mixin.collections.entity_ticking": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.world.tick_scheduler": false,
|
||||
"mixin.world.explosions.block_raycast": false,
|
||||
"mixin.world.explosions.cache_exposure": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.alloc.chunk_ticking": false,
|
||||
"mixin.alloc.entity_tracker": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.ai.poi": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.chunk.entity_class_groups": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.util.entity_movement_tracking": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.entity.collisions.intersection": false,
|
||||
"mixin.entity.collisions.movement": false,
|
||||
"mixin.entity.collisions.unpushable_cramming": false
|
||||
}
|
||||
"mixin.entity.collisions.unpushable_cramming": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.util.entity_movement_tracking": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.world.explosions.block_raycast": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.world.tick_scheduler": false
|
||||
},
|
||||
"ferritecore:disabled_options": [
|
||||
"replaceNeighborLookup",
|
||||
"replacePropertyMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package ca.spottedleaf.moonrise.fabric;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
|
||||
class MixinAuditTest {
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
void auditMixins() {
|
||||
MixinEnvironment.getCurrentEnvironment().audit();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,26 @@
|
||||
# Done to increase the memory available to gradle.
|
||||
org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.daemon=false
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=true
|
||||
# Fabric Properties
|
||||
# check these on https://modmuss50.me/fabric.html
|
||||
minecraft_version=1.21.1
|
||||
supported_minecraft_versions=1.21,1.21.1
|
||||
loader_version=0.16.5
|
||||
neoforge_version=21.1.62
|
||||
snakeyaml_version=2.2
|
||||
concurrentutil_version=0.0.2-SNAPSHOT
|
||||
cloth_version=15.0.128
|
||||
lithium_version=mc1.21.1-0.13.1
|
||||
minecraft_version=1.21.4
|
||||
loader_version=0.16.9
|
||||
supported_minecraft_versions=1.21.4
|
||||
neoforge_version=21.4.59-beta
|
||||
neoform_version=1.21.4-20241203.161809
|
||||
fabric_api_version=0.114.3+1.21.4
|
||||
snakeyaml_version=2.3
|
||||
concurrentutil_version=0.0.3
|
||||
yamlconfig_version=1.0.2
|
||||
cloth_version=17.0.144
|
||||
modmenu_version=13.0.0-beta.1
|
||||
junit_version=5.11.3
|
||||
# version ids from modrinth
|
||||
fabric_lithium_version=zVOQw7YU
|
||||
neo_lithium_version=CfXh2ZF6
|
||||
# Mod Properties
|
||||
mod_version=0.1.0-SNAPSHOT
|
||||
mod_version=0.2.0-SNAPSHOT
|
||||
maven_group=ca.spottedleaf.moonrise
|
||||
archives_base_name=moonrise
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
3
gradlew
vendored
3
gradlew
vendored
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -eou pipefail
|
||||
|
||||
git submodule update --init --recursive
|
||||
cd ConcurrentUtil
|
||||
mvn install
|
||||
@@ -1,9 +1,10 @@
|
||||
import net.fabricmc.loom.util.aw2at.Aw2At
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import net.neoforged.moddevgradle.internal.RunGameTask
|
||||
|
||||
plugins {
|
||||
id("xyz.jpenilla.quiet-architectury-loom")
|
||||
id("net.neoforged.moddev")
|
||||
id 'maven-publish'
|
||||
id 'com.gradleup.shadow'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -13,41 +14,90 @@ repositories {
|
||||
}
|
||||
}
|
||||
|
||||
configurations.implementation {
|
||||
extendsFrom(configurations.shadow)
|
||||
def aw2at = Aw2AtTask.configureDefault(
|
||||
getProject(),
|
||||
rootProject.layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
|
||||
sourceSets.main
|
||||
)
|
||||
|
||||
neoForge {
|
||||
version = rootProject.neoforge_version
|
||||
validateAccessTransformers = true
|
||||
accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
|
||||
mods {
|
||||
moonrise {
|
||||
sourceSet sourceSets.main
|
||||
sourceSet rootProject.sourceSets.main
|
||||
sourceSet rootProject.sourceSets.lithium
|
||||
}
|
||||
}
|
||||
runs {
|
||||
client {
|
||||
client()
|
||||
}
|
||||
server {
|
||||
server()
|
||||
}
|
||||
}
|
||||
unitTest {
|
||||
enable()
|
||||
testedMod = mods.moonrise
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add('shadow', project([path: ":", configuration: "namedElements"]))
|
||||
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
|
||||
runtimeOnly(project(":").sourceSets.main.output)
|
||||
runtimeOnly(project(":").sourceSets.lithium.output)
|
||||
shadow(project(":"))
|
||||
compileOnly(project(":"))
|
||||
|
||||
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
||||
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
||||
additionalRuntimeClasspath libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
|
||||
modImplementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
|
||||
include "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
|
||||
implementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
|
||||
jarJar "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
|
||||
tasks.processResources {
|
||||
def properties = [
|
||||
"version": project.version,
|
||||
"minecraft_version": minecraft_version,
|
||||
"mod_version": mod_version
|
||||
]
|
||||
inputs.properties(properties)
|
||||
filesMatching("META-INF/neoforge.mods.toml") {
|
||||
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
|
||||
expand properties
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
tasks.jar {
|
||||
archiveClassifier = "dev"
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
archiveClassifier = "dev-all"
|
||||
destinationDirectory = layout.buildDirectory.dir("libs")
|
||||
configurations = [project.configurations.shadow]
|
||||
relocate 'ca.spottedleaf.concurrentutil', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.concurrentutil'
|
||||
relocate 'ca.spottedleaf.yamlconfig', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.yamlconfig'
|
||||
relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml'
|
||||
}
|
||||
|
||||
Aw2At.setup(getProject(), tasks.remapJar)
|
||||
tasks.register("productionJar", Zip.class) {
|
||||
archiveClassifier = ""
|
||||
archiveExtension = "jar"
|
||||
destinationDirectory = layout.buildDirectory.dir("libs")
|
||||
from(tasks.jarJar)
|
||||
from(zipTree(tasks.shadowJar.archiveFile))
|
||||
}
|
||||
|
||||
tasks.assemble {
|
||||
dependsOn tasks.productionJar
|
||||
}
|
||||
|
||||
publishMods {
|
||||
file = productionJar.archiveFile
|
||||
modLoaders = ["neoforge"]
|
||||
|
||||
modrinth {
|
||||
@@ -65,3 +115,42 @@ publishMods {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
neoForge.runs.configureEach { cfg ->
|
||||
runConfigCommon.systemProperties.get().each {
|
||||
cfg.systemProperties.put it.key, it.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a run with lithium for compatibility testing
|
||||
neoForge {
|
||||
runs {
|
||||
lithiumClient {
|
||||
client()
|
||||
disableIdeRun()
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.withType(RunGameTask).configureEach {
|
||||
if (name == "runLithiumClient") {
|
||||
return
|
||||
}
|
||||
def out = gameDirectory.get().getAsFile().toPath().resolve("mods/lithium-tmp.jar")
|
||||
doFirst {
|
||||
Files.deleteIfExists(out)
|
||||
}
|
||||
}
|
||||
def lithium = configurations.lithium
|
||||
tasks.runLithiumClient {
|
||||
def out = gameDirectory.get().getAsFile().toPath().resolve("mods/lithium-tmp.jar")
|
||||
doFirst {
|
||||
for (File file in lithium) {
|
||||
Files.copy(file.toPath(), out, StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
}
|
||||
doLast {
|
||||
Files.deleteIfExists(out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
loom.platform=neoforge
|
||||
@@ -1,11 +1,16 @@
|
||||
package ca.spottedleaf.moonrise.neoforge;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import com.mojang.datafixers.DSL;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -13,16 +18,18 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.fml.loading.LoadingModList;
|
||||
import net.neoforged.neoforge.common.CommonHooks;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import net.neoforged.neoforge.entity.PartEntity;
|
||||
@@ -30,16 +37,26 @@ import net.neoforged.neoforge.event.EventHooks;
|
||||
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
|
||||
import net.neoforged.neoforge.event.level.ChunkDataEvent;
|
||||
import net.neoforged.neoforge.event.level.ChunkEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class NeoForgeHooks implements PlatformHooks {
|
||||
public final class NeoForgeHooks extends BaseChunkSystemHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public String getBrand() {
|
||||
return "Moonrise";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModLoaded(final String modId) {
|
||||
final ModList modList = ModList.get();
|
||||
if (modList == null) {
|
||||
return LoadingModList.get().getModFileById(modId) != null;
|
||||
}
|
||||
return modList.isLoaded(modId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos) {
|
||||
return blockState.getLightEmission(world, pos);
|
||||
@@ -52,16 +69,6 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
|
||||
EventHooks.onExplosionDetonate(world, explosion, possiblyAffecting, diameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
|
||||
return EventHooks.getExplosionKnockback(world, explosion, entity, original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCurrentlyLoadingChunk() {
|
||||
return true;
|
||||
@@ -88,10 +95,12 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
final ChunkPos pos = holder.getPos();
|
||||
|
||||
EventHooks.fireChunkTicketLevelUpdated(
|
||||
world, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ),
|
||||
oldLevel, newLevel, holder.vanillaChunkHolder
|
||||
world, CoordinateUtils.getChunkKey(pos.x, pos.z),
|
||||
oldLevel, newLevel, holder
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,7 +110,7 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
|
||||
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, world, data));
|
||||
}
|
||||
|
||||
@@ -118,7 +127,12 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
@Override
|
||||
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
|
||||
final List<Entity> into) {
|
||||
for (final PartEntity<?> part : world.getPartEntities()) {
|
||||
final Collection<PartEntity<?>> parts = world.dragonParts();
|
||||
if (parts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final PartEntity<?> part : parts) {
|
||||
if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
|
||||
into.add(part);
|
||||
}
|
||||
@@ -133,9 +147,18 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final PartEntity<?> part : world.getPartEntities()) {
|
||||
final Collection<PartEntity<?>> parts = world.dragonParts();
|
||||
if (parts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final PartEntity<?> part : parts) {
|
||||
if (!part.getBoundingBox().intersects(boundingBox)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final T casted = (T)entityTypeTest.tryCast(part);
|
||||
if (casted != null && casted.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(casted))) {
|
||||
if (casted != null && (predicate == null || predicate.test(casted))) {
|
||||
into.add(casted);
|
||||
if (into.size() >= maxCount) {
|
||||
break;
|
||||
@@ -193,12 +216,12 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long configAutoSaveInterval() {
|
||||
public long configAutoSaveInterval(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int configMaxAutoSavePerTick() {
|
||||
public int configMaxAutoSavePerTick(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
||||
}
|
||||
|
||||
@@ -206,4 +229,47 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
public boolean configFixMC159283() {
|
||||
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceNoSave(final ChunkAccess chunk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||
final int fromVersion, final int toVersion) {
|
||||
return (CompoundTag)dataFixer.update(
|
||||
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
|
||||
).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainChunkLoadHook() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
|
||||
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, chunkData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadEntity(final Entity entity) {
|
||||
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
|
||||
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
|
||||
return currentRange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ displayURL = "https://github.com/Tuinity/Moonrise"
|
||||
authors = "Spottedleaf"
|
||||
description = "Optimisation mod for the dedicated and integrated server."
|
||||
displayTest = "IGNORE_ALL_VERSION"
|
||||
"ferritecore:disabled_options" = [
|
||||
"replaceNeighborLookup",
|
||||
"replacePropertyMap"
|
||||
]
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "neoforge"
|
||||
@@ -24,7 +28,7 @@ side = "BOTH"
|
||||
[[dependencies.moonrise]]
|
||||
modId = "minecraft"
|
||||
type = "required"
|
||||
versionRange = "[1.21,1.21.2)"
|
||||
versionRange = "(1.21.3,1.21.5)"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
||||
|
||||
@@ -36,10 +40,6 @@ type = "incompatible"
|
||||
modId = "starlight"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "lithium"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
# unofficial lithium port
|
||||
modId = "canary"
|
||||
@@ -54,3 +54,31 @@ config = "moonrise.mixins.json"
|
||||
|
||||
[[mixins]]
|
||||
config = "moonrise-neoforge.mixins.json"
|
||||
|
||||
["lithium:options"]
|
||||
"mixin.ai.poi" = false
|
||||
"mixin.alloc.deep_passengers" = false
|
||||
"mixin.alloc.entity_tracker" = false
|
||||
"mixin.block.flatten_states" = false
|
||||
"mixin.chunk.entity_class_groups" = false
|
||||
"mixin.chunk.no_validation" = false
|
||||
"mixin.collections.chunk_tickets" = false
|
||||
"mixin.collections.entity_ticking" = false
|
||||
"mixin.entity.collisions.intersection" = false
|
||||
"mixin.entity.collisions.movement" = false
|
||||
"mixin.entity.collisions.unpushable_cramming" = false
|
||||
"mixin.entity.replace_entitytype_predicates" = false
|
||||
"mixin.math.fast_blockpos" = false
|
||||
"mixin.math.fast_util" = false
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space" = false
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets" = false
|
||||
"mixin.shapes.blockstate_cache" = false
|
||||
"mixin.shapes.optimized_matching" = false
|
||||
"mixin.shapes.specialized_shapes" = false
|
||||
"mixin.util.block_tracking" = false
|
||||
"mixin.util.entity_movement_tracking" = false
|
||||
"mixin.world.block_entity_ticking" = false
|
||||
"mixin.world.chunk_access" = false
|
||||
"mixin.world.explosions.block_raycast" = false
|
||||
"mixin.world.temperature_cache" = false
|
||||
"mixin.world.tick_scheduler" = false
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package ca.spottedleaf.moonrise.neoforge;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
|
||||
class MixinAuditTest {
|
||||
@Test
|
||||
void auditMixins() {
|
||||
final ClassLoader old = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(MixinAuditTest.class.getClassLoader());
|
||||
MixinEnvironment.getCurrentEnvironment().audit();
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ pluginManagement {
|
||||
maven {
|
||||
name = 'jmp'
|
||||
url = 'https://repo.jpenilla.xyz/snapshots'
|
||||
mavenContent { snapshotsOnly() }
|
||||
}
|
||||
maven {
|
||||
name = 'architectury'
|
||||
@@ -23,9 +22,10 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.295" apply false
|
||||
id 'com.gradleup.shadow' version '8.3.0' apply false
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
|
||||
id("quiet-fabric-loom") version "1.9.312" apply false
|
||||
id("net.neoforged.moddev") version "2.0.61-beta" apply false
|
||||
id 'com.gradleup.shadow' version '8.3.5' apply false
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
@@ -37,7 +37,7 @@ dependencyResolutionManagement {
|
||||
}
|
||||
versionCatalogs {
|
||||
create("fabricApiLibs") {
|
||||
from("net.fabricmc.fabric-api:fabric-api-catalog:0.103.0+1.21.1")
|
||||
from("net.fabricmc.fabric-api:fabric-api-catalog:${fabric_api_version}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,3 +48,6 @@ include("fabric")
|
||||
findProject(":fabric").name = "Moonrise-Fabric"
|
||||
include("neoforge")
|
||||
findProject(":neoforge").name = "Moonrise-NeoForge"
|
||||
|
||||
// includeBuild("../YamlConfig") // Uncomment to use local YamlConfig
|
||||
// includeBuild("../ConcurrentUtil") // Uncomment to use local ConcurrentUtil
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package ca.spottledleaf.moonrise.compat.lithium;
|
||||
|
||||
import net.caffeinemc.mods.lithium.common.world.chunk.ChunkStatusTracker;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
|
||||
public final class LithiumHooks {
|
||||
private LithiumHooks() {}
|
||||
|
||||
public static void onChunkInaccessible(final ServerLevel serverLevel, final ChunkPos pos) {
|
||||
ChunkStatusTracker.onChunkInaccessible(serverLevel, pos);
|
||||
}
|
||||
|
||||
public static void onChunkAccessible(final ServerLevel serverLevel, final LevelChunk chunk) {
|
||||
ChunkStatusTracker.onChunkAccessible(serverLevel, chunk);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,44 @@
|
||||
package ca.spottedleaf.moonrise.common;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import ca.spottedleaf.moonrise.common.util.ChunkSystemHooks;
|
||||
import com.mojang.datafixers.DSL;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface PlatformHooks {
|
||||
public interface PlatformHooks extends ChunkSystemHooks {
|
||||
public static PlatformHooks get() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
public String getBrand();
|
||||
|
||||
public default boolean isModLoaded(final String modId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos);
|
||||
|
||||
public Predicate<BlockState> maybeHasLightEmission();
|
||||
|
||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
|
||||
|
||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original);
|
||||
|
||||
public boolean hasCurrentlyLoadingChunk();
|
||||
|
||||
public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
|
||||
@@ -47,11 +49,11 @@ public interface PlatformHooks {
|
||||
|
||||
public boolean allowAsyncTicketUpdates();
|
||||
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel);
|
||||
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk);
|
||||
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data);
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
|
||||
|
||||
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
|
||||
|
||||
@@ -66,8 +68,6 @@ public interface PlatformHooks {
|
||||
|
||||
public void entityMove(final Entity entity, final long oldSection, final long newSection);
|
||||
|
||||
public boolean screenEntity(final ServerLevel world, final Entity entity, final boolean fromDisk, final boolean event);
|
||||
|
||||
public boolean configFixMC224294();
|
||||
|
||||
public boolean configAutoConfigSendDistance();
|
||||
@@ -82,12 +82,30 @@ public interface PlatformHooks {
|
||||
|
||||
public int configPlayerMaxConcurrentGens();
|
||||
|
||||
public long configAutoSaveInterval();
|
||||
public long configAutoSaveInterval(final ServerLevel world);
|
||||
|
||||
public int configMaxAutoSavePerTick();
|
||||
public int configMaxAutoSavePerTick(final ServerLevel world);
|
||||
|
||||
public boolean configFixMC159283();
|
||||
|
||||
// support for CB chunk mustNotSave
|
||||
public boolean forceNoSave(final ChunkAccess chunk);
|
||||
|
||||
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||
final int fromVersion, final int toVersion);
|
||||
|
||||
public boolean hasMainChunkLoadHook();
|
||||
|
||||
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData);
|
||||
|
||||
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities);
|
||||
|
||||
public void unloadEntity(final Entity entity);
|
||||
|
||||
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk);
|
||||
|
||||
public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
|
||||
|
||||
public static final class Holder {
|
||||
private Holder() {
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config;
|
||||
|
||||
public interface InitialiseHook {
|
||||
|
||||
public void initialise();
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public abstract class TypeAdapter<T, S> {
|
||||
|
||||
public abstract T deserialize(final TypeAdapterRegistry registry, final Object input, final Type type);
|
||||
|
||||
public abstract S serialize(final TypeAdapterRegistry registry, final T value, final Type type);
|
||||
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.InitialiseHook;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.collection.CollectionTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.collection.ListTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.collection.SortedMapTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.BooleanTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.ByteTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.DoubleTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.FloatTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.IntegerTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.LongTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.ShortTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.StringTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.type.BigDecimalTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.type.BigIntegerTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.type.DurationTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.annotation.Adaptable;
|
||||
import ca.spottedleaf.moonrise.common.config.annotation.Serializable;
|
||||
import ca.spottedleaf.moonrise.common.config.type.Duration;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class TypeAdapterRegistry {
|
||||
|
||||
private final Map<Class<?>, TypeAdapter<?, ?>> adapters = new HashMap<>();
|
||||
{
|
||||
this.adapters.put(boolean.class, BooleanTypeAdapter.INSTANCE);
|
||||
this.adapters.put(byte.class, ByteTypeAdapter.INSTANCE);
|
||||
this.adapters.put(short.class, ShortTypeAdapter.INSTANCE);
|
||||
this.adapters.put(int.class, IntegerTypeAdapter.INSTANCE);
|
||||
this.adapters.put(long.class, LongTypeAdapter.INSTANCE);
|
||||
this.adapters.put(float.class, FloatTypeAdapter.INSTANCE);
|
||||
this.adapters.put(double.class, DoubleTypeAdapter.INSTANCE);
|
||||
|
||||
this.adapters.put(Boolean.class, BooleanTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Byte.class, ByteTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Short.class, ShortTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Integer.class, IntegerTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Long.class, LongTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Float.class, FloatTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Double.class, DoubleTypeAdapter.INSTANCE);
|
||||
|
||||
this.adapters.put(String.class, StringTypeAdapter.INSTANCE);
|
||||
|
||||
this.adapters.put(Collection.class, CollectionTypeAdapter.INSTANCE);
|
||||
this.adapters.put(List.class, ListTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Map.class, SortedMapTypeAdapter.SORTED_CASE_INSENSITIVE);
|
||||
this.adapters.put(LinkedHashMap.class, SortedMapTypeAdapter.SORTED_CASE_INSENSITIVE);
|
||||
|
||||
this.adapters.put(BigInteger.class, BigIntegerTypeAdapter.INSTANCE);
|
||||
this.adapters.put(BigDecimal.class, BigDecimalTypeAdapter.INSTANCE);
|
||||
this.adapters.put(Duration.class, DurationTypeAdapter.INSTANCE);
|
||||
}
|
||||
|
||||
public TypeAdapter<?, ?> putAdapter(final Class<?> clazz, final TypeAdapter<?, ?> adapter) {
|
||||
return this.adapters.put(clazz, adapter);
|
||||
}
|
||||
|
||||
public TypeAdapter<?, ?> getAdapter(final Class<?> clazz) {
|
||||
return this.adapters.get(clazz);
|
||||
}
|
||||
|
||||
public Object deserialize(final Object input, final Type type) {
|
||||
TypeAdapter<?, ?> adapter = null;
|
||||
if (type instanceof Class<?> clazz) {
|
||||
adapter = this.adapters.get(clazz);
|
||||
}
|
||||
if (adapter == null && (type instanceof ParameterizedType parameterizedType)) {
|
||||
adapter = this.adapters.get((Class<?>)parameterizedType.getRawType());
|
||||
}
|
||||
|
||||
if (adapter == null) {
|
||||
throw new IllegalArgumentException("No adapter for " + input.getClass() + " with type " + type);
|
||||
}
|
||||
|
||||
return ((TypeAdapter)adapter).deserialize(this, input, type);
|
||||
}
|
||||
|
||||
public Object serialize(final Object input, final Type type) {
|
||||
TypeAdapter<?, ?> adapter = null;
|
||||
if (type instanceof Class<?> clazz) {
|
||||
adapter = this.adapters.get(clazz);
|
||||
}
|
||||
if (adapter == null && (type instanceof ParameterizedType parameterizedType)) {
|
||||
adapter = this.adapters.get((Class<?>)parameterizedType.getRawType());
|
||||
}
|
||||
if (adapter == null) {
|
||||
adapter = this.adapters.get(input.getClass());
|
||||
}
|
||||
|
||||
if (adapter == null) {
|
||||
throw new IllegalArgumentException("No adapter for " + input.getClass() + " with type " + type);
|
||||
}
|
||||
|
||||
return ((TypeAdapter)adapter).serialize(this, input, type);
|
||||
}
|
||||
|
||||
public <T> TypeAdapter<T, Map<Object, Object>> makeAdapter(final Class<? extends T> clazz) throws Exception {
|
||||
final TypeAdapter<T, Map<Object, Object>> ret = new AutoTypeAdapter<>(this, clazz);
|
||||
|
||||
this.putAdapter(clazz, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public <T> void callInitialisers(final T object) {
|
||||
if (object == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final TypeAdapter<?, ?> adapter = this.getAdapter(object.getClass());
|
||||
|
||||
if (!(adapter instanceof AutoTypeAdapter<?> autoTypeAdapter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
((AutoTypeAdapter<T>)autoTypeAdapter).callInitialisers(object);
|
||||
}
|
||||
|
||||
private static final class AutoTypeAdapter<T> extends TypeAdapter<T, Map<Object, Object>> {
|
||||
|
||||
private final TypeAdapterRegistry registry;
|
||||
private final Constructor<? extends T> constructor;
|
||||
private final SerializableField[] fields;
|
||||
|
||||
public AutoTypeAdapter(final TypeAdapterRegistry registry, final Class<? extends T> clazz) throws Exception {
|
||||
this.registry = registry;
|
||||
this.constructor = clazz.getConstructor();
|
||||
this.fields = findSerializableFields(registry, clazz);
|
||||
}
|
||||
|
||||
private static TypeAdapter<?, ?> findOrMakeAdapter(final TypeAdapterRegistry registry, final Class<?> clazz) throws Exception {
|
||||
final TypeAdapter<?, ?> ret = registry.getAdapter(clazz);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (final Annotation annotation : clazz.getAnnotations()) {
|
||||
if (annotation instanceof Adaptable adaptable) {
|
||||
return registry.makeAdapter(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No type adapter for " + clazz + " (Forgot @Adaptable?)");
|
||||
}
|
||||
|
||||
private static String makeSerializedKey(final String input) {
|
||||
final StringBuilder ret = new StringBuilder();
|
||||
|
||||
for (final char c : input.toCharArray()) {
|
||||
if (!Character.isUpperCase(c)) {
|
||||
ret.append(c);
|
||||
continue;
|
||||
}
|
||||
ret.append('-');
|
||||
ret.append(Character.toLowerCase(c));
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
private static record SerializableField(
|
||||
Field field,
|
||||
boolean required,
|
||||
String comment,
|
||||
TypeAdapter<?, ?> adapter,
|
||||
boolean serialize,
|
||||
String serializedKey
|
||||
) {}
|
||||
|
||||
private static SerializableField[] findSerializableFields(final TypeAdapterRegistry registry, Class<?> clazz) throws Exception {
|
||||
final List<SerializableField> ret = new ArrayList<>();
|
||||
do {
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
|
||||
for (final Annotation annotation : field.getAnnotations()) {
|
||||
if (!(annotation instanceof Serializable serializable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final TypeAdapter<?, ?> adapter;
|
||||
|
||||
if (serializable.adapter() != TypeAdapter.class) {
|
||||
adapter = serializable.adapter().getConstructor().newInstance();
|
||||
} else {
|
||||
adapter = findOrMakeAdapter(registry, field.getType());
|
||||
}
|
||||
|
||||
String serializedKey = serializable.serializedKey();
|
||||
if (serializedKey.isEmpty()) {
|
||||
serializedKey = makeSerializedKey(field.getName());
|
||||
}
|
||||
|
||||
ret.add(new SerializableField(
|
||||
field, serializable.required(), serializable.comment(), adapter,
|
||||
serializable.serialize(), serializedKey
|
||||
));
|
||||
}
|
||||
}
|
||||
} while ((clazz = clazz.getSuperclass()) != Object.class);
|
||||
|
||||
ret.sort((final SerializableField c1, final SerializableField c2) -> {
|
||||
return c1.serializedKey.compareTo(c2.serializedKey);
|
||||
});
|
||||
|
||||
return ret.toArray(new SerializableField[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (!(input instanceof Map<?,?> inputMap)) {
|
||||
throw new IllegalArgumentException("Not a map type: " + input.getClass());
|
||||
}
|
||||
|
||||
try {
|
||||
final T ret = this.constructor.newInstance();
|
||||
|
||||
for (final SerializableField field : this.fields) {
|
||||
final Object fieldValue = inputMap.get(field.serializedKey);
|
||||
|
||||
if (fieldValue == null) {
|
||||
if (field.required) {
|
||||
throw new IllegalArgumentException("Missing required field '" + field.serializedKey + "' in " + this.constructor.getDeclaringClass());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
field.field.set(ret, field.adapter.deserialize(registry, fieldValue, field.field.getGenericType()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (final Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> serialize(final TypeAdapterRegistry registry, final T value, final Type type) {
|
||||
final LinkedHashMap<Object, Object> ret = new LinkedHashMap<>();
|
||||
|
||||
for (final SerializableField field : this.fields) {
|
||||
if (!field.serialize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Object fieldValue;
|
||||
try {
|
||||
fieldValue = field.field.get(value);
|
||||
} catch (final Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
if (fieldValue != null) {
|
||||
ret.put(
|
||||
field.comment.isBlank() ? field.serializedKey : new CommentedData(field.comment, field.serializedKey),
|
||||
((TypeAdapter)field.adapter).serialize(
|
||||
registry, fieldValue, field.field.getGenericType()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void callInitialisers(final T value) {
|
||||
for (final SerializableField field : this.fields) {
|
||||
final Object fieldValue;
|
||||
try {
|
||||
fieldValue = field.field.get(value);
|
||||
} catch (final Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
if (fieldValue instanceof InitialiseHook initialiseHook) {
|
||||
initialiseHook.initialise();
|
||||
}
|
||||
|
||||
this.registry.callInitialisers(fieldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CommentedData {
|
||||
|
||||
public final String comment;
|
||||
public final Object data;
|
||||
|
||||
public CommentedData(final String comment, final Object data) {
|
||||
this.comment = comment;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.collection;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.primitive.StringTypeAdapter;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public final class CollectionTypeAdapter extends TypeAdapter<Collection<Object>, List<Object>> {
|
||||
|
||||
public static final CollectionTypeAdapter INSTANCE = new CollectionTypeAdapter();
|
||||
|
||||
@Override
|
||||
public Collection<Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (!(type instanceof ParameterizedType parameterizedType)) {
|
||||
throw new IllegalArgumentException("Collection field must specify generic type");
|
||||
}
|
||||
final Type elementType = parameterizedType.getActualTypeArguments()[0];
|
||||
if (input instanceof Collection<?> collection) {
|
||||
final List<Object> ret = new ArrayList<>(collection.size());
|
||||
|
||||
for (final Object v : collection) {
|
||||
ret.add(registry.deserialize(v, elementType));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
throw new IllegalArgumentException("Not a collection type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> serialize(final TypeAdapterRegistry registry, final Collection<Object> value, final Type type) {
|
||||
final List<Object> ret = new ArrayList<>(value.size());
|
||||
|
||||
final Type elementType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[0] : null;
|
||||
|
||||
for (final Object v : value) {
|
||||
ret.add(registry.serialize(v, elementType == null ? v.getClass() : elementType));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.collection;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public final class ListTypeAdapter extends TypeAdapter<List<Object>, List<Object>> {
|
||||
|
||||
public static final ListTypeAdapter INSTANCE = new ListTypeAdapter();
|
||||
|
||||
@Override
|
||||
public List<Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (!(type instanceof ParameterizedType parameterizedType)) {
|
||||
throw new IllegalArgumentException("Collection field must specify generic type");
|
||||
}
|
||||
final Type elementType = parameterizedType.getActualTypeArguments()[0];
|
||||
if (input instanceof Collection<?> collection) {
|
||||
final List<Object> ret = new ArrayList<>(collection.size());
|
||||
|
||||
for (final Object v : collection) {
|
||||
ret.add(registry.deserialize(v, elementType));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
throw new IllegalArgumentException("Not a collection type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> serialize(final TypeAdapterRegistry registry, final List<Object> value, final Type type) {
|
||||
final List<Object> ret = new ArrayList<>(value.size());
|
||||
|
||||
final Type elementType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[0] : null;
|
||||
|
||||
for (final Object v : value) {
|
||||
ret.add(registry.serialize(v, elementType));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.collection;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public final class SortedMapTypeAdapter extends TypeAdapter<Map<String, Object>, Map<String, Object>> {
|
||||
|
||||
public static final SortedMapTypeAdapter SORTED_CASE_INSENSITIVE = new SortedMapTypeAdapter(String.CASE_INSENSITIVE_ORDER);
|
||||
public static final SortedMapTypeAdapter SORTED_CASE_SENSITIVE = new SortedMapTypeAdapter(null);
|
||||
|
||||
private final Comparator<String> keyComparator;
|
||||
|
||||
public SortedMapTypeAdapter(final Comparator<String> keyComparator) {
|
||||
this.keyComparator = keyComparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (!(type instanceof ParameterizedType parameterizedType)) {
|
||||
throw new IllegalArgumentException("Collection field must specify generic type");
|
||||
}
|
||||
final Type valueType = parameterizedType.getActualTypeArguments()[1];
|
||||
if (input instanceof Map<?,?> inputMap) {
|
||||
final Map<String, Object> castedInput = (Map<String, Object>)inputMap;
|
||||
|
||||
final TreeMap<String, Object> ret = new TreeMap<>(this.keyComparator);
|
||||
|
||||
for (final Map.Entry<String, Object> entry : castedInput.entrySet()) {
|
||||
ret.put(entry.getKey(), registry.deserialize(entry.getValue(), valueType));
|
||||
}
|
||||
|
||||
// transform to linked so that get() is O(1)
|
||||
return new LinkedHashMap<>(ret);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a map type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize(final TypeAdapterRegistry registry, final Map<String, Object> value, final Type type) {
|
||||
final TreeMap<String, Object> ret = new TreeMap<>(this.keyComparator);
|
||||
|
||||
final Type valueType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[1] : null;
|
||||
|
||||
for (final Map.Entry<String, Object> entry : value.entrySet()) {
|
||||
ret.put(entry.getKey(), registry.serialize(entry.getValue(), valueType));
|
||||
}
|
||||
|
||||
// transform to linked so that get() is O(1)
|
||||
return new LinkedHashMap<>(ret);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.collection;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class UnsortedMapTypeAdapter extends TypeAdapter<Map<String, Object>, Map<String, Object>> {
|
||||
|
||||
public static final UnsortedMapTypeAdapter INSTANCE = new UnsortedMapTypeAdapter();
|
||||
|
||||
@Override
|
||||
public Map<String, Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (!(type instanceof ParameterizedType parameterizedType)) {
|
||||
throw new IllegalArgumentException("Collection field must specify generic type");
|
||||
}
|
||||
final Type valueType = parameterizedType.getActualTypeArguments()[1];
|
||||
if (input instanceof Map<?,?> inputMap) {
|
||||
final Map<String, Object> castedInput = (Map<String, Object>)inputMap;
|
||||
|
||||
final LinkedHashMap<String, Object> ret = new LinkedHashMap<>();
|
||||
|
||||
for (final Map.Entry<String, Object> entry : castedInput.entrySet()) {
|
||||
ret.put(entry.getKey(), registry.deserialize(entry.getValue(), valueType));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a map type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> serialize(final TypeAdapterRegistry registry, final Map<String, Object> value, final Type type) {
|
||||
final LinkedHashMap<String, Object> ret = new LinkedHashMap<>();
|
||||
|
||||
final Type valueType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[1] : null;
|
||||
|
||||
for (final Map.Entry<String, Object> entry : value.entrySet()) {
|
||||
ret.put(entry.getKey(), registry.serialize(entry.getValue(), valueType));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public final class BooleanTypeAdapter extends TypeAdapter<Boolean, Boolean> {
|
||||
|
||||
public static final BooleanTypeAdapter INSTANCE = new BooleanTypeAdapter();
|
||||
|
||||
@Override
|
||||
public Boolean deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Boolean ret) {
|
||||
return ret;
|
||||
}
|
||||
if (input instanceof String str) {
|
||||
if (str.equalsIgnoreCase("false")) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
if (str.equalsIgnoreCase("true")) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
throw new IllegalArgumentException("Not a boolean: " + str);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a boolean type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean serialize(final TypeAdapterRegistry registry, final Boolean value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public final class ByteTypeAdapter extends TypeAdapter<Byte, Byte> {
|
||||
|
||||
public static final ByteTypeAdapter INSTANCE = new ByteTypeAdapter();
|
||||
|
||||
private static Byte cast(final Object original, final long value) {
|
||||
if (value < (long)Byte.MIN_VALUE || value > (long)Byte.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Byte value is out of range: " + original.toString());
|
||||
}
|
||||
return Byte.valueOf((byte)value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
// note: silently discard floating point significand
|
||||
return cast(input, number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue());
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return cast(input, (long)Double.parseDouble(string));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a byte type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte serialize(final TypeAdapterRegistry registry, final Byte value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public final class DoubleTypeAdapter extends TypeAdapter<Double, Double> {
|
||||
|
||||
public static final DoubleTypeAdapter INSTANCE = new DoubleTypeAdapter();
|
||||
|
||||
@Override
|
||||
public Double deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
return Double.valueOf(number.doubleValue());
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return Double.valueOf(Double.parseDouble(string));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a byte type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double serialize(final TypeAdapterRegistry registry, final Double value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public final class FloatTypeAdapter extends TypeAdapter<Float, Float> {
|
||||
|
||||
public static final FloatTypeAdapter INSTANCE = new FloatTypeAdapter();
|
||||
|
||||
private static Float cast(final Object original, final double value) {
|
||||
if (value < -(double)Float.MAX_VALUE || value > (double)Float.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Byte value is out of range: " + original.toString());
|
||||
}
|
||||
// note: silently ignore precision loss
|
||||
return Float.valueOf((float)value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
return cast(input, number.doubleValue());
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return cast(input, Double.parseDouble(string));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a byte type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float serialize(final TypeAdapterRegistry registry, final Float value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public final class IntegerTypeAdapter extends TypeAdapter<Integer, Integer> {
|
||||
|
||||
public static final IntegerTypeAdapter INSTANCE = new IntegerTypeAdapter();
|
||||
|
||||
private static Integer cast(final Object original, final long value) {
|
||||
if (value < (long)Integer.MIN_VALUE || value > (long)Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Integer value is out of range: " + original.toString());
|
||||
}
|
||||
return Integer.valueOf((int)value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
// note: silently discard floating point significand
|
||||
return cast(input, number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue());
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return cast(input, (long)Double.parseDouble(string));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not an integer type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer serialize(final TypeAdapterRegistry registry, final Integer value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public final class LongTypeAdapter extends TypeAdapter<Long, Long> {
|
||||
|
||||
public static final LongTypeAdapter INSTANCE = new LongTypeAdapter();
|
||||
|
||||
@Override
|
||||
public Long deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
// note: silently discard floating point significand
|
||||
return number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue();
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
try {
|
||||
return Long.valueOf(Long.parseLong(string));
|
||||
} catch (final NumberFormatException ex) {
|
||||
return Long.valueOf((long)Double.parseDouble(string));
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a long type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long serialize(final TypeAdapterRegistry registry, final Long value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public final class ShortTypeAdapter extends TypeAdapter<Short, Short> {
|
||||
|
||||
public static final ShortTypeAdapter INSTANCE = new ShortTypeAdapter();
|
||||
|
||||
private static Short cast(final Object original, final long value) {
|
||||
if (value < (long)Short.MIN_VALUE || value > (long)Short.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Short value is out of range: " + original.toString());
|
||||
}
|
||||
return Short.valueOf((short)value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
// note: silently discard floating point significand
|
||||
return cast(input, number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue());
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return cast(input, (long)Double.parseDouble(string));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not a short type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short serialize(final TypeAdapterRegistry registry, final Short value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public final class StringTypeAdapter extends TypeAdapter<String, String> {
|
||||
|
||||
public static final StringTypeAdapter INSTANCE = new StringTypeAdapter();
|
||||
|
||||
@Override
|
||||
public String deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Boolean bool) {
|
||||
return String.valueOf(bool.booleanValue());
|
||||
}
|
||||
if (input instanceof Number number) {
|
||||
return number.toString();
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return string;
|
||||
}
|
||||
throw new IllegalArgumentException("Not a string type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize(final TypeAdapterRegistry registry, final String value, final Type type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.type;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public final class BigDecimalTypeAdapter extends TypeAdapter<BigDecimal, String> {
|
||||
|
||||
public static final BigDecimalTypeAdapter INSTANCE = new BigDecimalTypeAdapter();
|
||||
|
||||
@Override
|
||||
public BigDecimal deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
// safest to catch all number impls is to use toString
|
||||
return new BigDecimal(number.toString());
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return new BigDecimal(string);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not an BigDecimal type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize(final TypeAdapterRegistry registry, final BigDecimal value, final Type type) {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.type;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public final class BigIntegerTypeAdapter extends TypeAdapter<BigInteger, String> {
|
||||
|
||||
public static final BigIntegerTypeAdapter INSTANCE = new BigIntegerTypeAdapter();
|
||||
|
||||
@Override
|
||||
public BigInteger deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof Number number) {
|
||||
if (number instanceof BigInteger bigInteger) {
|
||||
return bigInteger;
|
||||
}
|
||||
// note: silently discard floating point significand
|
||||
if (number instanceof BigDecimal bigDecimal) {
|
||||
return bigDecimal.toBigInteger();
|
||||
}
|
||||
|
||||
return BigInteger.valueOf(number.longValue());
|
||||
}
|
||||
if (input instanceof String string) {
|
||||
return new BigDecimal(string).toBigInteger();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not an BigInteger type: " + input.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize(final TypeAdapterRegistry registry, final BigInteger value, final Type type) {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.adapter.type;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import ca.spottedleaf.moonrise.common.config.type.Duration;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public final class DurationTypeAdapter extends TypeAdapter<Duration, String> {
|
||||
|
||||
public static final DurationTypeAdapter INSTANCE = new DurationTypeAdapter();
|
||||
|
||||
@Override
|
||||
public Duration deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (!(input instanceof String string)) {
|
||||
throw new IllegalArgumentException("Not a string: " + input.getClass());
|
||||
}
|
||||
return Duration.parse(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize(final TypeAdapterRegistry registry, final Duration value, final Type type) {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation used on a class to indicate that its type adapter may automatically be generated. The class must have
|
||||
* a public no-args constructor.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Adaptable {
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.annotation;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a field should be deserialized or serialized from the config.
|
||||
* By default, this annotation is not assumed.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Serializable {
|
||||
|
||||
/**
|
||||
* Indicates whether this field is required to be present in the config. If the field is not present,
|
||||
* and {@code required = true}, then an exception will be thrown during deserialization. If {@code required = false}
|
||||
* and the field is not present, then the field value will remain unmodified.
|
||||
*/
|
||||
public boolean required() default false;
|
||||
|
||||
/**
|
||||
* The comment to apply before the element when serializing.
|
||||
*/
|
||||
public String comment() default "";
|
||||
|
||||
/**
|
||||
* Adapter override class. The class must have a public no-args constructor.
|
||||
*/
|
||||
public Class<? extends TypeAdapter> adapter() default TypeAdapter.class;
|
||||
|
||||
/**
|
||||
* Whether to serialize the value to the config.
|
||||
*/
|
||||
public boolean serialize() default true;
|
||||
|
||||
/**
|
||||
* When not empty, this value overrides the auto generated serialized key in the config.
|
||||
*/
|
||||
public String serializedKey() default "";
|
||||
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.config;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.comments.CommentLine;
|
||||
import org.yaml.snakeyaml.comments.CommentType;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.representer.Represent;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public final class YamlConfig<T> {
|
||||
|
||||
public final TypeAdapterRegistry typeAdapters;
|
||||
|
||||
private final Class<? extends T> clazz;
|
||||
|
||||
public volatile T config;
|
||||
|
||||
private final Yaml yaml;
|
||||
private final LoaderOptions loaderOptions;
|
||||
private final DumperOptions dumperOptions;
|
||||
|
||||
public YamlConfig(final Class<? extends T> clazz, final T dfl) throws Exception {
|
||||
this(clazz, dfl, new TypeAdapterRegistry());
|
||||
}
|
||||
|
||||
public YamlConfig(final Class<? extends T> clazz, final T dfl, final TypeAdapterRegistry registry) throws Exception {
|
||||
this.clazz = clazz;
|
||||
this.config = dfl;
|
||||
this.typeAdapters = registry;
|
||||
this.typeAdapters.makeAdapter(clazz);
|
||||
|
||||
final LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setProcessComments(true);
|
||||
|
||||
final DumperOptions dumperOptions = new DumperOptions();
|
||||
dumperOptions.setProcessComments(true);
|
||||
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
|
||||
this.loaderOptions = loaderOptions;
|
||||
this.dumperOptions = dumperOptions;
|
||||
this.yaml = new Yaml(new YamlConstructor(loaderOptions), new YamlRepresenter(dumperOptions), dumperOptions, loaderOptions);
|
||||
}
|
||||
|
||||
public void load(final File file) throws IOException {
|
||||
try (final InputStream is = new BufferedInputStream(new FileInputStream(file))) {
|
||||
this.load(is);
|
||||
}
|
||||
}
|
||||
|
||||
public void load(final InputStream is) throws IOException {
|
||||
final Object serialized = this.yaml.load(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||
|
||||
this.config = (T)this.typeAdapters.deserialize(serialized, this.clazz);
|
||||
}
|
||||
|
||||
public void save(final File file) throws IOException {
|
||||
this.save(file, "");
|
||||
}
|
||||
|
||||
public void save(final File file, final String header) throws IOException {
|
||||
if (file.isDirectory()) {
|
||||
throw new IOException("File is a directory");
|
||||
}
|
||||
|
||||
final File parent = file.getParentFile();
|
||||
if (parent != null) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
final File tmp = new File(parent, file.getName() + ".tmp");
|
||||
tmp.delete();
|
||||
tmp.createNewFile();
|
||||
try {
|
||||
try (final OutputStream os = new BufferedOutputStream(new FileOutputStream(tmp))) {
|
||||
this.save(os, header);
|
||||
}
|
||||
|
||||
try {
|
||||
Files.move(tmp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (final AtomicMoveNotSupportedException ex) {
|
||||
Files.move(tmp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
} finally {
|
||||
tmp.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public void save(final OutputStream os) throws IOException {
|
||||
os.write(this.saveToString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public void save(final OutputStream os, final String header) throws IOException {
|
||||
os.write(this.saveToString(header).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public String saveToString() {
|
||||
return this.yaml.dump(this.typeAdapters.serialize(this.config, this.clazz));
|
||||
}
|
||||
|
||||
public String saveToString(final String header) {
|
||||
if (header.isBlank()) {
|
||||
return this.saveToString();
|
||||
}
|
||||
|
||||
final StringBuilder ret = new StringBuilder();
|
||||
final String lineBreak = this.dumperOptions.getLineBreak().getString();
|
||||
|
||||
for (final String line : header.split("\n")) {
|
||||
ret.append("# ").append(line.trim()).append(lineBreak);
|
||||
}
|
||||
|
||||
ret.append(lineBreak);
|
||||
|
||||
return ret.append(this.saveToString()).toString();
|
||||
}
|
||||
|
||||
public void callInitialisers() {
|
||||
this.typeAdapters.callInitialisers(this.config);
|
||||
}
|
||||
|
||||
private static final class YamlConstructor extends Constructor {
|
||||
|
||||
public YamlConstructor(final LoaderOptions loadingConfig) {
|
||||
super(loadingConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class YamlRepresenter extends Representer {
|
||||
|
||||
public YamlRepresenter(final DumperOptions options) {
|
||||
super(options);
|
||||
|
||||
this.representers.put(TypeAdapterRegistry.CommentedData.class, new CommentedDataRepresenter());
|
||||
}
|
||||
|
||||
private final class CommentedDataRepresenter implements Represent {
|
||||
|
||||
@Override
|
||||
public Node representData(final Object data0) {
|
||||
final TypeAdapterRegistry.CommentedData commentedData = (TypeAdapterRegistry.CommentedData)data0;
|
||||
|
||||
final Node node = YamlRepresenter.this.representData(commentedData.data);
|
||||
|
||||
final List<CommentLine> comments = new ArrayList<>();
|
||||
|
||||
for (final String line : commentedData.comment.split("\n")) {
|
||||
comments.add(new CommentLine(null, null, " ".concat(line.trim()), CommentType.BLOCK));
|
||||
}
|
||||
|
||||
node.setBlockComments(comments);
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package ca.spottedleaf.moonrise.common.config.moonrise;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.InitialiseHook;
|
||||
import ca.spottedleaf.moonrise.common.config.annotation.Adaptable;
|
||||
import ca.spottedleaf.moonrise.common.config.ui.ClothConfig;
|
||||
import ca.spottedleaf.moonrise.common.config.annotation.Serializable;
|
||||
import ca.spottedleaf.moonrise.common.config.type.Duration;
|
||||
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
||||
import ca.spottedleaf.yamlconfig.InitialiseHook;
|
||||
import ca.spottedleaf.yamlconfig.annotation.Adaptable;
|
||||
import ca.spottedleaf.yamlconfig.annotation.Serializable;
|
||||
import ca.spottedleaf.yamlconfig.type.Duration;
|
||||
|
||||
@Adaptable
|
||||
public final class MoonriseConfig {
|
||||
@@ -251,4 +251,21 @@ public final class MoonriseConfig {
|
||||
)
|
||||
public boolean fixMC159283 = false;
|
||||
}
|
||||
|
||||
@Serializable
|
||||
public Misc misc = new Misc();
|
||||
|
||||
@Adaptable
|
||||
public static final class Misc {
|
||||
|
||||
@Serializable(
|
||||
serializedKey = "immediately-close-loading-screen",
|
||||
comment = """
|
||||
Whether the loading screen should be closed immediately when joining servers/SP worlds.
|
||||
This will let you in game faster, but may result in getting in game before enough chunks are
|
||||
loaded for rendering.
|
||||
"""
|
||||
)
|
||||
public boolean immediatelyCloseLoadingScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.moonrise.adapter;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import ca.spottedleaf.moonrise.common.config.moonrise.type.DefaultedValue;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public final class DefaultedTypeAdapter extends TypeAdapter<DefaultedValue<?>, Object> {
|
||||
|
||||
private static final String DEFAULT_STRING = "default";
|
||||
|
||||
@Override
|
||||
public DefaultedValue<?> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
|
||||
if (input instanceof String string && string.equalsIgnoreCase(DEFAULT_STRING)) {
|
||||
return new DefaultedValue<>();
|
||||
}
|
||||
|
||||
if (!(type instanceof ParameterizedType parameterizedType)) {
|
||||
throw new IllegalArgumentException("DefaultedValue field must specify generic type");
|
||||
}
|
||||
final Type valueType = parameterizedType.getActualTypeArguments()[0];
|
||||
|
||||
return new DefaultedValue<>(registry.deserialize(input, valueType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object serialize(final TypeAdapterRegistry registry, final DefaultedValue<?> value, final Type type) {
|
||||
final Object raw = value.getValueRaw();
|
||||
if (raw == null) {
|
||||
return DEFAULT_STRING;
|
||||
}
|
||||
|
||||
final Type valueType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[0] : null;
|
||||
|
||||
return registry.serialize(raw, valueType);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.moonrise.type;
|
||||
|
||||
public final class DefaultedValue<T> {
|
||||
|
||||
private final T value;
|
||||
|
||||
public DefaultedValue() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public DefaultedValue(final T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public T getValueRaw() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public T getOrDefault(final T dfl) {
|
||||
return this.value != null ? this.value : dfl;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.config.type;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public final class Duration {
|
||||
|
||||
private final String string;
|
||||
private final long timeNS;
|
||||
|
||||
private Duration(final String string, final long timeNS) {
|
||||
this.string = string;
|
||||
this.timeNS = timeNS;
|
||||
}
|
||||
|
||||
public static Duration parse(final String value) {
|
||||
if (value.length() < 2) {
|
||||
throw new IllegalArgumentException("Invalid duration: " + value);
|
||||
}
|
||||
|
||||
final char last = value.charAt(value.length() - 1);
|
||||
|
||||
final long multiplier;
|
||||
|
||||
switch (last) {
|
||||
case 's': {
|
||||
multiplier = (1000L * 1000L * 1000L) * 1L;
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
multiplier = (1000L * 1000L * 1000L) / 20L;
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
multiplier = (1000L * 1000L * 1000L) * 60L;
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
multiplier = (1000L * 1000L * 1000L) * 60L * 60L;
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
multiplier = (1000L * 1000L * 1000L) * 24L * 60L * 60L;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Duration must end with one of: [s, t, m, h, d]");
|
||||
}
|
||||
}
|
||||
|
||||
final BigDecimal parsed = new BigDecimal(value.substring(0, value.length() - 1))
|
||||
.multiply(new BigDecimal(multiplier));
|
||||
|
||||
return new Duration(value, parsed.toBigInteger().longValueExact());
|
||||
}
|
||||
|
||||
public long getTimeNS() {
|
||||
return this.timeNS;
|
||||
}
|
||||
|
||||
public long getTimeMS() {
|
||||
return this.timeNS / (1000L * 1000L);
|
||||
}
|
||||
|
||||
public long getTimeS() {
|
||||
return this.timeNS / (1000L * 1000L * 1000L);
|
||||
}
|
||||
|
||||
public long getTimeTicks() {
|
||||
return this.timeNS / ((1000L * 1000L * 1000L) / (20L));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.string;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package ca.spottedleaf.moonrise.common.misc;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
|
||||
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
|
||||
@@ -121,8 +121,8 @@ public final class NearbyPlayers {
|
||||
players[NearbyMapType.GENERAL.ordinal()].update(chunk.x, chunk.z, GENERAL_AREA_VIEW_DISTANCE);
|
||||
players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_SMALL_VIEW_DISTANCE);
|
||||
players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_REALLY_SMALL_VIEW_DISTANCE);
|
||||
players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getTickViewDistance(player));
|
||||
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getLoadViewDistance(player));
|
||||
players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getTickViewDistance(player));
|
||||
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getViewDistance(player));
|
||||
players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.common.misc;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.util.IntPairUtil;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
||||
|
||||
@@ -14,6 +15,10 @@ public final class PositionCountingAreaMap<T> {
|
||||
return this.counters.keySet();
|
||||
}
|
||||
|
||||
public LongSet getPositions() {
|
||||
return this.positions.keySet();
|
||||
}
|
||||
|
||||
public int getTotalPositions() {
|
||||
return this.positions.size();
|
||||
}
|
||||
|
||||
@@ -7,170 +7,192 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevel
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import ca.spottledleaf.moonrise.compat.lithium.LithiumHooks;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import org.slf4j.Logger;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ChunkSystem {
|
||||
public abstract class BaseChunkSystemHooks implements ChunkSystemHooks {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final boolean hasLithium = ((PlatformHooks)this).isModLoaded("lithium");
|
||||
|
||||
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
|
||||
@Override
|
||||
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
|
||||
scheduleChunkTask(level, chunkX, chunkZ, run, Priority.NORMAL);
|
||||
}
|
||||
|
||||
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority) {
|
||||
@Override
|
||||
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority) {
|
||||
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkTask(chunkX, chunkZ, run, priority);
|
||||
}
|
||||
|
||||
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
|
||||
@Override
|
||||
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
|
||||
final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
|
||||
final Consumer<ChunkAccess> onComplete) {
|
||||
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(chunkX, chunkZ, gen, toStatus, addTicket, priority, onComplete);
|
||||
}
|
||||
|
||||
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
|
||||
@Override
|
||||
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
|
||||
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
|
||||
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
|
||||
}
|
||||
|
||||
public static void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
|
||||
@Override
|
||||
public void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
|
||||
final FullChunkStatus toStatus, final boolean addTicket,
|
||||
final Priority priority, final Consumer<LevelChunk> onComplete) {
|
||||
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleTickingState(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
|
||||
}
|
||||
|
||||
public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
|
||||
@Override
|
||||
public List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
|
||||
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.getOldChunkHolders();
|
||||
}
|
||||
|
||||
public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
|
||||
@Override
|
||||
public List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
|
||||
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.getOldChunkHolders();
|
||||
}
|
||||
|
||||
public static int getVisibleChunkHolderCount(final ServerLevel level) {
|
||||
@Override
|
||||
public int getVisibleChunkHolderCount(final ServerLevel level) {
|
||||
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.size();
|
||||
}
|
||||
|
||||
public static int getUpdatingChunkHolderCount(final ServerLevel level) {
|
||||
@Override
|
||||
public int getUpdatingChunkHolderCount(final ServerLevel level) {
|
||||
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.size();
|
||||
}
|
||||
|
||||
public static boolean hasAnyChunkHolders(final ServerLevel level) {
|
||||
@Override
|
||||
public boolean hasAnyChunkHolders(final ServerLevel level) {
|
||||
return getUpdatingChunkHolderCount(level) != 0;
|
||||
}
|
||||
|
||||
public static boolean screenEntity(final ServerLevel level, final Entity entity, final boolean fromDisk, final boolean event) {
|
||||
if (!PlatformHooks.get().screenEntity(level, entity, fromDisk, event)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
|
||||
|
||||
}
|
||||
|
||||
public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
|
||||
// Update progress listener for LevelLoadingScreen
|
||||
final ChunkProgressListener progressListener = level.getChunkSource().chunkMap.progressListener;
|
||||
if (progressListener != null) {
|
||||
ChunkSystem.scheduleChunkTask(level, holder.getPos().x, holder.getPos().z, () -> {
|
||||
this.scheduleChunkTask(level, holder.getPos().x, holder.getPos().z, () -> {
|
||||
progressListener.onStatusChange(holder.getPos(), null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
|
||||
.moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, chunk);
|
||||
}
|
||||
|
||||
public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().add(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
if (this.hasLithium) {
|
||||
LithiumHooks.onChunkAccessible((ServerLevel) chunk.getLevel(), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().remove(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
if (this.hasLithium) {
|
||||
LithiumHooks.onChunkInaccessible((ServerLevel) chunk.getLevel(), chunk.getPos());
|
||||
}
|
||||
}
|
||||
|
||||
public static void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
|
||||
.moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, null);
|
||||
}
|
||||
|
||||
public static void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().add(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
||||
chunk.postProcessGeneration();
|
||||
chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
|
||||
}
|
||||
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
|
||||
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
|
||||
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$markChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$removeChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().add(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
|
||||
public static void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
@Override
|
||||
public void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().remove(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
|
||||
public static ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
|
||||
@Override
|
||||
public ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getSendViewDistance(final ServerPlayer player) {
|
||||
@Override
|
||||
public int getSendViewDistance(final ServerPlayer player) {
|
||||
return RegionizedPlayerChunkLoader.getAPISendViewDistance(player);
|
||||
}
|
||||
|
||||
public static int getLoadViewDistance(final ServerPlayer player) {
|
||||
return RegionizedPlayerChunkLoader.getLoadViewDistance(player);
|
||||
@Override
|
||||
public int getViewDistance(final ServerPlayer player) {
|
||||
return RegionizedPlayerChunkLoader.getAPIViewDistance(player);
|
||||
}
|
||||
|
||||
public static int getTickViewDistance(final ServerPlayer player) {
|
||||
@Override
|
||||
public int getTickViewDistance(final ServerPlayer player) {
|
||||
return RegionizedPlayerChunkLoader.getAPITickViewDistance(player);
|
||||
}
|
||||
|
||||
public static void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player) {
|
||||
@Override
|
||||
public void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player) {
|
||||
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().addPlayer(player);
|
||||
}
|
||||
|
||||
public static void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player) {
|
||||
@Override
|
||||
public void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player) {
|
||||
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().removePlayer(player);
|
||||
}
|
||||
|
||||
public static void updateMaps(final ServerLevel world, final ServerPlayer player) {
|
||||
@Override
|
||||
public void updateMaps(final ServerLevel world, final ServerPlayer player) {
|
||||
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().updatePlayer(player);
|
||||
}
|
||||
|
||||
private ChunkSystem() {}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface ChunkSystemHooks {
|
||||
|
||||
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run);
|
||||
|
||||
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority);
|
||||
|
||||
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
|
||||
final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
|
||||
final Consumer<ChunkAccess> onComplete);
|
||||
|
||||
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
|
||||
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete);
|
||||
|
||||
public void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
|
||||
final FullChunkStatus toStatus, final boolean addTicket,
|
||||
final Priority priority, final Consumer<LevelChunk> onComplete);
|
||||
|
||||
public List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level);
|
||||
|
||||
public List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level);
|
||||
|
||||
public int getVisibleChunkHolderCount(final ServerLevel level);
|
||||
|
||||
public int getUpdatingChunkHolderCount(final ServerLevel level);
|
||||
|
||||
public boolean hasAnyChunkHolders(final ServerLevel level);
|
||||
|
||||
public boolean screenEntity(final ServerLevel level, final Entity entity, final boolean fromDisk, final boolean event);
|
||||
|
||||
public void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder);
|
||||
|
||||
public void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder);
|
||||
|
||||
public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder);
|
||||
|
||||
public ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ);
|
||||
|
||||
public int getSendViewDistance(final ServerPlayer player);
|
||||
|
||||
public int getViewDistance(final ServerPlayer player);
|
||||
|
||||
public int getTickViewDistance(final ServerPlayer player);
|
||||
|
||||
public void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player);
|
||||
|
||||
public void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player);
|
||||
|
||||
public void updateMaps(final ServerLevel world, final ServerPlayer player);
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import ca.spottedleaf.moonrise.common.config.config.YamlConfig;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
|
||||
import ca.spottedleaf.moonrise.common.config.moonrise.adapter.DefaultedTypeAdapter;
|
||||
import ca.spottedleaf.moonrise.common.config.moonrise.type.DefaultedValue;
|
||||
import ca.spottedleaf.yamlconfig.adapter.TypeAdapterRegistry;
|
||||
import ca.spottedleaf.yamlconfig.config.YamlConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.io.File;
|
||||
@@ -13,19 +12,17 @@ public final class ConfigHolder {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigHolder.class);
|
||||
|
||||
private static final File CONFIG_FILE = new File(System.getProperty("Moonrise.ConfigFile", "config/moonrise.yml"));
|
||||
private static final File CONFIG_FILE = new File(System.getProperty(PlatformHooks.get().getBrand() + ".ConfigFile", "config/moonrise.yml"));
|
||||
private static final TypeAdapterRegistry CONFIG_ADAPTERS = new TypeAdapterRegistry();
|
||||
private static final YamlConfig<MoonriseConfig> CONFIG;
|
||||
static {
|
||||
CONFIG_ADAPTERS.putAdapter(DefaultedValue.class, new DefaultedTypeAdapter());
|
||||
|
||||
try {
|
||||
CONFIG = new YamlConfig<>(MoonriseConfig.class, new MoonriseConfig(), CONFIG_ADAPTERS);
|
||||
} catch (final Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
private static final String CONFIG_HEADER = """
|
||||
private static final String CONFIG_HEADER = String.format("""
|
||||
This is the configuration file for Moonrise.
|
||||
|
||||
Each configuration option is prefixed with a comment to explain what it does. Additional changes to this file
|
||||
@@ -33,10 +30,10 @@ public final class ConfigHolder {
|
||||
|
||||
Below are the Moonrise startup flags. Note that startup flags must be placed in the JVM arguments, not
|
||||
program arguments.
|
||||
-DMoonrise.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
|
||||
-DMoonrise.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
|
||||
-DMoonrise.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
|
||||
""";
|
||||
-D%1$s.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
|
||||
-D%1$s.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
|
||||
-D%1$s.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
|
||||
""", PlatformHooks.get().getBrand());
|
||||
|
||||
static {
|
||||
reloadConfig();
|
||||
@@ -51,14 +48,6 @@ public final class ConfigHolder {
|
||||
}
|
||||
|
||||
public static boolean reloadConfig() {
|
||||
final boolean ret = reloadConfig0();
|
||||
|
||||
CONFIG.callInitialisers();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean reloadConfig0() {
|
||||
synchronized (CONFIG) {
|
||||
if (CONFIG_FILE.exists()) {
|
||||
try {
|
||||
@@ -69,6 +58,8 @@ public final class ConfigHolder {
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG.callInitialisers();
|
||||
|
||||
// write back any changes, or create if needed
|
||||
return saveConfig();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.Strictness;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.File;
|
||||
@@ -16,7 +17,7 @@ public final class JsonUtil {
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
final JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||
jsonWriter.setIndent(" ");
|
||||
jsonWriter.setLenient(false);
|
||||
jsonWriter.setStrictness(Strictness.LENIENT);
|
||||
Streams.write(element, jsonWriter);
|
||||
|
||||
final String jsonString = stringWriter.toString();
|
||||
|
||||
@@ -3,8 +3,12 @@ package ca.spottedleaf.moonrise.common.util;
|
||||
public final class MixinWorkarounds {
|
||||
|
||||
// mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
|
||||
// https://github.com/FabricMC/Mixin/pull/147
|
||||
public static long[] clone(final long[] values) {
|
||||
return values.clone();
|
||||
}
|
||||
|
||||
public static byte[] clone(final byte[] values) {
|
||||
return values.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public final class MoonriseCommon {
|
||||
@Override
|
||||
public void accept(Thread thread) {
|
||||
thread.setDaemon(true);
|
||||
thread.setName("Moonrise Common Worker #" + this.idGenerator.getAndIncrement());
|
||||
thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
|
||||
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(final Thread thread, final Throwable throwable) {
|
||||
@@ -44,7 +44,7 @@ public final class MoonriseCommon {
|
||||
} else {
|
||||
defaultWorkerThreads = defaultWorkerThreads / 2;
|
||||
}
|
||||
defaultWorkerThreads = Integer.getInteger("Moonrise.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||
|
||||
int workerThreads = configWorkerThreads;
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
|
||||
public final class MoonriseConstants {
|
||||
|
||||
public static final int MAX_VIEW_DISTANCE = Integer.getInteger("Moonrise.MaxViewDistance", 32);
|
||||
public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
|
||||
|
||||
private MoonriseConstants() {}
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
||||
|
||||
/**
|
||||
* Avoid costly CAS of superclass
|
||||
*/
|
||||
public final class SimpleRandom extends LegacyRandomSource {
|
||||
|
||||
private static final long MULTIPLIER = 25214903917L;
|
||||
private static final long ADDEND = 11L;
|
||||
private static final int BITS = 48;
|
||||
private static final long MASK = (1L << BITS) - 1;
|
||||
|
||||
private long value;
|
||||
|
||||
public SimpleRandom(final long seed) {
|
||||
super(0L);
|
||||
this.value = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(final long seed) {
|
||||
this.value = (seed ^ MULTIPLIER) & MASK;
|
||||
}
|
||||
|
||||
private long advanceSeed() {
|
||||
return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int next(final int bits) {
|
||||
return (int)(this.advanceSeed() >>> (BITS - bits));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
final long seed = this.advanceSeed();
|
||||
return (int)(seed >>> (BITS - Integer.SIZE));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int nextInt(final int bound) {
|
||||
if (bound <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
|
||||
final long value = this.advanceSeed() >>> (BITS - Integer.SIZE);
|
||||
return (int)((value * (long)bound) >>> Integer.SIZE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.levelgen.BitRandomSource;
|
||||
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian;
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
||||
|
||||
/**
|
||||
* Avoid costly CAS of superclass + division in nextInt
|
||||
*/
|
||||
public final class SimpleThreadUnsafeRandom implements BitRandomSource {
|
||||
|
||||
private static final long MULTIPLIER = 25214903917L;
|
||||
private static final long ADDEND = 11L;
|
||||
private static final int BITS = 48;
|
||||
private static final long MASK = (1L << BITS) - 1L;
|
||||
|
||||
private long value;
|
||||
private final MarsagliaPolarGaussian gaussianSource = new MarsagliaPolarGaussian(this);
|
||||
|
||||
public SimpleThreadUnsafeRandom(final long seed) {
|
||||
this.setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(final long seed) {
|
||||
this.value = (seed ^ MULTIPLIER) & MASK;
|
||||
this.gaussianSource.reset();
|
||||
}
|
||||
|
||||
private long advanceSeed() {
|
||||
return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int next(final int bits) {
|
||||
return (int)(this.advanceSeed() >>> (BITS - bits));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
final long seed = this.advanceSeed();
|
||||
return (int)(seed >>> (BITS - Integer.SIZE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt(final int bound) {
|
||||
if (bound <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
|
||||
final long value = this.advanceSeed() >>> (BITS - Integer.SIZE);
|
||||
return (int)((value * (long)bound) >>> Integer.SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextGaussian() {
|
||||
return this.gaussianSource.nextGaussian();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource fork() {
|
||||
return new SimpleThreadUnsafeRandom(this.nextLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PositionalRandomFactory forkPositional() {
|
||||
return new SimpleRandomPositionalFactory(this.nextLong());
|
||||
}
|
||||
|
||||
public static final class SimpleRandomPositionalFactory implements PositionalRandomFactory {
|
||||
|
||||
private final long seed;
|
||||
|
||||
public SimpleRandomPositionalFactory(final long seed) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
public long getSeed() {
|
||||
return this.seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource fromHashOf(final String string) {
|
||||
return new SimpleThreadUnsafeRandom((long)string.hashCode() ^ this.seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource fromSeed(final long seed) {
|
||||
return new SimpleThreadUnsafeRandom(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource at(final int x, final int y, final int z) {
|
||||
return new SimpleThreadUnsafeRandom(Mth.getSeed(x, y, z) ^ this.seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parityConfigString(final StringBuilder stringBuilder) {
|
||||
stringBuilder.append("SimpleRandomPositionalFactory{").append(this.seed).append('}');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.levelgen.BitRandomSource;
|
||||
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian;
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
||||
|
||||
/**
|
||||
* Avoid costly CAS of superclass
|
||||
*/
|
||||
public final class ThreadUnsafeRandom implements BitRandomSource {
|
||||
|
||||
private static final long MULTIPLIER = 25214903917L;
|
||||
private static final long ADDEND = 11L;
|
||||
private static final int BITS = 48;
|
||||
private static final long MASK = (1L << BITS) - 1L;
|
||||
|
||||
private long value;
|
||||
private final MarsagliaPolarGaussian gaussianSource = new MarsagliaPolarGaussian(this);
|
||||
|
||||
public ThreadUnsafeRandom(final long seed) {
|
||||
this.setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(final long seed) {
|
||||
this.value = (seed ^ MULTIPLIER) & MASK;
|
||||
this.gaussianSource.reset();
|
||||
}
|
||||
|
||||
private long advanceSeed() {
|
||||
return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int next(final int bits) {
|
||||
return (int)(this.advanceSeed() >>> (BITS - bits));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
final long seed = this.advanceSeed();
|
||||
return (int)(seed >>> (BITS - Integer.SIZE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextGaussian() {
|
||||
return this.gaussianSource.nextGaussian();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource fork() {
|
||||
return new ThreadUnsafeRandom(this.nextLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PositionalRandomFactory forkPositional() {
|
||||
return new ThreadUnsafeRandomPositionalFactory(this.nextLong());
|
||||
}
|
||||
|
||||
public static final class ThreadUnsafeRandomPositionalFactory implements PositionalRandomFactory {
|
||||
|
||||
private final long seed;
|
||||
|
||||
public ThreadUnsafeRandomPositionalFactory(final long seed) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
public long getSeed() {
|
||||
return this.seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource fromHashOf(final String string) {
|
||||
return new ThreadUnsafeRandom((long)string.hashCode() ^ this.seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource fromSeed(final long seed) {
|
||||
return new ThreadUnsafeRandom(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomSource at(final int x, final int y, final int z) {
|
||||
return new ThreadUnsafeRandom(Mth.getSeed(x, y, z) ^ this.seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parityConfigString(final StringBuilder stringBuilder) {
|
||||
stringBuilder.append("ThreadUnsafeRandomPositionalFactory{").append(this.seed).append('}');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,43 +28,64 @@ public class TickThread extends Thread {
|
||||
|
||||
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
|
||||
if (!isTickThreadFor(world, pos)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
||||
reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
|
||||
if (!isTickThreadFor(world, pos, blockRadius)) {
|
||||
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
||||
reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
|
||||
if (!isTickThreadFor(world, pos)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
||||
reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
|
||||
if (!isTickThreadFor(world, chunkX, chunkZ)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
||||
reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Entity entity, final String reason) {
|
||||
if (!isTickThreadFor(entity)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
||||
reason + ", entity=" + entity;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
|
||||
if (!isTickThreadFor(world, aabb)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
||||
reason + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
|
||||
if (!isTickThreadFor(world, blockX, blockZ)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
||||
reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +126,10 @@ public class TickThread extends Thread {
|
||||
return isTickThread();
|
||||
}
|
||||
|
||||
public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
|
||||
return isTickThread();
|
||||
}
|
||||
|
||||
public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
|
||||
return isTickThread();
|
||||
}
|
||||
|
||||
@@ -8,19 +8,19 @@ public final class WorldUtil {
|
||||
// min, max are inclusive
|
||||
|
||||
public static int getMaxSection(final LevelHeightAccessor world) {
|
||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
||||
return world.getMaxSectionY();
|
||||
}
|
||||
|
||||
public static int getMaxSection(final Level world) {
|
||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
||||
return world.getMaxSectionY();
|
||||
}
|
||||
|
||||
public static int getMinSection(final LevelHeightAccessor world) {
|
||||
return world.getMinSection();
|
||||
return world.getMinSectionY();
|
||||
}
|
||||
|
||||
public static int getMinSection(final Level world) {
|
||||
return world.getMinSection();
|
||||
return world.getMinSectionY();
|
||||
}
|
||||
|
||||
public static int getMaxLightSection(final LevelHeightAccessor world) {
|
||||
|
||||
@@ -154,9 +154,9 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
|
||||
if (this.maybeHas((final BlockState state) -> !state.isAir())) {
|
||||
final PalettedContainer.Data<BlockState> data = this.states.data;
|
||||
final Palette<BlockState> palette = data.palette;
|
||||
final Palette<BlockState> palette = data.palette();
|
||||
final int paletteSize = palette.getSize();
|
||||
final BitStorage storage = data.storage;
|
||||
final BitStorage storage = data.storage();
|
||||
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> counts;
|
||||
if (paletteSize == 1) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -17,6 +18,9 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
|
||||
super(string, class_);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static final Boolean[] BY_ID = new Boolean[]{ Boolean.FALSE, Boolean.TRUE };
|
||||
|
||||
@Override
|
||||
public final int moonrise$getIdFor(final Boolean value) {
|
||||
return value.booleanValue() ? 1 : 0;
|
||||
@@ -33,23 +37,6 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
this.moonrise$setById(new Boolean[]{ Boolean.FALSE, Boolean.TRUE });
|
||||
}
|
||||
|
||||
/**
|
||||
* This skips all ops after the identity comparison in the original code.
|
||||
*
|
||||
* @reason Properties are identity comparable
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "equals",
|
||||
constant = @Constant(
|
||||
classValue = BooleanProperty.class,
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private boolean skipFurtherComparison(final Object obj, final Operation<Boolean> orig) {
|
||||
return false;
|
||||
this.moonrise$setById(BY_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ import java.util.Collection;
|
||||
@Mixin(EnumProperty.class)
|
||||
abstract class EnumPropertyMixin<T extends Enum<T> & StringRepresentable> extends Property<T> implements PropertyAccess<T> {
|
||||
|
||||
@Shadow
|
||||
public abstract Collection<T> getPossibleValues();
|
||||
|
||||
@Unique
|
||||
private int[] idLookupTable;
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
|
||||
}
|
||||
|
||||
// remove values arrays
|
||||
this.values = null;
|
||||
for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
|
||||
final S value = entry.getValue();
|
||||
((StateHolderMixin<O, S>)(Object)(StateHolder<O, S>)value).values = null;
|
||||
@@ -126,8 +125,8 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public <T extends Comparable<T>> Optional<T> getOptionalValue(final Property<T> property) {
|
||||
return property == null ? Optional.empty() : Optional.ofNullable(this.optimisedTable.get(this.tableIndex, property));
|
||||
public <T extends Comparable<T>> T getNullableValue(Property<T> property) {
|
||||
return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,9 +66,6 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
@Unique
|
||||
private final ReferenceList<ServerPlayer> playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY);
|
||||
|
||||
@Unique
|
||||
private boolean isMarkedDirtyForPlayers;
|
||||
|
||||
@Unique
|
||||
private ChunkMap getChunkMap() {
|
||||
return (ChunkMap)this.playerProvider;
|
||||
@@ -123,16 +120,6 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$isMarkedDirtyForPlayers() {
|
||||
return this.isMarkedDirtyForPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$markDirtyForPlayers(final boolean value) {
|
||||
this.isMarkedDirtyForPlayers = value;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static final ServerPlayer[] EMPTY_PLAYER_ARRAY = new ServerPlayer[0];
|
||||
|
||||
@@ -300,14 +287,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
// no players to sent to, so don't need to update anything
|
||||
return null;
|
||||
}
|
||||
final LevelChunk ret = this.getChunkToSend();
|
||||
|
||||
if (ret != null) {
|
||||
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return this.getChunkToSend();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,14 +306,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
// no players to sent to, so don't need to update anything
|
||||
return null;
|
||||
}
|
||||
final LevelChunk ret = this.getChunkToSend();
|
||||
|
||||
if (ret != null) {
|
||||
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return this.getChunkToSend();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
|
||||
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemChunkMap;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
@@ -11,14 +11,17 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.Supplier;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.StreamTagVisitor;
|
||||
import net.minecraft.server.level.ChunkGenerationTask;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkResult;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
|
||||
import net.minecraft.server.level.ChunkTaskDispatcher;
|
||||
import net.minecraft.server.level.ChunkTrackingView;
|
||||
import net.minecraft.server.level.GeneratingChunkMap;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
@@ -28,7 +31,6 @@ import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.StaticCache2D;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
@@ -78,13 +80,10 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
|
||||
|
||||
@Shadow
|
||||
private ChunkTaskPriorityQueueSorter queueSorter;
|
||||
private ChunkTaskDispatcher worldgenTaskDispatcher;
|
||||
|
||||
@Shadow
|
||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
|
||||
|
||||
@Shadow
|
||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
|
||||
private ChunkTaskDispatcher lightTaskDispatcher;
|
||||
|
||||
@Shadow
|
||||
private int serverViewDistance;
|
||||
@@ -98,6 +97,12 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
@Shadow
|
||||
private Queue<Runnable> unloadQueue;
|
||||
|
||||
@Shadow
|
||||
private LongSet chunksToEagerlySave;
|
||||
|
||||
@Shadow
|
||||
private AtomicInteger activeChunkWrites;
|
||||
|
||||
public ChunkMapMixin(RegionStorageInfo regionStorageInfo, Path path, DataFixer dataFixer, boolean bl) {
|
||||
super(regionStorageInfo, path, dataFixer, bl);
|
||||
}
|
||||
@@ -128,11 +133,12 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
this.updatingChunkMap = null;
|
||||
this.visibleChunkMap = null;
|
||||
this.pendingUnloads = null;
|
||||
this.queueSorter = null;
|
||||
this.worldgenMailbox = null;
|
||||
this.mainThreadMailbox = null;
|
||||
this.worldgenTaskDispatcher = null;
|
||||
this.lightTaskDispatcher = null;
|
||||
this.pendingGenerationTasks = null;
|
||||
this.unloadQueue = null;
|
||||
this.chunksToEagerlySave = null;
|
||||
this.activeChunkWrites = null;
|
||||
|
||||
// Dummy impl for mods that try to loadAsync directly
|
||||
this.worker = new IOWorker(
|
||||
@@ -152,7 +158,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
@Override
|
||||
public CompletableFuture<Optional<CompoundTag>> loadAsync(final ChunkPos chunkPos) {
|
||||
final CompletableFuture<Optional<CompoundTag>> future = new CompletableFuture<>();
|
||||
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (tag, throwable) -> {
|
||||
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (final CompoundTag tag, final Throwable throwable) -> {
|
||||
if (throwable != null) {
|
||||
future.completeExceptionally(throwable);
|
||||
} else {
|
||||
@@ -184,6 +190,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason This map is not needed, we maintain our own ordered set of chunks to autosave.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void setChunkUnsaved(final ChunkPos pos) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system hooks
|
||||
* @author Spottedleaf
|
||||
@@ -261,6 +276,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void onLevelChange(final ChunkPos chunkPos, final IntSupplier intSupplier, final int i, final IntConsumer intConsumer) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
@@ -309,6 +333,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.autoSave();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void saveChunksEagerly(final BooleanSupplier hasTime) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
@@ -403,7 +436,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void onChunkReadyToSend(final LevelChunk chunk) {
|
||||
public void onChunkReadyToSend(final ChunkHolder holder, final LevelChunk chunk) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -422,7 +455,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
* @see NewChunkHolder#save(boolean)
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder) {
|
||||
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder, final long time) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -466,7 +499,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
*/
|
||||
@Overwrite
|
||||
public int getPlayerViewDistance(final ServerPlayer player) {
|
||||
return ChunkSystem.getSendViewDistance(player);
|
||||
return PlatformHooks.get().getSendViewDistance(player);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -505,6 +538,21 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "forEachSpawnCandidateChunk",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;"
|
||||
)
|
||||
)
|
||||
private <V> V redirectChunkHolderGet(final Long2ObjectLinkedOpenHashMap<V> instance, final long key) {
|
||||
return (V)this.getVisibleChunkIfPresent(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<CompoundTag>> read(final ChunkPos pos) {
|
||||
final CompletableFuture<Optional<CompoundTag>> ret = new CompletableFuture<>();
|
||||
@@ -524,10 +572,11 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> write(final ChunkPos pos, final CompoundTag tag) {
|
||||
public CompletableFuture<Void> write(final ChunkPos pos, final Supplier<CompoundTag> tag) {
|
||||
MoonriseRegionFileIO.scheduleSave(
|
||||
this.level, pos.x, pos.z, tag,
|
||||
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
|
||||
this.level, pos.x, pos.z, tag.get(),
|
||||
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -548,7 +597,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
)
|
||||
)
|
||||
private void avoidUpdateChunkTrackingInUpdate(final ChunkMap instance, final ServerPlayer serverPlayer) {
|
||||
ChunkSystem.addPlayerToDistanceMaps(this.level, serverPlayer);
|
||||
PlatformHooks.get().addPlayerToDistanceMaps(this.level, serverPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -578,7 +627,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
)
|
||||
private void avoidApplyChunkTrackingViewInUpdate(final ChunkMap instance, final ServerPlayer serverPlayer,
|
||||
final ChunkTrackingView chunkTrackingView) {
|
||||
ChunkSystem.removePlayerFromDistanceMaps(this.level, serverPlayer);
|
||||
PlatformHooks.get().removePlayerFromDistanceMaps(this.level, serverPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,7 +641,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
)
|
||||
)
|
||||
private void updateMapsHook(final ServerPlayer player, final CallbackInfo ci) {
|
||||
ChunkSystem.updateMaps(this.level, player);
|
||||
PlatformHooks.get().updateMaps(this.level, player);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,12 +22,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(ChunkStorage.class)
|
||||
abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
private IOWorker worker;
|
||||
public IOWorker worker;
|
||||
|
||||
@Unique
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -118,13 +119,13 @@ abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseab
|
||||
method = "write",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
|
||||
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;"
|
||||
)
|
||||
)
|
||||
private CompletableFuture<Void> redirectWrite(final IOWorker instance, final ChunkPos chunkPos,
|
||||
final CompoundTag compoundTag) {
|
||||
final Supplier<CompoundTag> compoundTag) {
|
||||
try {
|
||||
this.storage.write(chunkPos, compoundTag);
|
||||
this.storage.write(chunkPos, compoundTag.get());
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} catch (final Throwable throwable) {
|
||||
return CompletableFuture.failedFuture(throwable);
|
||||
|
||||
@@ -41,8 +41,8 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
||||
@Final
|
||||
private ClientChunkCache chunkSource;
|
||||
|
||||
protected ClientLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
||||
protected ClientLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,9 +55,8 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData,
|
||||
ResourceKey<Level> resourceKey, Holder<DimensionType> holder, int i, int j, Supplier<ProfilerFiller> supplier,
|
||||
LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
|
||||
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey<Level> resourceKey,
|
||||
Holder<DimensionType> holder, int i, int j, LevelRenderer levelRenderer, boolean bl, long l, int k, CallbackInfo ci) {
|
||||
this.entityStorage = null;
|
||||
|
||||
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
||||
@@ -8,13 +9,13 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ThrottlingChunkTaskDispatcher;
|
||||
import net.minecraft.server.level.Ticket;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.server.level.TickingTracker;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.SortedArraySet;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
@@ -45,13 +46,7 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
Set<ChunkHolder> chunksToUpdateFutures;
|
||||
|
||||
@Shadow
|
||||
ChunkTaskPriorityQueueSorter ticketThrottler;
|
||||
|
||||
@Shadow
|
||||
ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> ticketThrottlerInput;
|
||||
|
||||
@Shadow
|
||||
ProcessorHandle<ChunkTaskPriorityQueueSorter.Release> ticketThrottlerReleaser;
|
||||
ThrottlingChunkTaskDispatcher ticketDispatcher;
|
||||
|
||||
@Shadow
|
||||
LongSet ticketsToRelease;
|
||||
@@ -59,12 +54,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
@Shadow
|
||||
Executor mainThreadExecutor;
|
||||
|
||||
@Shadow
|
||||
private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
|
||||
|
||||
@Shadow
|
||||
private int simulationDistance;
|
||||
|
||||
|
||||
@Override
|
||||
public ChunkMap moonrise$getChunkMap() {
|
||||
throw new AbstractMethodError();
|
||||
@@ -86,16 +79,14 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
this.tickingTicketsTracker = null;
|
||||
this.playerTicketManager = null;
|
||||
this.chunksToUpdateFutures = null;
|
||||
this.ticketThrottler = null;
|
||||
this.ticketThrottlerInput = null;
|
||||
this.ticketThrottlerReleaser = null;
|
||||
this.ticketDispatcher = null;
|
||||
this.ticketsToRelease = null;
|
||||
this.mainThreadExecutor = null;
|
||||
this.simulationDistance = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkHolderManager moonrise$getChunkHolderManager() {
|
||||
public final ChunkHolderManager moonrise$getChunkHolderManager() {
|
||||
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
||||
}
|
||||
|
||||
@@ -296,7 +287,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
*/
|
||||
@Overwrite
|
||||
public void updateSimulationDistance(final int simulationDistance) {
|
||||
((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getPlayerChunkLoader().setTickDistance(simulationDistance);
|
||||
// note: vanilla does not clamp to 0, but we do simply because we need a min of 0
|
||||
final int clamped = Mth.clamp(simulationDistance, 0, MoonriseConstants.MAX_VIEW_DISTANCE);
|
||||
|
||||
((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getPlayerChunkLoader().setTickDistance(clamped);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,6 +320,15 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Remove old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public LongSet getTickingChunks() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason This hack is not required anymore, see {@link MinecraftServerMixin}
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -195,15 +195,6 @@ abstract class GenerationChunkHolderMixin {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Chunk system is not built on futures anymore
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public int getGenerationRefCount() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk holder
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -93,11 +93,15 @@ abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystemLevelCh
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnsaved(final boolean needsSaving) {
|
||||
if (!needsSaving) {
|
||||
public boolean tryMarkSaved() {
|
||||
if (!this.isUnsaved()) {
|
||||
return false;
|
||||
}
|
||||
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
|
||||
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
|
||||
}
|
||||
super.setUnsaved(needsSaving);
|
||||
|
||||
super.tryMarkSaved();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEnti
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.util.profiling.Profiler;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -37,9 +38,6 @@ import java.util.function.Predicate;
|
||||
@Mixin(Level.class)
|
||||
abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter, LevelAccessor, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
public abstract ProfilerFiller getProfiler();
|
||||
|
||||
@Shadow
|
||||
public abstract LevelChunk getChunk(int i, int j);
|
||||
|
||||
@@ -59,7 +57,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moonrise$setEntityLookup(final EntityLookup entityLookup) {
|
||||
public final void moonrise$setEntityLookup(final EntityLookup entityLookup) {
|
||||
if (this.entityLookup != null && !(this.entityLookup instanceof DefaultEntityLookup)) {
|
||||
throw new IllegalStateException("Entity lookup already initialised");
|
||||
}
|
||||
@@ -87,7 +85,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
@Overwrite
|
||||
@Override
|
||||
public List<Entity> getEntities(final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
final List<Entity> ret = new ArrayList<>();
|
||||
|
||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entity, boundingBox, ret, predicate);
|
||||
@@ -105,7 +103,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
public <T extends Entity> void getEntities(final EntityTypeTest<Entity, T> entityTypeTest,
|
||||
final AABB boundingBox, final Predicate<? super T> predicate,
|
||||
final List<? super T> into, final int maxCount) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
|
||||
if (entityTypeTest instanceof EntityType<T> byType) {
|
||||
if (maxCount != Integer.MAX_VALUE) {
|
||||
@@ -178,7 +176,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
*/
|
||||
@Override
|
||||
public final <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
final List<T> ret = new ArrayList<>();
|
||||
|
||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate);
|
||||
@@ -196,7 +194,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
*/
|
||||
@Override
|
||||
public final List<Entity> moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
final List<Entity> ret = new ArrayList<>();
|
||||
|
||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate);
|
||||
|
||||
@@ -240,6 +240,7 @@ abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop<TickTask
|
||||
private void closeIOThreads(final CallbackInfo ci) {
|
||||
LOGGER.info("Waiting for I/O tasks to complete...");
|
||||
MoonriseRegionFileIO.flush((MinecraftServer)(Object)this);
|
||||
LOGGER.info("All I/O tasks to complete");
|
||||
if ((Object)this instanceof DedicatedServer) {
|
||||
MoonriseCommon.haltExecutors();
|
||||
}
|
||||
|
||||
@@ -43,4 +43,17 @@ abstract class PlayerListMixin {
|
||||
)
|
||||
private void doNotAdjustVD(final PlayerList instance, final Packet<?> packet) {}
|
||||
|
||||
|
||||
/**
|
||||
* @reason The RegionizedPlayerChunkLoader will handle the SD packet
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "setSimulationDistance",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/players/PlayerList;broadcastAll(Lnet/minecraft/network/protocol/Packet;)V"
|
||||
)
|
||||
)
|
||||
private void doNotAdjustSD(final PlayerList instance, final Packet<?> packet) {}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
@@ -44,7 +45,14 @@ import java.util.stream.Stream;
|
||||
@Mixin(PoiManager.class)
|
||||
// Declare the generic type as Object so that our Overrides match the method signature of the superclass
|
||||
// Specifically, getOrCreate must return Object so that existing invokes do not route to the superclass
|
||||
public abstract class PoiManagerMixin extends SectionStorage<Object> implements ChunkSystemPoiManager {
|
||||
public abstract class PoiManagerMixin extends SectionStorage<Object, Object> implements ChunkSystemPoiManager {
|
||||
|
||||
public PoiManagerMixin(final SimpleRegionStorage simpleRegionStorage, final Codec<Object> codec, final Function<Object, Object> function,
|
||||
final BiFunction<Object, Runnable, Object> biFunction, final Function<Runnable, Object> function2,
|
||||
final RegistryAccess registryAccess, final ChunkIOErrorReporter chunkIOErrorReporter,
|
||||
final LevelHeightAccessor levelHeightAccessor) {
|
||||
super(simpleRegionStorage, codec, function, biFunction, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
abstract boolean isVillageCenter(long l);
|
||||
@@ -52,10 +60,6 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
@Shadow
|
||||
public abstract void checkConsistencyWithBlocks(SectionPos sectionPos, LevelChunkSection levelChunkSection);
|
||||
|
||||
public PoiManagerMixin(SimpleRegionStorage simpleRegionStorage, Function<Runnable, Codec<Object>> function, Function<Runnable, Object> function2, RegistryAccess registryAccess, ChunkIOErrorReporter chunkIOErrorReporter, LevelHeightAccessor levelHeightAccessor) {
|
||||
super(simpleRegionStorage, function, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private ServerLevel world;
|
||||
|
||||
@@ -151,8 +155,8 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
|
||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
|
||||
|
||||
final ChunkHolderManager manager = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
||||
final PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
||||
final PoiChunk ret = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
||||
.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
||||
|
||||
return ret == null ? Optional.empty() : (Optional)ret.getSectionForVanilla(chunkY);
|
||||
}
|
||||
@@ -206,9 +210,13 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
public final void moonrise$onUnload(final long coordinate) { // Paper - rewrite chunk system
|
||||
final int chunkX = CoordinateUtils.getChunkX(coordinate);
|
||||
final int chunkZ = CoordinateUtils.getChunkZ(coordinate);
|
||||
|
||||
final int minY = WorldUtil.getMinSection(this.world);
|
||||
final int maxY = WorldUtil.getMaxSection(this.world);
|
||||
|
||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
|
||||
for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
|
||||
final long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
|
||||
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
||||
final long sectionPos = SectionPos.asLong(chunkX, sectionY, chunkZ);
|
||||
this.updateDistanceTracking(sectionPos);
|
||||
}
|
||||
}
|
||||
@@ -217,8 +225,12 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
public final void moonrise$loadInPoiChunk(final PoiChunk poiChunk) {
|
||||
final int chunkX = poiChunk.chunkX;
|
||||
final int chunkZ = poiChunk.chunkZ;
|
||||
|
||||
final int minY = WorldUtil.getMinSection(this.world);
|
||||
final int maxY = WorldUtil.getMaxSection(this.world);
|
||||
|
||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
|
||||
for (int sectionY = this.levelHeightAccessor.getMinSection(); sectionY < this.levelHeightAccessor.getMaxSection(); ++sectionY) {
|
||||
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
||||
final PoiSection section = poiChunk.getSection(sectionY);
|
||||
if (section != null && !((ChunkSystemPoiSection)section).moonrise$isEmpty()) {
|
||||
this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
|
||||
@@ -254,20 +266,4 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
private <T> Stream<T> skipLoadedSet(final Stream<T> instance, final Predicate<? super T> predicate) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$close() throws IOException {}
|
||||
|
||||
@Override
|
||||
public final CompoundTag moonrise$read(final int chunkX, final int chunkZ) throws IOException {
|
||||
return MoonriseRegionFileIO.loadData(
|
||||
this.world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.POI_DATA,
|
||||
MoonriseRegionFileIO.getIOBlockingPriorityForCurrentThread()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$write(final int chunkX, final int chunkZ, final CompoundTag data) throws IOException {
|
||||
MoonriseRegionFileIO.scheduleSave(this.world, chunkX, chunkZ, data, MoonriseRegionFileIO.RegionFileType.POI_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ abstract class PoiSectionMixin implements ChunkSystemPoiSection {
|
||||
|
||||
|
||||
@Unique
|
||||
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);;
|
||||
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$isEmpty() {
|
||||
|
||||
@@ -2,8 +2,6 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.storage.ChunkSystemSectionStorage;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
||||
import net.minecraft.world.level.chunk.storage.SectionStorage;
|
||||
@@ -23,15 +21,11 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(SectionStorage.class)
|
||||
abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoCloseable {
|
||||
abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
private SimpleRegionStorage simpleRegionStorage;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static Logger LOGGER;
|
||||
|
||||
|
||||
@Unique
|
||||
private RegionFileStorage storage;
|
||||
@@ -41,6 +35,9 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
||||
return this.storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moonrise$close() throws IOException {}
|
||||
|
||||
/**
|
||||
* @reason Retrieve storage from IOWorker, and then nuke it
|
||||
* @author Spottedleaf
|
||||
@@ -61,12 +58,8 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public final CompletableFuture<Optional<CompoundTag>> tryRead(final ChunkPos pos) {
|
||||
try {
|
||||
return CompletableFuture.completedFuture(Optional.ofNullable(this.moonrise$read(pos.x, pos.z)));
|
||||
} catch (final Throwable thr) {
|
||||
return CompletableFuture.failedFuture(thr);
|
||||
}
|
||||
public final CompletableFuture<Optional<SectionStorage.PackedChunk<P>>> tryRead(final ChunkPos pos) {
|
||||
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,29 +67,17 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void readColumn(final ChunkPos pos, final RegistryOps<Tag> ops, final CompoundTag data) {
|
||||
public void unpackChunk(final ChunkPos chunkPos, final SectionStorage.PackedChunk<P> packedChunk) {
|
||||
throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system hook
|
||||
* @reason Destroy old chunk system hook
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "writeColumn(Lnet/minecraft/world/level/ChunkPos;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/chunk/storage/SimpleRegionStorage;write(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
|
||||
)
|
||||
)
|
||||
private CompletableFuture<Void> redirectWrite(final SimpleRegionStorage instance, final ChunkPos pos,
|
||||
final CompoundTag tag) {
|
||||
try {
|
||||
this.moonrise$write(pos.x, pos.z, tag);
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.error("Error writing poi chunk data to disk for chunk " + pos, ex);
|
||||
}
|
||||
return null;
|
||||
@Overwrite
|
||||
private void writeChunk(final ChunkPos chunkPos) {
|
||||
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,13 +3,13 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ChunkSerializer.class)
|
||||
abstract class ChunkSerializerMixin {
|
||||
@Mixin(SerializableChunkData.class)
|
||||
abstract class SerializableChunkDataMixin {
|
||||
|
||||
/**
|
||||
* @reason Chunk system handles this during full transition
|
||||
@@ -23,6 +23,6 @@ abstract class ChunkSerializerMixin {
|
||||
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
|
||||
)
|
||||
)
|
||||
private static void skipConsistencyCheck(PoiManager instance, SectionPos sectionPos, LevelChunkSection levelChunkSection) {}
|
||||
private void skipConsistencyCheck(final PoiManager instance, final SectionPos sectionPos, final LevelChunkSection levelChunkSection) {}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LightChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
@@ -51,6 +52,10 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
@Final
|
||||
public ServerLevel level;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private DimensionDataStorage dataStorage;
|
||||
|
||||
@Unique
|
||||
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
|
||||
@@ -81,6 +86,13 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
completable::complete
|
||||
);
|
||||
|
||||
if (!completable.isDone() && chunkTaskScheduler.hasShutdown()) {
|
||||
throw new IllegalStateException(
|
||||
"Chunk system has shut down, cannot process chunk requests in world '" + ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(this.level) + "' at "
|
||||
+ "(" + chunkX + "," + chunkZ + ") status: " + toStatus
|
||||
);
|
||||
}
|
||||
|
||||
if (TickThread.isTickThreadFor(this.level, chunkX, chunkZ)) {
|
||||
ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ);
|
||||
this.mainThreadProcessor.managedBlock(completable::isDone);
|
||||
@@ -261,6 +273,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
@Override
|
||||
@Overwrite
|
||||
public void close() throws IOException {
|
||||
this.dataStorage.close();
|
||||
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(true, true);
|
||||
}
|
||||
|
||||
@@ -309,12 +322,28 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @reason We need to use {@link ChunkHolder#getChunkToSend()} as the new chunk system will not bring every chunk
|
||||
* sent to players up to block ticking.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "broadcastChangedChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ChunkHolder;getTickingChunk()Lnet/minecraft/world/level/chunk/LevelChunk;")
|
||||
)
|
||||
private LevelChunk redirectTickingChunk(final ChunkHolder instance) {
|
||||
return instance.getChunkToSend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Perform mid-tick chunk task processing during chunk tick
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "tickChunks",
|
||||
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
shift = At.Shift.AFTER,
|
||||
@@ -335,23 +364,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ServerLevel;isNaturalSpawningAllowed(Lnet/minecraft/world/level/ChunkPos;)Z"
|
||||
)
|
||||
)
|
||||
private boolean shortNaturalSpawning(final ServerLevel instance, final ChunkPos chunkPos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason In the chunk system, ticking chunks always have loaded entities. Of course, they are also always
|
||||
* marked to be as ticking as well.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
|
||||
@@ -360,35 +373,4 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Since chunks in non-simulation range are only brought up to FULL status, not TICKING,
|
||||
* those chunks may not be present in the ticking list and as a result we need to use our own list
|
||||
* to ensure these chunks broadcast changes
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V"
|
||||
)
|
||||
)
|
||||
private void fixBroadcastChanges(final List<ServerChunkCache.ChunkAndHolder> instance,
|
||||
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
|
||||
final ReferenceList<ChunkHolder> unsyncedChunks = ((ChunkSystemServerLevel)this.level).moonrise$getUnsyncedChunks();
|
||||
final ChunkHolder[] chunkHolders = unsyncedChunks.getRawDataUnchecked();
|
||||
final int totalUnsyncedChunks = unsyncedChunks.size();
|
||||
|
||||
Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length);
|
||||
for (int i = 0; i < totalUnsyncedChunks; ++i) {
|
||||
final ChunkHolder chunkHolder = chunkHolders[i];
|
||||
final LevelChunk chunk = chunkHolder.getChunkToSend();
|
||||
if (chunk != null) {
|
||||
chunkHolder.broadcastChanges(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
((ChunkSystemServerLevel)this.level).moonrise$clearUnsyncedChunks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
@Final
|
||||
private ServerChunkCache chunkSource;
|
||||
|
||||
protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
||||
protected ServerLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -123,9 +123,6 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
@Unique
|
||||
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
|
||||
|
||||
@Unique
|
||||
private static final ChunkHolder[] EMPTY_CHUNK_HOLDERS = new ChunkHolder[0];
|
||||
|
||||
@Unique
|
||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
|
||||
|
||||
@@ -135,9 +132,6 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
@Unique
|
||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
|
||||
|
||||
@Unique
|
||||
private final ReferenceList<ChunkHolder> unsyncedChunks = new ReferenceList<>(EMPTY_CHUNK_HOLDERS);
|
||||
|
||||
/**
|
||||
* @reason Initialise fields / destroy entity manager state
|
||||
* @author Spottedleaf
|
||||
@@ -354,45 +348,6 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
return this.entityTickingChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks() {
|
||||
return this.unsyncedChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder) {
|
||||
if (((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(true);
|
||||
this.unsyncedChunks.add(chunkHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder) {
|
||||
if (!((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false);
|
||||
this.unsyncedChunks.remove(chunkHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$clearUnsyncedChunks() {
|
||||
final ChunkHolder[] chunkHolders = this.unsyncedChunks.getRawDataUnchecked();
|
||||
final int totalUnsyncedChunks = this.unsyncedChunks.size();
|
||||
|
||||
Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length);
|
||||
for (int i = 0; i < totalUnsyncedChunks; ++i) {
|
||||
final ChunkHolder chunkHolder = chunkHolders[i];
|
||||
|
||||
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false);
|
||||
}
|
||||
this.unsyncedChunks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ServerChunkCache chunkSource = this.chunkSource;
|
||||
|
||||
@@ -85,11 +85,20 @@ abstract class ChunkMapMixin {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use nearby players to avoid iterating over all online players
|
||||
* @reason Avoid checking first if there are nearby players, as we make internal perform this implicitly.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
|
||||
return this.anyPlayerCloseEnoughForSpawningInternal(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use nearby players to avoid iterating over all online players
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos) {
|
||||
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
|
||||
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -106,4 +107,13 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
|
||||
public boolean hasPlayersNearby(final long pos) {
|
||||
return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use spawnChunkTracker instead
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public LongIterator getSpawnCandidateChunks() {
|
||||
return this.spawnChunkTracker.getPositions().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@ package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
@@ -18,22 +16,15 @@ import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(ServerChunkCache.class)
|
||||
abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
@@ -42,109 +33,17 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
@Final
|
||||
public ServerLevel level;
|
||||
|
||||
@Unique
|
||||
private ServerChunkCache.ChunkAndHolder[] iterationCopy;
|
||||
@Shadow
|
||||
@Final
|
||||
public ChunkMap chunkMap;
|
||||
|
||||
|
||||
@Unique
|
||||
private int iterationCopyLen;
|
||||
private final SimpleThreadUnsafeRandom shuffleRandom = new SimpleThreadUnsafeRandom(0L);
|
||||
|
||||
@Unique
|
||||
private final SimpleRandom shuffleRandom = new SimpleRandom(0L);
|
||||
|
||||
/**
|
||||
* @reason Avoid creating the list, which is sized at the chunkholder count. The actual number of ticking
|
||||
* chunks is always lower. The mixin below will initialise the list to non-null.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Lists;newArrayListWithCapacity(I)Ljava/util/ArrayList;"
|
||||
)
|
||||
)
|
||||
private <T> ArrayList<T> avoidListCreation(final int initialArraySize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Initialise the list to contain only the ticking chunks.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@ModifyVariable(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "STORE",
|
||||
opcode = Opcodes.ASTORE,
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private List<ServerChunkCache.ChunkAndHolder> initTickChunks(final List<ServerChunkCache.ChunkAndHolder> shouldBeNull) {
|
||||
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
||||
|
||||
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
||||
final int size = tickingChunks.size();
|
||||
|
||||
if (this.iterationCopy == null || this.iterationCopy.length < size) {
|
||||
this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length];
|
||||
}
|
||||
this.iterationCopyLen = size;
|
||||
System.arraycopy(raw, 0, this.iterationCopy, 0, size);
|
||||
|
||||
return ObjectArrayList.wrap(
|
||||
this.iterationCopy, size
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
|
||||
* function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
|
||||
)
|
||||
)
|
||||
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
|
||||
this.shuffleRandom.setSeed(randomSource.nextLong());
|
||||
Util.shuffle(list, this.shuffleRandom);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Do not initialise ticking chunk list, as we did that above.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/Iterator;hasNext()Z",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private <E> boolean skipTickAdd(final Iterator<E> instance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use the nearby players cache
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ChunkMap;anyPlayerCloseEnoughForSpawning(Lnet/minecraft/world/level/ChunkPos;)Z"
|
||||
)
|
||||
)
|
||||
private boolean useNearbyCache(final ChunkMap instance, final ChunkPos chunkPos,
|
||||
@Local(ordinal = 0, argsOnly = false) final LevelChunk levelChunk) {
|
||||
final ChunkData chunkData =
|
||||
((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
|
||||
private boolean isChunkNearPlayer(final ChunkMap chunkMap, final ChunkPos chunkPos, final LevelChunk levelChunk) {
|
||||
final ChunkData chunkData = ((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
|
||||
.moonrise$getRealChunkHolder().holderData;
|
||||
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
||||
if (nearbyPlayers == null) {
|
||||
@@ -162,7 +61,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
|
||||
Objects.checkFromIndexSize(0, len, raw.length);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (instance.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
|
||||
if (chunkMap.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -171,19 +70,47 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Clear the iteration array after the list is done being used.
|
||||
* @reason Use the player ticking chunks list, which already contains chunks that are:
|
||||
* 1. block ticking
|
||||
* 2. within spawn range (8 chunks on any axis)
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "tickChunks",
|
||||
@Overwrite
|
||||
private void collectTickingChunks(final List<LevelChunk> list) {
|
||||
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
||||
|
||||
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
||||
final int size = tickingChunks.size();
|
||||
|
||||
final ChunkMap chunkMap = this.chunkMap;
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i];
|
||||
final LevelChunk levelChunk = chunkAndHolder.chunk();
|
||||
|
||||
if (!this.isChunkNearPlayer(chunkMap, levelChunk.getPos(), levelChunk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list.add(levelChunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
|
||||
* function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks()V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
||||
ordinal = 0,
|
||||
shift = At.Shift.AFTER
|
||||
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
|
||||
)
|
||||
)
|
||||
private void broadcastChanges(final CallbackInfo ci) {
|
||||
Arrays.fill(this.iterationCopy, 0, this.iterationCopyLen, null);
|
||||
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
|
||||
this.shuffleRandom.setSeed(randomSource.nextLong());
|
||||
Util.shuffle(list, this.shuffleRandom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,37 +6,32 @@ import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.entity.vehicle.AbstractMinecart;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(ArmorStand.class)
|
||||
abstract class ArmorStandMixin extends LivingEntity {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static Predicate<Entity> RIDABLE_MINECARTS;
|
||||
|
||||
protected ArmorStandMixin(EntityType<? extends LivingEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Optimise this method by making it use the class lookup
|
||||
* @reason Optimise by making it use the class lookup
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void pushEntities() {
|
||||
final List<AbstractMinecart> nearby = this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), RIDABLE_MINECARTS);
|
||||
|
||||
for (int i = 0, len = nearby.size(); i < len; ++i) {
|
||||
final AbstractMinecart minecart = nearby.get(i);
|
||||
if (this.distanceToSqr(minecart) <= 0.2) {
|
||||
minecart.push(this);
|
||||
}
|
||||
}
|
||||
@Redirect(
|
||||
method = "pushEntities",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/Level;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;"
|
||||
)
|
||||
)
|
||||
private List<Entity> redirectGetEntities(final Level instance, final Entity entity, final AABB list, final Predicate<? super Entity> arg) {
|
||||
return (List)this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), arg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,12 @@ import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import it.unimi.dsi.fastutil.HashCommon;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateHolder;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -32,7 +28,13 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
protected BlockBehaviour.BlockStateBase.Cache cache;
|
||||
|
||||
@Shadow
|
||||
public abstract VoxelShape getCollisionShape(BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext);
|
||||
public abstract boolean isAir();
|
||||
|
||||
@Shadow
|
||||
public VoxelShape[] occlusionShapesByFace;
|
||||
|
||||
@Shadow
|
||||
public VoxelShape occlusionShape;
|
||||
|
||||
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
|
||||
super(object, reference2ObjectArrayMap, mapCodec);
|
||||
@@ -74,7 +76,7 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
}
|
||||
if (neighbours) {
|
||||
for (final Direction direction : DIRECTIONS_CACHED) {
|
||||
initCaches(Shapes.getFaceShape(shape, direction), false);
|
||||
initCaches(((CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction), false);
|
||||
initCaches(shape.getFaceShape(direction), false);
|
||||
}
|
||||
}
|
||||
@@ -93,10 +95,9 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
private void initCollisionState(final CallbackInfo ci) {
|
||||
if (this.cache != null) {
|
||||
final VoxelShape collisionShape = this.cache.collisionShape;
|
||||
try {
|
||||
this.constantCollisionShape = this.getCollisionShape(null, null, null);
|
||||
} catch (final Throwable throwable) {
|
||||
// :(
|
||||
if (this.isAir()) {
|
||||
this.constantCollisionShape = Shapes.empty();
|
||||
} else {
|
||||
this.constantCollisionShape = null;
|
||||
}
|
||||
this.occludesFullBlock = ((CollisionVoxelShape)collisionShape).moonrise$occludesFullBlock();
|
||||
@@ -107,17 +108,21 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
if (this.constantCollisionShape != null) {
|
||||
initCaches(this.constantCollisionShape, true);
|
||||
}
|
||||
if (this.cache.occlusionShapes != null) {
|
||||
for (final VoxelShape shape : this.cache.occlusionShapes) {
|
||||
initCaches(shape, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.occludesFullBlock = false;
|
||||
this.emptyCollisionShape = false;
|
||||
this.emptyConstantCollisionShape = false;
|
||||
this.constantCollisionShape = null;
|
||||
}
|
||||
|
||||
if (this.occlusionShape != null) {
|
||||
initCaches(this.occlusionShape, true);
|
||||
}
|
||||
if (this.occlusionShapesByFace != null) {
|
||||
for (final VoxelShape shape : this.occlusionShapesByFace) {
|
||||
initCaches(shape, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.util.NoneMatchStream;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArraySet;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrays;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
@@ -27,11 +23,8 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
abstract class EntityMixin {
|
||||
@@ -57,12 +50,6 @@ abstract class EntityMixin {
|
||||
@Shadow
|
||||
private boolean onGround;
|
||||
|
||||
@Shadow
|
||||
public abstract boolean isAlive();
|
||||
|
||||
@Shadow
|
||||
protected abstract void onInsideBlock(final BlockState blockState);
|
||||
|
||||
@Unique
|
||||
private static float[] calculateStepHeights(final AABB box, final List<VoxelShape> voxels, final List<AABB> aabbs, final float stepHeight,
|
||||
final float collidedY) {
|
||||
@@ -296,163 +283,4 @@ abstract class EntityMixin {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid streams for retrieving blocks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "move",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/Level;getBlockStatesIfLoaded(Lnet/minecraft/world/phys/AABB;)Ljava/util/stream/Stream;",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private <T> Stream<T> avoidStreams(final Level world, final AABB boundingBox) {
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Mth.floor(boundingBox.minY);
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
final int maxBlockX = Mth.floor(boundingBox.maxX);
|
||||
final int maxBlockY = Mth.floor(boundingBox.maxY);
|
||||
final int maxBlockZ = Mth.floor(boundingBox.maxZ);
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
if (!((ChunkSystemLevel)world).moonrise$areChunksLoaded(minChunkX, minChunkZ, maxChunkX, maxChunkZ)) {
|
||||
return new NoneMatchStream<>(true);
|
||||
}
|
||||
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
|
||||
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
||||
|
||||
if (blockState.is(Blocks.LAVA) || blockState.is(BlockTags.FIRE)) {
|
||||
return new NoneMatchStream<>(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new NoneMatchStream<>(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Retrieve blocks more efficiently
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void checkInsideBlocks() {
|
||||
final AABB boundingBox = this.getBoundingBox();
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX + CollisionUtil.COLLISION_EPSILON);
|
||||
final int minBlockY = Mth.floor(boundingBox.minY + CollisionUtil.COLLISION_EPSILON);
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ + CollisionUtil.COLLISION_EPSILON);
|
||||
|
||||
final int maxBlockX = Mth.floor(boundingBox.maxX - CollisionUtil.COLLISION_EPSILON);
|
||||
final int maxBlockY = Mth.floor(boundingBox.maxY - CollisionUtil.COLLISION_EPSILON);
|
||||
final int maxBlockZ = Mth.floor(boundingBox.maxZ - CollisionUtil.COLLISION_EPSILON);
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final Level world = this.level;
|
||||
|
||||
if (!((ChunkSystemLevel)world).moonrise$areChunksLoaded(minChunkX, minChunkZ, maxChunkX, maxChunkZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
|
||||
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
mutablePos.setY(currY | (currChunkY << 4));
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
mutablePos.setZ(currZ | (currChunkZ << 4));
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
mutablePos.setX(currX | (currChunkX << 4));
|
||||
|
||||
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
||||
|
||||
if (!this.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockState.entityInside(world, mutablePos, (Entity)(Object)this);
|
||||
this.onInsideBlock(blockState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
final Vec3 to = clipContext.getTo();
|
||||
final Vec3 from = clipContext.getFrom();
|
||||
|
||||
return BlockHitResult.miss(to, Direction.getNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
||||
return BlockHitResult.miss(to, Direction.getApproximateNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
||||
}
|
||||
|
||||
@Unique
|
||||
|
||||
@@ -4,9 +4,7 @@ import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||
import net.minecraft.client.renderer.block.LiquidBlockRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.shapes.ArrayVoxelShape;
|
||||
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||
@@ -23,12 +21,7 @@ abstract class LiquidBlockRendererMixin {
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
private static boolean isFaceOccludedByState(final BlockGetter world, final Direction direction, final float height,
|
||||
final BlockPos pos, final BlockState state) {
|
||||
if (!state.canOcclude()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isFaceOccludedByState(final Direction direction, final float height, final BlockState state) {
|
||||
// check for created shape is empty
|
||||
if (height < (float)CollisionUtil.COLLISION_EPSILON) {
|
||||
return false;
|
||||
@@ -57,18 +50,19 @@ abstract class LiquidBlockRendererMixin {
|
||||
);
|
||||
}
|
||||
|
||||
final VoxelShape stateShape = ((CollisionVoxelShape)state.getOcclusionShape(world, pos)).moonrise$getFaceShapeClamped(direction.getOpposite());
|
||||
final VoxelShape occlusionShape = ((CollisionVoxelShape)state.getFaceOcclusionShape(direction.getOpposite()))
|
||||
.moonrise$getFaceShapeClamped(direction.getOpposite());
|
||||
|
||||
if (stateShape.isEmpty()) {
|
||||
if (occlusionShape.isEmpty()) {
|
||||
// cannot occlude
|
||||
return false;
|
||||
}
|
||||
|
||||
// fast check for box
|
||||
if (heightShape == stateShape) {
|
||||
if (heightShape == occlusionShape) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !Shapes.joinIsNotEmpty(heightShape, stateShape, BooleanOp.ONLY_FIRST);
|
||||
return !Shapes.joinIsNotEmpty(heightShape, occlusionShape, BooleanOp.ONLY_FIRST);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user