Compare commits
74 Commits
v0.1.0-bet
...
v0.1.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fdc3cb1df | ||
|
|
04e2b976ac | ||
|
|
bd938e6e35 | ||
|
|
3624d261f8 | ||
|
|
6c75d6935e | ||
|
|
e40c40613d | ||
|
|
84e7106762 | ||
|
|
0218cb5979 | ||
|
|
7d0d5a56dd | ||
|
|
4597f04a6d | ||
|
|
20764890a7 | ||
|
|
f719abdb64 | ||
|
|
0dce47bba6 | ||
|
|
47584ceee1 | ||
|
|
2fc858a683 | ||
|
|
68ea18bc51 | ||
|
|
9944946b29 | ||
|
|
ec1120ed10 | ||
|
|
8040c7a264 | ||
|
|
a9e36795e5 | ||
|
|
a70073ae3e | ||
|
|
6724814c02 | ||
|
|
f32a08738e | ||
|
|
56e48ed069 | ||
|
|
9da99576a6 | ||
|
|
dfbe1bcf8b | ||
|
|
ae29196221 | ||
|
|
f1eb61cc51 | ||
|
|
58c933938f | ||
|
|
1dc3cb5f14 | ||
|
|
2acfc6a68e | ||
|
|
c22538c364 | ||
|
|
e244c60375 | ||
|
|
65253d5199 | ||
|
|
6e82b034b7 | ||
|
|
a7cd78e63b | ||
|
|
05ba7066d9 | ||
|
|
340ac4e8f5 | ||
|
|
a4770aca2b | ||
|
|
a8d4ce526b | ||
|
|
2acd5cc213 | ||
|
|
07dce0ffe6 | ||
|
|
c2e2f0b9f2 | ||
|
|
1374ea34ee | ||
|
|
fef4872a3a | ||
|
|
a9678c76eb | ||
|
|
bafa195546 | ||
|
|
04c6e6c01a | ||
|
|
7351872730 | ||
|
|
77b977ac17 | ||
|
|
f0d122f358 | ||
|
|
aa240eb16d | ||
|
|
f9c99d1e32 | ||
|
|
3fa0ff67a7 | ||
|
|
f7d9e5b422 | ||
|
|
5d1975154b | ||
|
|
d21fa9f48c | ||
|
|
1c150afe83 | ||
|
|
d9988c86f4 | ||
|
|
0958439e54 | ||
|
|
8d9d6488e8 | ||
|
|
8ba83bc9c2 | ||
|
|
0c60a6ac08 | ||
|
|
d404dbc6c5 | ||
|
|
8d456890f1 | ||
|
|
23eddfe918 | ||
|
|
55ff406372 | ||
|
|
3e25a2f4aa | ||
|
|
cfa80c4488 | ||
|
|
453b635ef2 | ||
|
|
e07e4fdcc4 | ||
|
|
81bb9701da | ||
|
|
d0a7f9af62 | ||
|
|
fe7bcfc56a |
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
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -31,8 +31,8 @@ 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: "setup dependencies"
|
||||
run: ./install_deps.sh
|
||||
- name: "execute gradle build"
|
||||
run: ./gradlew build
|
||||
- name: Determine Snapshot Status
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
||||
[submodule "ConcurrentUtil"]
|
||||
path = ConcurrentUtil
|
||||
url = https://github.com/Spottedleaf/ConcurrentUtil.git
|
||||
[submodule "YamlConfig"]
|
||||
path = YamlConfig
|
||||
url = https://github.com/Spottedleaf/YamlConfig.git
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
Moonrise
|
||||
==
|
||||
[](https://modrinth.com/mod/moonrise-opt)
|
||||
[](https://www.curseforge.com/minecraft/mc-mods/moonrise)
|
||||
[](https://github.com/Tuinity/Moonrise/releases)
|
||||
[](LICENSE.md)
|
||||
|
||||
Fabric/NeoForge mod for optimising performance of the integrated (singleplayer/LAN) and dedicated server.
|
||||
|
||||
|
||||
@@ -19,7 +24,7 @@ patches. Listed below are notable patches:
|
||||
| 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> |
|
||||
| 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
|
||||
|
||||
1
YamlConfig
Submodule
1
YamlConfig
Submodule
Submodule YamlConfig added at 67552e7707
33
build.gradle
33
build.gradle
@@ -23,6 +23,7 @@ dependencies {
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
|
||||
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
|
||||
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
|
||||
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
|
||||
@@ -48,6 +49,7 @@ allprojects {
|
||||
mavenLocal {
|
||||
mavenContent {
|
||||
includeModule("ca.spottedleaf", "concurrentutil")
|
||||
includeModule("ca.spottedleaf", "yamlconfig")
|
||||
}
|
||||
}
|
||||
maven {
|
||||
@@ -112,18 +114,45 @@ subprojects {
|
||||
}
|
||||
changelog = providers.environmentVariable("RELEASE_NOTES")
|
||||
|
||||
List<String> supportedMcVersions = rootProject.supported_minecraft_versions.split(',')
|
||||
|
||||
modrinth {
|
||||
projectId = "KOHu7RCS"
|
||||
accessToken = providers.environmentVariable("MODRINTH_TOKEN")
|
||||
minecraftVersions = [rootProject.minecraft_version]
|
||||
minecraftVersions = supportedMcVersions
|
||||
}
|
||||
|
||||
curseforge {
|
||||
projectId = "1096335"
|
||||
accessToken = providers.environmentVariable("CURSEFORGE_TOKEN")
|
||||
minecraftVersions = [rootProject.minecraft_version]
|
||||
minecraftVersions = supportedMcVersions
|
||||
}
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||
}
|
||||
dependencies {
|
||||
String coordinates = "maven.modrinth:lithium:"
|
||||
if (getProject().name == "Moonrise-NeoForge") {
|
||||
coordinates += rootProject.neo_lithium_version
|
||||
} else {
|
||||
coordinates += rootProject.fabric_lithium_version
|
||||
}
|
||||
modLithiumRuntimeOnly coordinates
|
||||
}
|
||||
}
|
||||
|
||||
loom.runs.all {
|
||||
|
||||
@@ -18,6 +18,7 @@ dependencies {
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
|
||||
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
|
||||
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
|
||||
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||
@@ -25,6 +26,7 @@ dependencies {
|
||||
modImplementation "com.terraformersmc:modmenu:11.0.1"
|
||||
|
||||
modImplementation fabricApiLibs.command.api.v2
|
||||
modImplementation fabricApiLibs.lifecycle.events.v1
|
||||
include fabricApiLibs.command.api.v2
|
||||
include fabricApiLibs.base
|
||||
}
|
||||
@@ -42,6 +44,7 @@ shadowJar {
|
||||
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'
|
||||
}
|
||||
|
||||
@@ -59,26 +62,7 @@ publishMods {
|
||||
incompatible(
|
||||
"not-enough-crashes",
|
||||
"starlight",
|
||||
"c2me-fabric"
|
||||
"c2me"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||
}
|
||||
dependencies {
|
||||
modLithiumRuntimeOnly "maven.modrinth:lithium:${rootProject.lithium_version}"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ package ca.spottedleaf.moonrise.fabric;
|
||||
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 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.server.level.GenerationChunkHolder;
|
||||
@@ -15,6 +19,7 @@ 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.entity.EntityTypeTest;
|
||||
@@ -25,6 +30,21 @@ import java.util.function.Predicate;
|
||||
|
||||
public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public String getBrand() {
|
||||
return "Moonrise";
|
||||
@@ -44,7 +64,7 @@ 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
|
||||
@@ -69,7 +89,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
|
||||
@@ -84,7 +109,9 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk) {
|
||||
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel) chunk.getLevel(), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package ca.spottedleaf.moonrise.fabric.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
abstract class EntityMixin {
|
||||
|
||||
@Shadow
|
||||
public abstract boolean touchingUnloadedChunk();
|
||||
|
||||
@Shadow
|
||||
public abstract AABB getBoundingBox();
|
||||
|
||||
@Shadow
|
||||
@Deprecated
|
||||
public abstract boolean isPushedByFluid();
|
||||
|
||||
@Shadow
|
||||
private Level level;
|
||||
|
||||
@Shadow
|
||||
@Deprecated
|
||||
protected Object2DoubleMap<TagKey<Fluid>> fluidHeight;
|
||||
|
||||
@Shadow
|
||||
public abstract Vec3 getDeltaMovement();
|
||||
|
||||
@Shadow
|
||||
public abstract void setDeltaMovement(final Vec3 arg);
|
||||
|
||||
/**
|
||||
* @reason Optimise the block reading in this function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, final double flowScale) {
|
||||
if (this.touchingUnloadedChunk()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||
|
||||
final Level world = this.level;
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
|
||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||
|
||||
final boolean isPushable = this.isPushedByFluid();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
Vec3 pushVector = Vec3.ZERO;
|
||||
double totalPushes = 0.0;
|
||||
double maxHeightDiff = 0.0;
|
||||
boolean inFluid = false;
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
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();
|
||||
|
||||
// bound y
|
||||
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 FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
|
||||
|
||||
if (fluidState.isEmpty() || !fluidState.is(fluid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
|
||||
|
||||
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
|
||||
final double diff = height - boundingBox.minY;
|
||||
|
||||
if (diff < 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inFluid = true;
|
||||
maxHeightDiff = Math.max(maxHeightDiff, diff);
|
||||
|
||||
if (!isPushable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++totalPushes;
|
||||
|
||||
final Vec3 flow = fluidState.getFlow(world, mutablePos);
|
||||
|
||||
if (diff < 0.4) {
|
||||
pushVector = pushVector.add(flow.scale(diff));
|
||||
} else {
|
||||
pushVector = pushVector.add(flow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.fluidHeight.put(fluid, maxHeightDiff);
|
||||
|
||||
if (pushVector.lengthSqr() == 0.0) {
|
||||
return inFluid;
|
||||
}
|
||||
|
||||
// note: totalPushes != 0 as pushVector != 0
|
||||
pushVector = pushVector.scale(1.0 / totalPushes);
|
||||
final Vec3 currMovement = this.getDeltaMovement();
|
||||
|
||||
if (!((Entity)(Object)this instanceof Player)) {
|
||||
pushVector = pushVector.normalize();
|
||||
}
|
||||
|
||||
pushVector = pushVector.scale(flowScale);
|
||||
if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
|
||||
pushVector = pushVector.normalize().scale(0.0045000000000000005);
|
||||
}
|
||||
|
||||
this.setDeltaMovement(currMovement.add(pushVector));
|
||||
|
||||
// note: inFluid = true here as pushVector != 0
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -41,31 +41,38 @@
|
||||
},
|
||||
"custom": {
|
||||
"lithium:options": {
|
||||
"mixin.collections.chunk_tickets": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.collections.entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.ai.poi": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.world.tick_scheduler": false,
|
||||
"mixin.alloc.chunk_ticking": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.shapes.optimized_matching": 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.chunk.entity_class_groups": false,
|
||||
"mixin.alloc.entity_tracker": 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.explosions.cache_exposure": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.world.tick_scheduler": false
|
||||
},
|
||||
"ferritecore:disabled_options": [
|
||||
"replaceNeighborLookup",
|
||||
"replacePropertyMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"mixins": [
|
||||
"chunk_system.FabricDistanceManagerMixin",
|
||||
"chunk_system.FabricMinecraftServerMixin",
|
||||
"chunk_system.FabricServerLevelMixin"
|
||||
"chunk_system.FabricServerLevelMixin",
|
||||
"collisions.EntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"command.ClientSuggestionProviderMixin"
|
||||
|
||||
@@ -4,13 +4,17 @@ org.gradle.daemon=false
|
||||
# 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.42
|
||||
snakeyaml_version=2.2
|
||||
neoforge_version=21.1.79
|
||||
snakeyaml_version=2.3
|
||||
concurrentutil_version=0.0.2-SNAPSHOT
|
||||
yamlconfig_version=1.0.2-SNAPSHOT
|
||||
cloth_version=15.0.128
|
||||
lithium_version=mc1.21.1-0.13.0
|
||||
# version ids from modrinth
|
||||
fabric_lithium_version=frXUdgvL
|
||||
neo_lithium_version=KhdehJ6l
|
||||
# Mod Properties
|
||||
mod_version=0.1.0-beta.2
|
||||
mod_version=0.1.0-beta.10
|
||||
maven_group=ca.spottedleaf.moonrise
|
||||
archives_base_name=moonrise
|
||||
|
||||
@@ -2,5 +2,11 @@
|
||||
set -eou pipefail
|
||||
|
||||
git submodule update --init --recursive
|
||||
|
||||
cd ConcurrentUtil
|
||||
mvn install
|
||||
|
||||
cd ..
|
||||
|
||||
cd YamlConfig
|
||||
mvn install
|
||||
@@ -22,6 +22,7 @@ dependencies {
|
||||
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
|
||||
|
||||
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||
shadow("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
|
||||
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||
|
||||
@@ -42,6 +43,7 @@ shadowJar {
|
||||
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'
|
||||
}
|
||||
|
||||
@@ -54,16 +56,14 @@ publishMods {
|
||||
incompatible(
|
||||
"notenoughcrashes",
|
||||
"starlight-neoforge",
|
||||
"canary",
|
||||
"radium"
|
||||
"canary"
|
||||
)
|
||||
}
|
||||
curseforge {
|
||||
incompatible(
|
||||
"not-enough-crashes-forge",
|
||||
"starlight-neoforge",
|
||||
"canary",
|
||||
"radium-reforged"
|
||||
"canary"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package ca.spottedleaf.moonrise.neoforge.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.server.level.ChunkLevel;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
@@ -15,13 +15,13 @@ 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;
|
||||
|
||||
@Mixin(DistanceManager.class)
|
||||
abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
|
||||
@Unique
|
||||
private final ConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket<?>>> mtSafeForcedTickets = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
@Shadow
|
||||
@Final
|
||||
private Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> forcedTickets;
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system
|
||||
@@ -34,7 +34,7 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
if (forceTicks) {
|
||||
final Ticket<T> forceTicket = new Ticket<>(type, level, identifier, forceTicks);
|
||||
|
||||
this.mtSafeForcedTickets.compute(pos.toLong(), (final long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
this.forcedTickets.compute(pos.toLong(), (final Long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
final SortedArraySet<Ticket<?>> ret;
|
||||
if (valueInMap != null) {
|
||||
ret = valueInMap;
|
||||
@@ -42,7 +42,11 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
ret = SortedArraySet.create(4);
|
||||
}
|
||||
|
||||
ret.add(forceTicket);
|
||||
if (ret.add(forceTicket)) {
|
||||
((ChunkTickServerLevel)NeoForgeDistanceManagerMixin.this.moonrise$getChunkMap().level).moonrise$addPlayerTickingRequest(
|
||||
CoordinateUtils.getChunkX(keyInMap.longValue()), CoordinateUtils.getChunkZ(keyInMap.longValue())
|
||||
);
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
@@ -60,8 +64,12 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
if (forceTicks) {
|
||||
final Ticket<T> forceTicket = new Ticket<>(type, level, identifier, forceTicks);
|
||||
|
||||
this.mtSafeForcedTickets.computeIfPresent(pos.toLong(), (final long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
valueInMap.remove(forceTicket);
|
||||
this.forcedTickets.computeIfPresent(pos.toLong(), (final Long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
if (valueInMap.remove(forceTicket)) {
|
||||
((ChunkTickServerLevel)NeoForgeDistanceManagerMixin.this.moonrise$getChunkMap().level).moonrise$removePlayerTickingRequest(
|
||||
CoordinateUtils.getChunkX(keyInMap.longValue()), CoordinateUtils.getChunkZ(keyInMap.longValue())
|
||||
);
|
||||
}
|
||||
|
||||
return valueInMap.isEmpty() ? null : valueInMap;
|
||||
});
|
||||
@@ -69,11 +77,11 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Make this API thread-safe
|
||||
* @reason Only use containsKey, as we fix the leak with this impl
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean shouldForceTicks(final long chunkPos) {
|
||||
return this.mtSafeForcedTickets.containsKey(chunkPos);
|
||||
return this.forcedTickets.containsKey(chunkPos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
package ca.spottedleaf.moonrise.neoforge.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.neoforge.patches.collisions.FluidPushCalculation;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.neoforged.neoforge.common.extensions.IEntityExtension;
|
||||
import net.neoforged.neoforge.fluids.FluidType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import java.util.Iterator;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
abstract class EntityMixin implements IEntityExtension {
|
||||
|
||||
@Shadow
|
||||
public abstract boolean touchingUnloadedChunk();
|
||||
|
||||
@Shadow
|
||||
public abstract AABB getBoundingBox();
|
||||
|
||||
@Shadow
|
||||
private Level level;
|
||||
|
||||
@Shadow
|
||||
protected abstract void setFluidTypeHeight(final FluidType type, final double height);
|
||||
|
||||
@Shadow
|
||||
public abstract Vec3 getDeltaMovement();
|
||||
|
||||
@Shadow
|
||||
public abstract void setDeltaMovement(final Vec3 arg);
|
||||
|
||||
/**
|
||||
* @reason Optimise the block reading in this function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void updateFluidHeightAndDoFluidPushing() {
|
||||
if (this.touchingUnloadedChunk()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||
|
||||
final Level world = this.level;
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
// note: bounds are exclusive in Vanilla, so we subtract 1
|
||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
final Reference2ReferenceArrayMap<FluidType, FluidPushCalculation> calculations = new Reference2ReferenceArrayMap<>();
|
||||
|
||||
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();
|
||||
|
||||
// bound y
|
||||
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 FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
|
||||
|
||||
if (fluidState.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
|
||||
|
||||
final FluidType type = fluidState.getFluidType();
|
||||
|
||||
// note: assume fluidState.isEmpty() == type.isAir()
|
||||
|
||||
final FluidPushCalculation calculation = calculations.computeIfAbsent(type, (final FluidType key) -> {
|
||||
return new FluidPushCalculation();
|
||||
});
|
||||
|
||||
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
|
||||
final double diff = height - boundingBox.minY;
|
||||
|
||||
if (diff < 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
calculation.maxHeightDiff = Math.max(calculation.maxHeightDiff, diff);
|
||||
|
||||
if (calculation.isPushed == Boolean.FALSE) {
|
||||
continue;
|
||||
} else if (calculation.isPushed == null) {
|
||||
final boolean isPushed = this.isPushedByFluid(type);
|
||||
calculation.isPushed = Boolean.valueOf(isPushed);
|
||||
if (!isPushed) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++calculation.totalPushes;
|
||||
|
||||
final Vec3 flow = fluidState.getFlow(world, mutablePos);
|
||||
|
||||
if (diff < 0.4) {
|
||||
calculation.pushVector = calculation.pushVector.add(flow.scale(diff));
|
||||
} else {
|
||||
calculation.pushVector = calculation.pushVector.add(flow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (calculations.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Iterator<Reference2ReferenceMap.Entry<FluidType, FluidPushCalculation>> iterator = calculations.reference2ReferenceEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Reference2ReferenceMap.Entry<FluidType, FluidPushCalculation> entry = iterator.next();
|
||||
final FluidType type = entry.getKey();
|
||||
final FluidPushCalculation calculation = entry.getValue();
|
||||
|
||||
this.setFluidTypeHeight(type, calculation.maxHeightDiff);
|
||||
|
||||
Vec3 pushVector = calculation.pushVector;
|
||||
|
||||
if (pushVector.lengthSqr() == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// note: totalPushes != 0 as pushVector != 0
|
||||
pushVector = pushVector.scale(1.0 / calculation.totalPushes);
|
||||
final Vec3 currMovement = this.getDeltaMovement();
|
||||
|
||||
if (!((Entity)(Object)this instanceof Player)) {
|
||||
pushVector = pushVector.normalize();
|
||||
}
|
||||
|
||||
pushVector = pushVector.scale(this.getFluidMotionScale(type));
|
||||
if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
|
||||
pushVector = pushVector.normalize().scale(0.0045000000000000005);
|
||||
}
|
||||
|
||||
this.setDeltaMovement(currMovement.add(pushVector));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ca.spottedleaf.moonrise.neoforge.patches.collisions;
|
||||
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class FluidPushCalculation {
|
||||
|
||||
public Vec3 pushVector = Vec3.ZERO;
|
||||
public double totalPushes = 0.0;
|
||||
public double maxHeightDiff = 0.0;
|
||||
public Boolean isPushed = null;
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
@@ -36,20 +40,11 @@ type = "incompatible"
|
||||
modId = "starlight"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "lithium"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
# unofficial lithium port
|
||||
modId = "canary"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
# unofficial lithium port
|
||||
modId = "radium"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "c2me"
|
||||
type = "incompatible"
|
||||
@@ -59,3 +54,33 @@ config = "moonrise.mixins.json"
|
||||
|
||||
[[mixins]]
|
||||
config = "moonrise-neoforge.mixins.json"
|
||||
|
||||
["lithium:options"]
|
||||
"mixin.ai.poi" = false
|
||||
"mixin.alloc.chunk_ticking" = 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.explosions.cache_exposure" = false
|
||||
"mixin.world.temperature_cache" = false
|
||||
"mixin.world.tick_scheduler" = false
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"mixins": [
|
||||
"chunk_system.NeoForgeDistanceManagerMixin",
|
||||
"chunk_system.NeoForgeMinecraftServerMixin",
|
||||
"chunk_system.NeoForgeServerLevelMixin"
|
||||
"chunk_system.NeoForgeServerLevelMixin",
|
||||
"collisions.EntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"command.ClientCommandSourceStackMixin"
|
||||
|
||||
@@ -24,7 +24,7 @@ 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("xyz.jpenilla.quiet-architectury-loom") version "1.7.300" apply false
|
||||
id 'com.gradleup.shadow' version '8.3.0' apply false
|
||||
}
|
||||
|
||||
@@ -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:0.107.0+1.21.1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,13 @@ public final class IntList {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
public void setMinCapacity(final int len) {
|
||||
final int[] byIndex = this.byIndex;
|
||||
if (byIndex.length < len) {
|
||||
this.byIndex = Arrays.copyOf(byIndex, len);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRaw(final int index) {
|
||||
return this.byIndex[index];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package ca.spottedleaf.moonrise.common.list;
|
||||
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class ShortList {
|
||||
|
||||
private final Short2ShortOpenHashMap map = new Short2ShortOpenHashMap();
|
||||
{
|
||||
this.map.defaultReturnValue(Short.MIN_VALUE);
|
||||
}
|
||||
|
||||
private static final short[] EMPTY_LIST = new short[0];
|
||||
|
||||
private short[] byIndex = EMPTY_LIST;
|
||||
private short count;
|
||||
|
||||
public int size() {
|
||||
return (int)this.count;
|
||||
}
|
||||
|
||||
public short getRaw(final int index) {
|
||||
return this.byIndex[index];
|
||||
}
|
||||
|
||||
public void setMinCapacity(final int len) {
|
||||
final short[] byIndex = this.byIndex;
|
||||
if (byIndex.length < len) {
|
||||
this.byIndex = Arrays.copyOf(byIndex, len);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(final short value) {
|
||||
final int count = (int)this.count;
|
||||
final short currIndex = this.map.putIfAbsent(value, (short)count);
|
||||
|
||||
if (currIndex != Short.MIN_VALUE) {
|
||||
return false; // already in this list
|
||||
}
|
||||
|
||||
short[] list = this.byIndex;
|
||||
|
||||
if (list.length == count) {
|
||||
// resize required
|
||||
list = this.byIndex = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
|
||||
}
|
||||
|
||||
list[count] = value;
|
||||
this.count = (short)(count + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean remove(final short value) {
|
||||
final short index = this.map.remove(value);
|
||||
if (index == Short.MIN_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// move the entry at the end to this index
|
||||
final short endIndex = --this.count;
|
||||
final short end = this.byIndex[endIndex];
|
||||
if (index != endIndex) {
|
||||
// not empty after this call
|
||||
this.map.put(end, index);
|
||||
}
|
||||
this.byIndex[(int)index] = end;
|
||||
this.byIndex[(int)endIndex] = (short)0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.count = (short)0;
|
||||
this.map.clear();
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,17 @@ 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;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class NearbyPlayers {
|
||||
|
||||
@@ -20,7 +24,27 @@ public final class NearbyPlayers {
|
||||
GENERAL_REALLY_SMALL,
|
||||
TICK_VIEW_DISTANCE,
|
||||
VIEW_DISTANCE,
|
||||
SPAWN_RANGE, // Moonrise - chunk tick iteration
|
||||
// Moonrise start - chunk tick iteration
|
||||
SPAWN_RANGE {
|
||||
@Override
|
||||
void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
((ChunkTickServerLevel)world).moonrise$addPlayerTickingRequest(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
((ChunkTickServerLevel)world).moonrise$removePlayerTickingRequest(chunkX, chunkZ);
|
||||
}
|
||||
};
|
||||
// Moonrise end - chunk tick iteration
|
||||
|
||||
void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
|
||||
}
|
||||
|
||||
void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static final NearbyMapType[] MAP_TYPES = NearbyMapType.values();
|
||||
@@ -37,6 +61,12 @@ public final class NearbyPlayers {
|
||||
private final ServerLevel world;
|
||||
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
|
||||
private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
|
||||
private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
|
||||
{
|
||||
for (int i = 0; i < this.directByChunk.length; ++i) {
|
||||
this.directByChunk[i] = new Long2ReferenceOpenHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public NearbyPlayers(final ServerLevel world) {
|
||||
this.world = world;
|
||||
@@ -70,6 +100,16 @@ public final class NearbyPlayers {
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (this.players.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final ServerPlayer player : new ArrayList<>(this.players.keySet())) {
|
||||
this.removePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void tickPlayer(final ServerPlayer player) {
|
||||
final TrackedPlayer[] players = this.players.get(player);
|
||||
if (players == null) {
|
||||
@@ -82,7 +122,7 @@ public final class NearbyPlayers {
|
||||
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.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getViewDistance(player));
|
||||
players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
@@ -94,38 +134,41 @@ public final class NearbyPlayers {
|
||||
return this.byChunk.get(CoordinateUtils.getChunkKey(pos));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
|
||||
public TrackedChunk getChunk(final int chunkX, final int chunkZ) {
|
||||
return this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayers(final ChunkPos pos, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayersByBlock(final int blockX, final int blockZ, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
|
||||
}
|
||||
|
||||
public static final class TrackedChunk {
|
||||
|
||||
private static final ServerPlayer[] EMPTY_PLAYERS_ARRAY = new ServerPlayer[0];
|
||||
|
||||
private final long chunkKey;
|
||||
private final NearbyPlayers nearbyPlayers;
|
||||
private final ReferenceList<ServerPlayer>[] players = new ReferenceList[TOTAL_MAP_TYPES];
|
||||
private int nonEmptyLists;
|
||||
private long updateCount;
|
||||
|
||||
public TrackedChunk(final long chunkKey, final NearbyPlayers nearbyPlayers) {
|
||||
this.chunkKey = chunkKey;
|
||||
this.nearbyPlayers = nearbyPlayers;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.nonEmptyLists == 0;
|
||||
}
|
||||
@@ -145,7 +188,10 @@ public final class NearbyPlayers {
|
||||
final ReferenceList<ServerPlayer> list = this.players[idx];
|
||||
if (list == null) {
|
||||
++this.nonEmptyLists;
|
||||
(this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY)).add(player);
|
||||
final ReferenceList<ServerPlayer> players = (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY));
|
||||
this.nearbyPlayers.directByChunk[idx].put(this.chunkKey, players);
|
||||
players.add(player);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,6 +215,7 @@ public final class NearbyPlayers {
|
||||
|
||||
if (list.size() == 0) {
|
||||
this.players[idx] = null;
|
||||
this.nearbyPlayers.directByChunk[idx].remove(this.chunkKey);
|
||||
--this.nonEmptyLists;
|
||||
}
|
||||
}
|
||||
@@ -187,9 +234,19 @@ public final class NearbyPlayers {
|
||||
protected void addCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) {
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
|
||||
NearbyPlayers.this.byChunk.computeIfAbsent(chunkKey, (final long keyInMap) -> {
|
||||
return new TrackedChunk();
|
||||
}).addPlayer(parameter, this.type);
|
||||
final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey);
|
||||
final NearbyMapType type = this.type;
|
||||
if (chunk != null) {
|
||||
chunk.addPlayer(parameter, type);
|
||||
type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
|
||||
} else {
|
||||
final TrackedChunk created = new TrackedChunk(chunkKey, NearbyPlayers.this);
|
||||
NearbyPlayers.this.byChunk.put(chunkKey, created);
|
||||
created.addPlayer(parameter, type);
|
||||
type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
|
||||
|
||||
((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$requestChunkData(chunkKey).nearbyPlayers = created;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,10 +258,16 @@ public final class NearbyPlayers {
|
||||
throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey));
|
||||
}
|
||||
|
||||
chunk.removePlayer(parameter, this.type);
|
||||
final NearbyMapType type = this.type;
|
||||
chunk.removePlayer(parameter, type);
|
||||
type.removeFrom(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
|
||||
|
||||
if (chunk.isEmpty()) {
|
||||
NearbyPlayers.this.byChunk.remove(chunkKey);
|
||||
final ChunkData chunkData = ((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$releaseChunkData(chunkKey);
|
||||
if (chunkData != null) {
|
||||
chunkData.nearbyPlayers = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
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 net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
@@ -121,12 +122,14 @@ public final class ChunkSystem {
|
||||
}
|
||||
((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) {
|
||||
((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) {
|
||||
@@ -149,8 +152,8 @@ public final class ChunkSystem {
|
||||
return RegionizedPlayerChunkLoader.getAPISendViewDistance(player);
|
||||
}
|
||||
|
||||
public static int getLoadViewDistance(final ServerPlayer player) {
|
||||
return RegionizedPlayerChunkLoader.getLoadViewDistance(player);
|
||||
public static int getViewDistance(final ServerPlayer player) {
|
||||
return RegionizedPlayerChunkLoader.getAPIViewDistance(player);
|
||||
}
|
||||
|
||||
public static int getTickViewDistance(final ServerPlayer player) {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
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.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;
|
||||
@@ -17,8 +15,6 @@ public final class ConfigHolder {
|
||||
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) {
|
||||
@@ -35,6 +31,7 @@ public final class ConfigHolder {
|
||||
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.
|
||||
""";
|
||||
|
||||
static {
|
||||
@@ -50,14 +47,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 {
|
||||
@@ -68,6 +57,8 @@ public final class ConfigHolder {
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG.callInitialisers();
|
||||
|
||||
// write back any changes, or create if needed
|
||||
return saveConfig();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
public final class MoonriseConstants {
|
||||
|
||||
public static final int MAX_VIEW_DISTANCE = 32;
|
||||
public static final int MAX_VIEW_DISTANCE = Integer.getInteger("Moonrise.MaxViewDistance", 32);
|
||||
|
||||
private MoonriseConstants() {}
|
||||
|
||||
|
||||
@@ -11,10 +11,18 @@ public final class WorldUtil {
|
||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
||||
}
|
||||
|
||||
public static int getMaxSection(final Level world) {
|
||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
||||
}
|
||||
|
||||
public static int getMinSection(final LevelHeightAccessor world) {
|
||||
return world.getMinSection();
|
||||
}
|
||||
|
||||
public static int getMinSection(final Level world) {
|
||||
return world.getMinSection();
|
||||
}
|
||||
|
||||
public static int getMaxLightSection(final LevelHeightAccessor world) {
|
||||
return getMaxSection(world) + 1;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -18,15 +19,15 @@ interface BitStorageMixin extends BlockCountingBitStorage {
|
||||
|
||||
// provide default impl in case mods implement this...
|
||||
@Override
|
||||
public default Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>();
|
||||
public default Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
final int size = this.getSize();
|
||||
for (int index = 0; index < size; ++index) {
|
||||
final int paletteIdx = this.get(index);
|
||||
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
||||
return new IntArrayList();
|
||||
}).add(index);
|
||||
return new ShortArrayList(64);
|
||||
}).add((short)index);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
@@ -48,26 +48,32 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
public abstract boolean maybeHas(Predicate<BlockState> predicate);
|
||||
|
||||
@Unique
|
||||
private static final IntArrayList FULL_LIST = new IntArrayList(16*16*16);
|
||||
private static final ShortArrayList FULL_LIST = new ShortArrayList(16*16*16);
|
||||
static {
|
||||
for (int i = 0; i < (16*16*16); ++i) {
|
||||
for (short i = 0; i < (16*16*16); ++i) {
|
||||
FULL_LIST.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private int specialCollidingBlocks;
|
||||
private boolean isClient;
|
||||
|
||||
@Unique
|
||||
private final IntList tickingBlocks = new IntList();
|
||||
private static final short CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS = (short)9999;
|
||||
|
||||
@Unique
|
||||
private short specialCollidingBlocks;
|
||||
|
||||
@Unique
|
||||
private final ShortList tickingBlocks = new ShortList();
|
||||
|
||||
@Override
|
||||
public final int moonrise$getSpecialCollidingBlocks() {
|
||||
return this.specialCollidingBlocks;
|
||||
public final boolean moonrise$hasSpecialCollidingBlocks() {
|
||||
return this.specialCollidingBlocks != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IntList moonrise$getTickingBlockList() {
|
||||
public final ShortList moonrise$getTickingBlockList() {
|
||||
return this.tickingBlocks;
|
||||
}
|
||||
|
||||
@@ -86,20 +92,35 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
if (oldState == newState) {
|
||||
return;
|
||||
}
|
||||
if (CollisionUtil.isSpecialCollidingBlock(oldState)) {
|
||||
--this.specialCollidingBlocks;
|
||||
}
|
||||
|
||||
if (this.isClient) {
|
||||
if (CollisionUtil.isSpecialCollidingBlock(newState)) {
|
||||
this.specialCollidingBlocks = CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isSpecialOld = CollisionUtil.isSpecialCollidingBlock(oldState);
|
||||
final boolean isSpecialNew = CollisionUtil.isSpecialCollidingBlock(newState);
|
||||
if (isSpecialOld != isSpecialNew) {
|
||||
if (isSpecialOld) {
|
||||
--this.specialCollidingBlocks;
|
||||
} else {
|
||||
++this.specialCollidingBlocks;
|
||||
}
|
||||
|
||||
final int position = x | (z << 4) | (y << (4+4));
|
||||
|
||||
if (oldState.isRandomlyTicking()) {
|
||||
this.tickingBlocks.remove(position);
|
||||
}
|
||||
if (newState.isRandomlyTicking()) {
|
||||
this.tickingBlocks.add(position);
|
||||
|
||||
final boolean oldTicking = oldState.isRandomlyTicking();
|
||||
final boolean newTicking = newState.isRandomlyTicking();
|
||||
if (oldTicking != newTicking) {
|
||||
final ShortList tickingBlocks = this.tickingBlocks;
|
||||
final short position = (short)(x | (z << 4) | (y << (4+4)));
|
||||
|
||||
if (oldTicking) {
|
||||
tickingBlocks.remove(position);
|
||||
} else {
|
||||
tickingBlocks.add(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +158,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
final int paletteSize = palette.getSize();
|
||||
final BitStorage storage = data.storage;
|
||||
|
||||
final Int2ObjectOpenHashMap<IntArrayList> counts;
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> counts;
|
||||
if (paletteSize == 1) {
|
||||
counts = new Int2ObjectOpenHashMap<>(1);
|
||||
counts.put(0, FULL_LIST);
|
||||
@@ -145,10 +166,10 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
counts = ((BlockCountingBitStorage)storage).moonrise$countEntries();
|
||||
}
|
||||
|
||||
for (final Iterator<Int2ObjectMap.Entry<IntArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Int2ObjectMap.Entry<IntArrayList> entry = iterator.next();
|
||||
for (final Iterator<Int2ObjectMap.Entry<ShortArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Int2ObjectMap.Entry<ShortArrayList> entry = iterator.next();
|
||||
final int paletteIdx = entry.getIntKey();
|
||||
final IntArrayList coordinates = entry.getValue();
|
||||
final ShortArrayList coordinates = entry.getValue();
|
||||
final int paletteCount = coordinates.size();
|
||||
|
||||
final BlockState state = palette.valueFor(paletteIdx);
|
||||
@@ -158,16 +179,21 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
}
|
||||
|
||||
if (CollisionUtil.isSpecialCollidingBlock(state)) {
|
||||
this.specialCollidingBlocks += paletteCount;
|
||||
this.specialCollidingBlocks += (short)paletteCount;
|
||||
}
|
||||
this.nonEmptyBlockCount += paletteCount;
|
||||
this.nonEmptyBlockCount += (short)paletteCount;
|
||||
if (state.isRandomlyTicking()) {
|
||||
this.tickingBlockCount += paletteCount;
|
||||
final int[] raw = coordinates.elements();
|
||||
this.tickingBlockCount += (short)paletteCount;
|
||||
final short[] raw = coordinates.elements();
|
||||
final int rawLen = raw.length;
|
||||
|
||||
Objects.checkFromToIndex(0, paletteCount, raw.length);
|
||||
final ShortList tickingBlocks = this.tickingBlocks;
|
||||
|
||||
tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16));
|
||||
|
||||
Objects.checkFromToIndex(0, paletteCount, rawLen);
|
||||
for (int i = 0; i < paletteCount; ++i) {
|
||||
this.tickingBlocks.add(raw[i]);
|
||||
tickingBlocks.add(raw[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +202,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
if (!fluid.isEmpty()) {
|
||||
//this.nonEmptyBlockCount += count; // fix vanilla bug: make non-empty block count correct
|
||||
if (fluid.isRandomlyTicking()) {
|
||||
this.tickingFluidCount += paletteCount;
|
||||
this.tickingFluidCount += (short)paletteCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +210,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Call recalcBlockCounts on the client, as the client does not invoke it when deserializing chunk sections.
|
||||
* @reason Set up special colliding blocks on the client, as it is too expensive to perform a full calculation
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
@@ -194,6 +220,8 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
)
|
||||
)
|
||||
private void callRecalcBlocksClient(final CallbackInfo ci) {
|
||||
this.recalcBlockCounts();
|
||||
this.isClient = true;
|
||||
// force has special colliding blocks to be true
|
||||
this.specialCollidingBlocks = this.nonEmptyBlockCount != (short)0 && this.maybeHas(CollisionUtil::isSpecialCollidingBlock) ? CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS : (short)0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -24,24 +25,48 @@ abstract class SimpleBitStorageMixin implements BitStorage, BlockCountingBitStor
|
||||
@Final
|
||||
private int bits;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private long mask;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private int size;
|
||||
|
||||
@Override
|
||||
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final int valuesPerLong = this.valuesPerLong;
|
||||
final int bits = this.bits;
|
||||
final long mask = this.mask;
|
||||
final long mask = (1L << bits) - 1L;
|
||||
final int size = this.size;
|
||||
|
||||
// we may be backed by global palette, so limit bits for init capacity
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(
|
||||
1 << Math.min(6, bits)
|
||||
if (bits <= 6) {
|
||||
final ShortArrayList[] byId = new ShortArrayList[1 << bits];
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1 << bits);
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (long value : this.data) {
|
||||
int li = 0;
|
||||
do {
|
||||
final int paletteIdx = (int)(value & mask);
|
||||
value >>= bits;
|
||||
++li;
|
||||
|
||||
final ShortArrayList coords = byId[paletteIdx];
|
||||
if (coords != null) {
|
||||
coords.add((short)index++);
|
||||
continue;
|
||||
} else {
|
||||
final ShortArrayList newCoords = new ShortArrayList(64);
|
||||
byId[paletteIdx] = newCoords;
|
||||
newCoords.add((short)index++);
|
||||
ret.put(paletteIdx, newCoords);
|
||||
continue;
|
||||
}
|
||||
} while (li < valuesPerLong && index < size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(
|
||||
1 << 6
|
||||
);
|
||||
|
||||
int index = 0;
|
||||
@@ -51,16 +76,15 @@ abstract class SimpleBitStorageMixin implements BitStorage, BlockCountingBitStor
|
||||
do {
|
||||
final int paletteIdx = (int)(value & mask);
|
||||
value >>= bits;
|
||||
++li;
|
||||
|
||||
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
||||
return new IntArrayList();
|
||||
}).add(index);
|
||||
|
||||
++li;
|
||||
++index;
|
||||
return new ShortArrayList(64);
|
||||
}).add((short)index++);
|
||||
} while (li < valuesPerLong && index < size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.ZeroBitStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -17,17 +18,17 @@ abstract class ZeroBitStorageMixin implements BitStorage, BlockCountingBitStorag
|
||||
private int size;
|
||||
|
||||
@Override
|
||||
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final int size = this.size;
|
||||
|
||||
final int[] raw = new int[size];
|
||||
final short[] raw = new short[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
raw[i] = i;
|
||||
raw[i] = (short)i;
|
||||
}
|
||||
|
||||
final IntArrayList coordinates = IntArrayList.wrap(raw, size);
|
||||
final ShortArrayList coordinates = ShortArrayList.wrap(raw, size);
|
||||
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(1);
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1);
|
||||
ret.put(0, coordinates);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,9 @@ 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;
|
||||
@@ -120,6 +123,16 @@ 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];
|
||||
|
||||
@@ -287,7 +300,14 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
// no players to sent to, so don't need to update anything
|
||||
return null;
|
||||
}
|
||||
return this.getChunkToSend();
|
||||
final LevelChunk ret = this.getChunkToSend();
|
||||
|
||||
if (ret != null) {
|
||||
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,7 +322,18 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
)
|
||||
)
|
||||
private LevelChunk redirectLightUpdate(final ChunkHolder instance) {
|
||||
return this.getChunkToSend();
|
||||
if (this.playersSentChunkTo.size() == 0) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,6 +63,21 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
||||
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ClientChunkCache chunkSource = this.chunkSource;
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Redirect to new entity manager
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -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;
|
||||
@@ -13,6 +14,7 @@ import net.minecraft.server.level.DistanceManager;
|
||||
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;
|
||||
@@ -296,7 +298,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -49,6 +50,9 @@ abstract class EntityMixin implements ChunkSystemEntity {
|
||||
@Unique
|
||||
private FullChunkStatus chunkStatus;
|
||||
|
||||
@Unique
|
||||
private ChunkData chunkData;
|
||||
|
||||
@Unique
|
||||
private int sectionX = Integer.MIN_VALUE;
|
||||
|
||||
@@ -76,6 +80,16 @@ abstract class EntityMixin implements ChunkSystemEntity {
|
||||
this.chunkStatus = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData() {
|
||||
return this.chunkData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$setChunkData(final ChunkData chunkData) {
|
||||
this.chunkData = chunkData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getSectionX() {
|
||||
return this.sectionX;
|
||||
|
||||
@@ -99,8 +99,18 @@ abstract class EntityTickListMixin {
|
||||
* @reason Route to new entity list
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void forEach(final Consumer<Entity> action) {
|
||||
@Inject(
|
||||
method = "forEach",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void injectForEach(final Consumer<Entity> consumer, final CallbackInfo ci) {
|
||||
this.forEach(consumer);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void forEach(final Consumer<Entity> action) {
|
||||
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
|
||||
// (by dfl iterator() is configured to not iterate over new entries)
|
||||
final IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = this.entities.iterator();
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup;
|
||||
@@ -13,6 +16,7 @@ import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
@@ -46,6 +50,9 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
@Unique
|
||||
private EntityLookup entityLookup;
|
||||
|
||||
@Unique
|
||||
private final ConcurrentLong2ReferenceChainedHashTable<ChunkData> chunkData = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
|
||||
@Override
|
||||
public final EntityLookup moonrise$getEntityLookup() {
|
||||
return this.entityLookup;
|
||||
@@ -217,6 +224,52 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
// no-op on ClientLevel
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData(final long chunkKey) {
|
||||
return this.chunkData.get(chunkKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData(final int chunkX, final int chunkZ) {
|
||||
return this.chunkData.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$requestChunkData(final long chunkKey) {
|
||||
return this.chunkData.compute(chunkKey, (final long keyInMap, final ChunkData valueInMap) -> {
|
||||
if (valueInMap == null) {
|
||||
final ChunkData ret = new ChunkData();
|
||||
ret.increaseRef();
|
||||
return ret;
|
||||
}
|
||||
|
||||
valueInMap.increaseRef();
|
||||
return valueInMap;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$releaseChunkData(final long chunkKey) {
|
||||
return this.chunkData.compute(chunkKey, (final long keyInMap, final ChunkData chunkData) -> {
|
||||
return chunkData.decreaseRef() == 0 ? null : chunkData;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ChunkSource chunkSource = this.getChunkSource();
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Declare method in this class so that any invocations are virtual, and not interface.
|
||||
* @author Spottedleaf
|
||||
@@ -226,6 +279,13 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
return this.getChunkSource().hasChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChunksAt(final int minBlockX, final int minBlockZ, final int maxBlockX, final int maxBlockZ) {
|
||||
return this.moonrise$areChunksLoaded(
|
||||
minBlockX >> 4, minBlockZ >> 4, maxBlockX >> 4, maxBlockZ >> 4
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Turn all getChunk(x, z, status) calls into virtual invokes, instead of interface invokes:
|
||||
* 1. The interface invoke is expensive
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
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.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
@@ -33,6 +34,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -282,7 +285,8 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
*/
|
||||
@Overwrite
|
||||
public void getFullChunk(final long pos, final Consumer<LevelChunk> consumer) {
|
||||
final LevelChunk fullChunk = this.getChunkNow(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
|
||||
// note: bypass currentlyLoaded from getChunkNow
|
||||
final LevelChunk fullChunk = this.fullChunks.get(pos);
|
||||
if (fullChunk != null) {
|
||||
consumer.accept(fullChunk);
|
||||
}
|
||||
@@ -356,4 +360,35 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityData
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.PoiDataController;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
||||
@@ -26,6 +27,7 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
@@ -62,6 +64,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -120,6 +123,9 @@ 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);
|
||||
|
||||
@@ -129,6 +135,9 @@ 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
|
||||
@@ -345,6 +354,60 @@ 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;
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Declare method in this class so that any invocations are virtual, and not interface.
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -33,7 +33,7 @@ abstract class ChunkMapMixin {
|
||||
public ServerLevel level;
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean playerIsCloseEnoughForSpawning(ServerPlayer serverPlayer, ChunkPos chunkPos);
|
||||
public abstract boolean playerIsCloseEnoughForSpawning(ServerPlayer serverPlayer, ChunkPos chunkPos);
|
||||
|
||||
/**
|
||||
* @reason Hook for updating the spawn tracker in distance manager. We add our own hook instead of using the
|
||||
@@ -116,15 +116,15 @@ abstract class ChunkMapMixin {
|
||||
*/
|
||||
@Overwrite
|
||||
public List<ServerPlayer> getPlayersCloseForSpawning(final ChunkPos pos) {
|
||||
final List<ServerPlayer> ret = new ArrayList<>();
|
||||
|
||||
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
|
||||
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
|
||||
);
|
||||
if (players == null) {
|
||||
return ret;
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<ServerPlayer> ret = null;
|
||||
|
||||
final ServerPlayer[] raw = players.getRawDataUnchecked();
|
||||
final int len = players.size();
|
||||
|
||||
@@ -132,10 +132,15 @@ abstract class ChunkMapMixin {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
final ServerPlayer player = raw[i];
|
||||
if (this.playerIsCloseEnoughForSpawning(player, pos)) {
|
||||
if (ret == null) {
|
||||
ret = new ArrayList<>(len - i);
|
||||
ret.add(player);
|
||||
} else {
|
||||
ret.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret == null ? new ArrayList<>() : ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
|
||||
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;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
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.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;
|
||||
@@ -30,6 +45,12 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
@Unique
|
||||
private ServerChunkCache.ChunkAndHolder[] iterationCopy;
|
||||
|
||||
@Unique
|
||||
private int iterationCopyLen;
|
||||
|
||||
@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.
|
||||
@@ -60,7 +81,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
)
|
||||
private List<ServerChunkCache.ChunkAndHolder> initTickChunks(final List<ServerChunkCache.ChunkAndHolder> shouldBeNull) {
|
||||
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||
((ChunkSystemServerLevel)this.level).moonrise$getTickingChunks();
|
||||
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
||||
|
||||
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
||||
final int size = tickingChunks.size();
|
||||
@@ -68,6 +89,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
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(
|
||||
@@ -75,6 +97,23 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -92,29 +131,59 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Clear the iteration array, and at the same time broadcast chunk changes.
|
||||
* @reason Use the nearby players cache
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
||||
ordinal = 0
|
||||
target = "Lnet/minecraft/server/level/ChunkMap;anyPlayerCloseEnoughForSpawning(Lnet/minecraft/world/level/ChunkPos;)Z"
|
||||
)
|
||||
)
|
||||
private void broadcastChanges(final List<ServerChunkCache.ChunkAndHolder> instance,
|
||||
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
|
||||
final ObjectArrayList<ServerChunkCache.ChunkAndHolder> chunks = (ObjectArrayList<ServerChunkCache.ChunkAndHolder>)instance;
|
||||
final ServerChunkCache.ChunkAndHolder[] raw = chunks.elements();
|
||||
final int size = chunks.size();
|
||||
|
||||
Objects.checkFromToIndex(0, size, raw.length);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final ServerChunkCache.ChunkAndHolder holder = raw[i];
|
||||
raw[i] = null;
|
||||
|
||||
holder.holder().broadcastChanges(holder.chunk());
|
||||
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())
|
||||
.moonrise$getRealChunkHolder().holderData;
|
||||
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
||||
if (nearbyPlayers == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ReferenceList<ServerPlayer> players = nearbyPlayers.getPlayers(NearbyPlayers.NearbyMapType.SPAWN_RANGE);
|
||||
|
||||
if (players == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerPlayer[] raw = players.getRawDataUnchecked();
|
||||
final int len = players.size();
|
||||
|
||||
Objects.checkFromIndexSize(0, len, raw.length);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (instance.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Clear the iteration array after the list is done being used.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
||||
ordinal = 0,
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
private void broadcastChanges(final CallbackInfo ci) {
|
||||
Arrays.fill(this.iterationCopy, 0, this.iterationCopyLen, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
abstract class ServerLevelMixin implements ChunkTickServerLevel {
|
||||
|
||||
@Unique
|
||||
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_PLAYER_CHUNK_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
|
||||
|
||||
@Unique
|
||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> playerTickingChunks = new ReferenceList<>(EMPTY_PLAYER_CHUNK_HOLDERS);
|
||||
|
||||
@Unique
|
||||
private final Long2IntOpenHashMap playerTickingRequests = new Long2IntOpenHashMap();
|
||||
|
||||
@Override
|
||||
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks() {
|
||||
return this.playerTickingChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$markChunkForPlayerTicking(final LevelChunk chunk) {
|
||||
final ChunkPos pos = chunk.getPos();
|
||||
if (!this.playerTickingRequests.containsKey(CoordinateUtils.getChunkKey(pos))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.add(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removeChunkForPlayerTicking(final LevelChunk chunk) {
|
||||
this.playerTickingChunks.remove(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$addPlayerTickingRequest(final int chunkX, final int chunkZ) {
|
||||
TickThread.ensureTickThread((ServerLevel)(Object)this, chunkX, chunkZ, "Cannot add ticking request async");
|
||||
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
|
||||
if (this.playerTickingRequests.addTo(chunkKey, 1) != 0) {
|
||||
// already added
|
||||
return;
|
||||
}
|
||||
|
||||
final NewChunkHolder chunkHolder = ((ChunkSystemServerLevel)(ServerLevel)(Object)this).moonrise$getChunkTaskScheduler()
|
||||
.chunkHolderManager.getChunkHolder(chunkKey);
|
||||
|
||||
if (chunkHolder == null || !chunkHolder.isTickingReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.add(
|
||||
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removePlayerTickingRequest(final int chunkX, final int chunkZ) {
|
||||
TickThread.ensureTickThread((ServerLevel)(Object)this, chunkX, chunkZ, "Cannot remove ticking request async");
|
||||
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
final int val = this.playerTickingRequests.addTo(chunkKey, -1);
|
||||
|
||||
if (val <= 0) {
|
||||
throw new IllegalStateException("Negative counter");
|
||||
}
|
||||
|
||||
if (val != 1) {
|
||||
// still has at least one request
|
||||
return;
|
||||
}
|
||||
|
||||
final NewChunkHolder chunkHolder = ((ChunkSystemServerLevel)(ServerLevel)(Object)this).moonrise$getChunkTaskScheduler()
|
||||
.chunkHolderManager.getChunkHolder(chunkKey);
|
||||
|
||||
if (chunkHolder == null || !chunkHolder.isTickingReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.remove(
|
||||
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,14 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(BlockPos.class)
|
||||
abstract class BlockPosMixin extends Vec3i {
|
||||
|
||||
@Shadow
|
||||
public abstract BlockPos immutable();
|
||||
|
||||
public BlockPosMixin(int i, int j, int k) {
|
||||
super(i, j, k);
|
||||
}
|
||||
@@ -29,7 +33,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos above(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY() + distance, this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() + distance, this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +53,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos below(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY() - distance, this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() - distance, this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +73,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos north(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY(), this.getZ() - distance);
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() - distance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +93,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos south(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY(), this.getZ() + distance);
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() + distance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +113,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos west(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX() - distance, this.getY(), this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX() - distance, this.getY(), this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +133,6 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos east(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX() + distance, this.getY(), this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX() + distance, this.getY(), this.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 @@ 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();
|
||||
|
||||
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
|
||||
super(object, reference2ObjectArrayMap, mapCodec);
|
||||
@@ -93,10 +89,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();
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
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.ChunkPos;
|
||||
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.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -23,8 +27,11 @@ 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 {
|
||||
@@ -48,7 +55,13 @@ abstract class EntityMixin {
|
||||
public abstract Vec3 getEyePosition();
|
||||
|
||||
@Shadow
|
||||
public abstract boolean onGround();
|
||||
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,
|
||||
@@ -144,9 +157,9 @@ abstract class EntityMixin {
|
||||
|
||||
final boolean collidedDownwards = collidedY && movement.y < 0.0;
|
||||
|
||||
final double stepHeight = (double)this.maxUpStep();
|
||||
final double stepHeight;
|
||||
|
||||
if (stepHeight <= 0.0 || (!collidedDownwards && !this.onGround()) || (!collidedX && !collidedZ)) {
|
||||
if ((!collidedDownwards && !this.onGround) || (!collidedX && !collidedZ) || (stepHeight = (double)this.maxUpStep()) <= 0.0) {
|
||||
return collided;
|
||||
}
|
||||
|
||||
@@ -184,51 +197,83 @@ abstract class EntityMixin {
|
||||
return false;
|
||||
}
|
||||
|
||||
final float reducedWith = this.dimensions.width() * 0.8F;
|
||||
final AABB box = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
||||
final double reducedWith = (double)(this.dimensions.width() * 0.8F);
|
||||
final AABB boundingBox = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
||||
final Level world = this.level;
|
||||
|
||||
if (CollisionUtil.isEmpty(box)) {
|
||||
if (CollisionUtil.isEmpty(boundingBox)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final BlockPos.MutableBlockPos tempPos = new BlockPos.MutableBlockPos();
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Mth.floor(boundingBox.minY);
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
final int minX = Mth.floor(box.minX);
|
||||
final int minY = Mth.floor(box.minY);
|
||||
final int minZ = Mth.floor(box.minZ);
|
||||
final int maxX = Mth.floor(box.maxX);
|
||||
final int maxY = Mth.floor(box.maxY);
|
||||
final int maxZ = Mth.floor(box.maxZ);
|
||||
final int maxBlockX = Mth.floor(boundingBox.maxX);
|
||||
final int maxBlockY = Mth.floor(boundingBox.maxY);
|
||||
final int maxBlockZ = Mth.floor(boundingBox.maxZ);
|
||||
|
||||
final ChunkSource chunkProvider = this.level.getChunkSource();
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
|
||||
long lastChunkKey = ChunkPos.INVALID_CHUNK_POS;
|
||||
LevelChunk lastChunk = null;
|
||||
for (int fz = minZ; fz <= maxZ; ++fz) {
|
||||
tempPos.setZ(fz);
|
||||
for (int fx = minX; fx <= maxX; ++fx) {
|
||||
final int newChunkX = fx >> 4;
|
||||
final int newChunkZ = fz >> 4;
|
||||
final LevelChunk chunk = lastChunkKey == (lastChunkKey = CoordinateUtils.getChunkKey(newChunkX, newChunkZ)) ?
|
||||
lastChunk : (lastChunk = (LevelChunk)chunkProvider.getChunk(newChunkX, newChunkZ, ChunkStatus.FULL, true));
|
||||
tempPos.setX(fx);
|
||||
for (int fy = minY; fy <= maxY; ++fy) {
|
||||
tempPos.setY(fy);
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final BlockState state = chunk.getBlockState(tempPos);
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
if (((CollisionBlockState)state).moonrise$emptyCollisionShape() || !state.isSuffocating(this.level, tempPos)) {
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, true).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) {
|
||||
final int blockY = currY | (currChunkY << 4);
|
||||
mutablePos.setY(blockY);
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
final int blockZ = currZ | (currChunkZ << 4);
|
||||
mutablePos.setZ(blockZ);
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final int blockX = currX | (currChunkX << 4);
|
||||
mutablePos.setX(blockX);
|
||||
|
||||
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
||||
|
||||
if (((CollisionBlockState)blockState).moonrise$emptyCollisionShape()
|
||||
|| !blockState.isSuffocating(world, mutablePos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, it does not use the Entity context stuff.
|
||||
final VoxelShape collisionShape = state.getCollisionShape(this.level, tempPos);
|
||||
final VoxelShape collisionShape = blockState.getCollisionShape(world, mutablePos);
|
||||
|
||||
if (collisionShape.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final AABB toCollide = box.move(-(double)fx, -(double)fy, -(double)fz);
|
||||
final AABB toCollide = boundingBox.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)collisionShape).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
@@ -245,7 +290,169 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
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.world.CollisionLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
@@ -16,6 +15,7 @@ import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
@@ -27,20 +27,18 @@ import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||
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;
|
||||
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.callback.CallbackInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(Level.class)
|
||||
abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseable {
|
||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
public abstract LevelChunk getChunk(int x, int z);
|
||||
@@ -48,38 +46,6 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
@Shadow
|
||||
public abstract WorldBorder getWorldBorder();
|
||||
|
||||
|
||||
@Unique
|
||||
private int minSection;
|
||||
|
||||
@Unique
|
||||
private int maxSection;
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMinSection() {
|
||||
return this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMaxSection() {
|
||||
return this.maxSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Init min/max section
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
this.minSection = WorldUtil.getMinSection(this);
|
||||
this.maxSection = WorldUtil.getMaxSection(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to faster lookup.
|
||||
* See {@link EntityGetterMixin#isUnobstructed(Entity, VoxelShape)} for expected behavior
|
||||
@@ -173,7 +139,7 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
int lastChunkY = Integer.MIN_VALUE;
|
||||
int lastChunkZ = Integer.MIN_VALUE;
|
||||
|
||||
final int minSection = ((CollisionLevel)level).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(level);
|
||||
|
||||
for (;;) {
|
||||
currPos.set(currX, currY, currZ);
|
||||
@@ -368,83 +334,116 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
*/
|
||||
@Override
|
||||
public Optional<BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
||||
final int minSection = WorldUtil.getMinSection((Level)(Object)this);
|
||||
|
||||
final int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
final int minBlockY = Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockY = Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection((Level)(Object)this) << 4) + 16, Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1);
|
||||
|
||||
final int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
CollisionUtil.LazyEntityCollisionContext collisionContext = null;
|
||||
|
||||
final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
final CollisionContext collisionShape = new CollisionUtil.LazyEntityCollisionContext(entity);
|
||||
BlockPos selected = null;
|
||||
double selectedDistance = Double.MAX_VALUE;
|
||||
|
||||
final Vec3 entityPos = entity.position();
|
||||
|
||||
LevelChunk lastChunk = null;
|
||||
int lastChunkX = Integer.MIN_VALUE;
|
||||
int lastChunkZ = Integer.MIN_VALUE;
|
||||
// special cases:
|
||||
if (minBlockY > maxBlockY) {
|
||||
// no point in checking
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final ChunkSource chunkSource = this.getChunkSource();
|
||||
|
||||
for (int currZ = minBlockZ; currZ <= maxBlockZ; ++currZ) {
|
||||
pos.setZ(currZ);
|
||||
for (int currX = minBlockX; currX <= maxBlockX; ++currX) {
|
||||
pos.setX(currX);
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);
|
||||
|
||||
final int newChunkX = currX >> 4;
|
||||
final int newChunkZ = currZ >> 4;
|
||||
|
||||
if (((newChunkX ^ lastChunkX) | (newChunkZ ^ lastChunkZ)) != 0) {
|
||||
lastChunkX = newChunkX;
|
||||
lastChunkZ = newChunkZ;
|
||||
lastChunk = (LevelChunk)chunkSource.getChunk(newChunkX, newChunkZ, ChunkStatus.FULL, false);
|
||||
}
|
||||
|
||||
if (lastChunk == null) {
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
for (int currY = minBlockY; currY <= maxBlockY; ++currY) {
|
||||
int edgeCount = ((currX == minBlockX || currX == maxBlockX) ? 1 : 0) +
|
||||
((currY == minBlockY || currY == maxBlockY) ? 1 : 0) +
|
||||
((currZ == minBlockZ || currZ == maxBlockZ) ? 1 : 0);
|
||||
|
||||
final LevelChunkSection[] sections = chunk.getSections();
|
||||
|
||||
// bound y
|
||||
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 boolean hasSpecial = ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks();
|
||||
final int sectionAdjust = !hasSpecial ? 1 : 0;
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) + sectionAdjust : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) - sectionAdjust : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) + sectionAdjust : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) - sectionAdjust : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) + sectionAdjust : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) - sectionAdjust : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
final int blockY = currY | (currChunkY << 4);
|
||||
mutablePos.setY(blockY);
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
final int blockZ = currZ | (currChunkZ << 4);
|
||||
mutablePos.setZ(blockZ);
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
|
||||
final int blockX = currX | (currChunkX << 4);
|
||||
mutablePos.setX(blockX);
|
||||
|
||||
final int edgeCount = hasSpecial ? ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
|
||||
((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
|
||||
((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0) : 0;
|
||||
if (edgeCount == 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pos.setY(currY);
|
||||
|
||||
final double distance = pos.distToCenterSqr(entityPos);
|
||||
if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(pos) >= 0)) {
|
||||
final double distance = mutablePos.distToCenterSqr(entityPos);
|
||||
if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(mutablePos) >= 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final BlockState state = ((GetBlockChunk)lastChunk).moonrise$getBlock(currX, currY, currZ);
|
||||
if (((CollisionBlockState)state).moonrise$emptyContextCollisionShape()) {
|
||||
final BlockState blockData = blocks.get(localBlockIndex);
|
||||
|
||||
if (((CollisionBlockState)blockData).moonrise$emptyContextCollisionShape()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VoxelShape blockCollision = ((CollisionBlockState)state).moonrise$getConstantContextCollisionShape();
|
||||
|
||||
if ((edgeCount != 1 || state.hasLargeCollisionShape()) && (edgeCount != 2 || state.getBlock() == Blocks.MOVING_PISTON)) {
|
||||
if (collisionContext == null) {
|
||||
collisionContext = new CollisionUtil.LazyEntityCollisionContext(entity);
|
||||
}
|
||||
VoxelShape blockCollision = ((CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape();
|
||||
|
||||
if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) {
|
||||
if (blockCollision == null) {
|
||||
blockCollision = state.getCollisionShape((Level)(Object)this, pos, collisionContext);
|
||||
}
|
||||
blockCollision = blockData.getCollisionShape((Level)(Object)this, mutablePos, collisionShape);
|
||||
|
||||
if (blockCollision.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid VoxelShape#move by shifting the entity collision shape instead
|
||||
final AABB shiftedAABB = aabb.move(-(double)currX, -(double)currY, -(double)currZ);
|
||||
final AABB shiftedAABB = aabb.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
@@ -452,7 +451,7 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
continue;
|
||||
}
|
||||
|
||||
selected = pos.immutable();
|
||||
selected = mutablePos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
@@ -461,13 +460,16 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
continue;
|
||||
}
|
||||
|
||||
selected = pos.immutable();
|
||||
selected = mutablePos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.ofNullable(selected);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ abstract class LivingEntityMixin extends Entity implements Attackable {
|
||||
int nonPassengers = 0;
|
||||
for (int i = 0, len = nearby.size(); i < len; ++i) {
|
||||
final Entity entity = nearby.get(i);
|
||||
nonPassengers += (entity.isPassenger() ? 1 : 0);
|
||||
nonPassengers += (entity.isPassenger() ? 0 : 1);
|
||||
this.doPush(entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@ 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.Redirect;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Supplier;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Shapes.class)
|
||||
abstract class ShapesMixin {
|
||||
@@ -168,13 +170,13 @@ abstract class ShapesMixin {
|
||||
final VoxelShape first = tmp[i];
|
||||
final VoxelShape second = tmp[next];
|
||||
|
||||
tmp[newSize++] = Shapes.or(first, second);
|
||||
tmp[newSize++] = Shapes.joinUnoptimized(first, second, BooleanOp.OR);
|
||||
}
|
||||
}
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
return tmp[0];
|
||||
return tmp[0].optimize();
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -219,11 +221,24 @@ abstract class ShapesMixin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an inject instead of overwrite to avoid mixin conflicts - obviously this will still disregard any changes made by other
|
||||
* mixins, but at least it won't conflict immediately. This is useful because some library mods mixin here when only the content
|
||||
* mod actually needs the change.
|
||||
*
|
||||
* @reason Route to faster logic
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
@Inject(
|
||||
method = "joinUnoptimized",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void injectJoinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction, final CallbackInfoReturnable<VoxelShape> cir) {
|
||||
cir.setReturnValue(joinUnoptimized(first, second, mergeFunction));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
final VoxelShape ret = CollisionUtil.joinUnoptimized(first, second, mergeFunction);
|
||||
if (DEBUG_SHAPE_MERGING) {
|
||||
final VoxelShape vanilla = joinUnoptimizedVanilla(first, second, mergeFunction);
|
||||
@@ -239,9 +254,19 @@ abstract class ShapesMixin {
|
||||
/**
|
||||
* @reason Route to faster logic
|
||||
* @author Spottedleaf
|
||||
* @see #injectJoinUnoptimized(VoxelShape, VoxelShape, BooleanOp, CallbackInfoReturnable)
|
||||
*/
|
||||
@Overwrite
|
||||
public static boolean joinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
@Inject(
|
||||
method = "joinIsNotEmpty(Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/BooleanOp;)Z",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void injectJoinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction, final CallbackInfoReturnable<Boolean> cir) {
|
||||
cir.setReturnValue(joinIsNotEmpty(first, second, mergeFunction));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean joinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
final boolean ret = CollisionUtil.isJoinNonEmpty(first, second, mergeFunction);
|
||||
if (DEBUG_SHAPE_MERGING) {
|
||||
if (ret != !joinUnoptimizedVanilla(first, second, mergeFunction).isEmpty()) {
|
||||
|
||||
@@ -67,7 +67,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkHolder.PlayerP
|
||||
)
|
||||
)
|
||||
private boolean newTrackerTick(final Iterator<?> iterator) {
|
||||
final NearbyPlayers nearbyPlayers = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers();
|
||||
final ServerEntityLookup entityLookup = (ServerEntityLookup)((ChunkSystemServerLevel)this.level).moonrise$getEntityLookup();;
|
||||
|
||||
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
|
||||
@@ -78,7 +77,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkHolder.PlayerP
|
||||
if (tracker == null) {
|
||||
continue;
|
||||
}
|
||||
((EntityTrackerTrackedEntity)tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
((EntityTrackerTrackedEntity)tracker).moonrise$tick(((ChunkSystemEntity)entity).moonrise$getChunkData().nearbyPlayers);
|
||||
if (((EntityTrackerTrackedEntity)tracker).moonrise$hasPlayers()
|
||||
|| ((ChunkSystemEntity)entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
|
||||
tracker.serverEntity.sendChanges();
|
||||
|
||||
@@ -4,16 +4,16 @@ import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(ChunkMap.TrackedEntity.class)
|
||||
@@ -22,26 +22,22 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
||||
@Final
|
||||
private Set<ServerPlayerConnection> seenBy;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private int range;
|
||||
|
||||
@Shadow
|
||||
public abstract void updatePlayer(ServerPlayer serverPlayer);
|
||||
|
||||
@Shadow
|
||||
public abstract void removePlayer(ServerPlayer serverPlayer);
|
||||
|
||||
/**
|
||||
* @reason ReferenceOpenHashSet is a better choice than a wrapped IdentityHashMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Sets;newIdentityHashSet()Ljava/util/Set;"
|
||||
)
|
||||
)
|
||||
private <E> Set<E> useBetterIdentitySet() {
|
||||
return new ReferenceOpenHashSet<>();
|
||||
}
|
||||
@Shadow
|
||||
protected abstract int scaledRange(final int i);
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
Entity entity;
|
||||
|
||||
@Unique
|
||||
private long lastChunkUpdate = -1L;
|
||||
@@ -126,4 +122,42 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
||||
public final boolean moonrise$hasPlayers() {
|
||||
return !this.seenBy.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason ReferenceOpenHashSet is a better choice than a wrapped IdentityHashMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Sets;newIdentityHashSet()Ljava/util/Set;"
|
||||
)
|
||||
)
|
||||
private <E> Set<E> useBetterIdentitySet() {
|
||||
return new ReferenceOpenHashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Optimise impl to not retrieve indirect passengers unless needed
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public int getEffectiveRange() {
|
||||
int range = this.range;
|
||||
final Entity entity = this.entity;
|
||||
|
||||
if (entity.getPassengers() == ImmutableList.<Entity>of()) {
|
||||
return this.scaledRange(range);
|
||||
}
|
||||
|
||||
// note: we change to List
|
||||
final List<Entity> passengers = (List<Entity>)entity.getIndirectPassengers();
|
||||
for (int i = 0, len = passengers.size(); i < len; ++i) {
|
||||
// note: max should be branchless
|
||||
range = Math.max(range, passengers.get(i).getType().clientTrackingRange() << 4);
|
||||
}
|
||||
|
||||
return this.scaledRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,40 +35,18 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V",
|
||||
// cannot use `<init>*` due to https://github.com/FabricMC/tiny-remapper/issues/137
|
||||
method = {
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V"
|
||||
},
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
),
|
||||
require = 3 // Require matching all 3 constructors
|
||||
)
|
||||
)
|
||||
private void constructorHook1(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook2(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook3(final CallbackInfo ci) {
|
||||
private void constructorHook(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fluid;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fluid.FluidFluidState;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -11,12 +12,9 @@ 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.callback.CallbackInfo;
|
||||
|
||||
@Mixin(FluidState.class)
|
||||
abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> {
|
||||
abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> implements FluidFluidState {
|
||||
|
||||
@Shadow
|
||||
public abstract Fluid getType();
|
||||
@@ -43,23 +41,9 @@ abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> {
|
||||
@Unique
|
||||
private BlockState legacyBlock;
|
||||
|
||||
/**
|
||||
* @reason Initialise caches
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
try {
|
||||
@Override
|
||||
public void moonrise$initCaches() {
|
||||
this.amount = this.getType().getAmount((FluidState)(Object)this);
|
||||
} catch (final Exception ex) {
|
||||
// https://github.com/JDKDigital/productivetrees/issues/16
|
||||
new RuntimeException("Failed to retrieve fluid amount for " + this, ex).printStackTrace();
|
||||
}
|
||||
this.isEmpty = this.getType().isEmpty();
|
||||
this.isSource = this.getType().isSource((FluidState)(Object)this);
|
||||
this.ownHeight = this.getType().getOwnHeight((FluidState)(Object)this);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fluid;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fluid.FluidFluidState;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.RegistrationInfo;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(MappedRegistry.class)
|
||||
abstract class MappedRegistryMixin<T> {
|
||||
/**
|
||||
* @reason Initialise caches
|
||||
* @author jpenilla
|
||||
*/
|
||||
@Inject(
|
||||
method = "register(Lnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lnet/minecraft/core/RegistrationInfo;)Lnet/minecraft/core/Holder$Reference;",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void injectFluidRegister(
|
||||
final ResourceKey<?> resourceKey,
|
||||
final T object,
|
||||
final RegistrationInfo registrationInfo,
|
||||
final CallbackInfoReturnable<Holder.Reference<T>> cir
|
||||
) {
|
||||
if (resourceKey.registryKey() == (Object) Registries.FLUID) {
|
||||
for (final FluidState possibleState : ((Fluid) object).getStateDefinition().getPossibleStates()) {
|
||||
((FluidFluidState) (Object) possibleState).moonrise$initCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
@@ -81,6 +81,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
|
||||
* @reason Route to optimized getBlock
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Override
|
||||
@Overwrite
|
||||
public BlockState getBlockState(final BlockPos pos) {
|
||||
return this.moonrise$getBlock(pos.getX(), pos.getY(), pos.getZ());
|
||||
@@ -101,7 +102,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState moonrise$getBlock(final int x, final int y, final int z) {
|
||||
public final BlockState moonrise$getBlock(final int x, final int y, final int z) {
|
||||
if (this.debug) {
|
||||
return this.getBlockDebug(x, y, z);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
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.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
// Higher priority to apply after Lithium mixin.world.inline_height.WorldMixin
|
||||
@Mixin(value = Level.class, priority = 1100)
|
||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Unique
|
||||
private int height;
|
||||
|
||||
@Unique
|
||||
private int minBuildHeight;
|
||||
|
||||
@Unique
|
||||
private int maxBuildHeight;
|
||||
|
||||
@Unique
|
||||
private int minSection;
|
||||
|
||||
@Unique
|
||||
private int maxSection;
|
||||
|
||||
@Unique
|
||||
private int sectionsCount;
|
||||
|
||||
/**
|
||||
* @reason Init min/max section
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "CTOR_HEAD"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci,
|
||||
@Local(ordinal = 0, argsOnly = true) final Holder<DimensionType> dimensionTypeHolder) {
|
||||
final DimensionType dimType = dimensionTypeHolder.value();
|
||||
this.height = dimType.height();
|
||||
this.minBuildHeight = dimType.minY();
|
||||
this.maxBuildHeight = this.minBuildHeight + this.height;
|
||||
this.minSection = this.minBuildHeight >> 4;
|
||||
this.maxSection = ((this.maxBuildHeight - 1) >> 4) + 1;
|
||||
this.sectionsCount = this.maxSection - this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight() {
|
||||
return this.minBuildHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxBuildHeight() {
|
||||
return this.maxBuildHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSection() {
|
||||
return this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSection() {
|
||||
return this.maxSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(final int y) {
|
||||
return y < this.minBuildHeight || y >= this.maxBuildHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(final BlockPos blockPos) {
|
||||
return this.isOutsideBuildHeight(blockPos.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndex(final int blockY) {
|
||||
return (blockY >> 4) - this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndexFromSectionY(final int sectionY) {
|
||||
return sectionY - this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionYFromSectionIndex(final int sectionIdx) {
|
||||
return sectionIdx + this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionsCount() {
|
||||
return this.sectionsCount;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.loading_screen;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||
import net.minecraft.client.multiplayer.LevelLoadStatusManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -25,6 +26,9 @@ abstract class LevelLoadStatusManagerMixin {
|
||||
)
|
||||
)
|
||||
private void immediatelyClose(final CallbackInfo ci) {
|
||||
if (!ConfigHolder.getConfig().misc.immediatelyCloseLoadingScreen) {
|
||||
return;
|
||||
}
|
||||
if (this.status == LevelLoadStatusManager.Status.WAITING_FOR_SERVER) {
|
||||
this.status = LevelLoadStatusManager.Status.LEVEL_READY;
|
||||
ci.cancel();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(EntityType.class)
|
||||
abstract class EntityTypeMixin implements MobSpawningEntityType {
|
||||
|
||||
@Unique
|
||||
private boolean hasBiomeCost = false;
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$hasAnyBiomeCost() {
|
||||
return this.hasBiomeCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$setHasBiomeCost() {
|
||||
this.hasBiomeCost = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LocalMobCapCalculator;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
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.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LocalMobCapCalculator.MobCounts.class)
|
||||
abstract class LocalMobCapCalculator$MobCountsMixin {
|
||||
|
||||
@Shadow
|
||||
@Mutable
|
||||
@Final
|
||||
private Object2IntMap<MobCategory> counts;
|
||||
|
||||
@Unique
|
||||
private static final MobCategory[] CATEGORIES = MobCategory.values();
|
||||
|
||||
@Unique
|
||||
private static final Object2IntOpenHashMap<?> DUMMY = new Object2IntOpenHashMap<>();
|
||||
|
||||
@Unique
|
||||
private final int[] newCounts = new int[CATEGORIES.length];
|
||||
|
||||
/**
|
||||
* @reason Ensure accesses of old field blow up
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void destroyField(final CallbackInfo ci) {
|
||||
this.counts = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid allocating the map, we null it later
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "(I)Lit/unimi/dsi/fastutil/objects/Object2IntOpenHashMap;"
|
||||
)
|
||||
)
|
||||
private <T> Object2IntOpenHashMap<T> avoidAllocation(final int expected) {
|
||||
return (Object2IntOpenHashMap<T>)DUMMY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Do not allocate MobCategory[]
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/MobCategory;values()[Lnet/minecraft/world/entity/MobCategory;"
|
||||
)
|
||||
)
|
||||
private MobCategory[] useCachedArray() {
|
||||
return CATEGORIES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use simple array instead of compute call
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void add(final MobCategory category) {
|
||||
++this.newCounts[category.ordinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use simple array instead of map get call
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean canSpawn(final MobCategory category) {
|
||||
return this.newCounts[category.ordinal()] < category.getMaxInstancesPerChunk();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(MobSpawnSettings.class)
|
||||
abstract class MobSpawnSettingsMixin {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<EntityType<?>, MobSpawnSettings.MobSpawnCost> mobSpawnCosts;
|
||||
|
||||
/**
|
||||
* @reason Set biome cost flag for any EntityType which has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void initBiomeCost(final CallbackInfo ci) {
|
||||
for (final EntityType<?> type : this.mobSpawnCosts.keySet()) {
|
||||
((MobSpawningEntityType)type).moonrise$setHasBiomeCost();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.NaturalSpawner;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(NaturalSpawner.class)
|
||||
abstract class NaturalSpawnerMixin {
|
||||
|
||||
@Shadow
|
||||
static Biome getRoughBiome(final BlockPos arg, final ChunkAccess arg2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Delay until we determine if the entity type even has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/NaturalSpawner;getRoughBiome(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/chunk/ChunkAccess;)Lnet/minecraft/world/level/biome/Biome;"
|
||||
)
|
||||
)
|
||||
private static Biome delayRoughBiome(final BlockPos pos, final ChunkAccess chunk) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Delay until we determine if the entity type even has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/biome/Biome;getMobSettings()Lnet/minecraft/world/level/biome/MobSpawnSettings;"
|
||||
)
|
||||
)
|
||||
private static MobSpawnSettings delayMobSpawnSettings(final Biome biome) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid looking up biomes for mobs which have no cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/biome/MobSpawnSettings;getMobSpawnCost(Lnet/minecraft/world/entity/EntityType;)Lnet/minecraft/world/level/biome/MobSpawnSettings$MobSpawnCost;"
|
||||
)
|
||||
)
|
||||
private static MobSpawnSettings.MobSpawnCost avoidBiomeLookupIfPossible(final MobSpawnSettings isNull,
|
||||
final EntityType<?> type,
|
||||
@Local(ordinal = 0, argsOnly = true) final BlockPos pos,
|
||||
@Local(ordinal = 0, argsOnly = true) final LevelChunk chunk) {
|
||||
if (!((MobSpawningEntityType)type).moonrise$hasAnyBiomeCost()) {
|
||||
// if the type has no associated cost with any biome, then no point in looking
|
||||
return null;
|
||||
}
|
||||
|
||||
return getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(type);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.random_ticking;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
@@ -85,11 +85,11 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
||||
final int offsetY = (sectionIndex + minSection) << 4;
|
||||
final LevelChunkSection section = sections[sectionIndex];
|
||||
final PalettedContainer<BlockState> states = section.states;
|
||||
if (section == null || !section.isRandomlyTickingBlocks()) {
|
||||
if (!section.isRandomlyTickingBlocks()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final IntList tickList = ((BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
||||
final ShortList tickList = ((BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
||||
|
||||
for (int i = 0; i < tickSpeed; ++i) {
|
||||
final int tickingBlocks = tickList.size();
|
||||
@@ -100,7 +100,7 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int location = tickList.getRaw(index);
|
||||
final int location = (int)tickList.getRaw(index) & 0xFFFF;
|
||||
final BlockState state = states.get(location);
|
||||
|
||||
// do not use a mutable pos, as some random tick implementations store the input without calling immutable()!
|
||||
|
||||
@@ -17,11 +17,17 @@ abstract class PalettedContainerMixin {
|
||||
* @author jpenilla
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>*",
|
||||
// cannot use `<init>*` due to https://github.com/FabricMC/tiny-remapper/issues/137
|
||||
method = {
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V"
|
||||
},
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "Lnet/minecraft/util/ThreadingDetector;"
|
||||
)
|
||||
),
|
||||
require = 3 // Require matching all 3 constructors
|
||||
)
|
||||
private static ThreadingDetector threadingDetector(final String name) {
|
||||
return THREADING_DETECTOR;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.patches.block_counting;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
|
||||
public interface BlockCountingBitStorage {
|
||||
|
||||
public Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries();
|
||||
public Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package ca.spottedleaf.moonrise.patches.block_counting;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
|
||||
public interface BlockCountingChunkSection {
|
||||
|
||||
public int moonrise$getSpecialCollidingBlocks();
|
||||
public boolean moonrise$hasSpecialCollidingBlocks();
|
||||
|
||||
public IntList moonrise$getTickingBlockList();
|
||||
public ShortList moonrise$getTickingBlockList();
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import it.unimi.dsi.fastutil.objects.AbstractReference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -27,7 +27,7 @@ public final class ZeroCollidingReferenceStateTable<O, S> {
|
||||
|
||||
public ZeroCollidingReferenceStateTable(final Collection<Property<?>> properties) {
|
||||
this.propertyToIndexer = new Int2ObjectOpenHashMap<>(properties.size());
|
||||
this.properties = new ReferenceOpenHashSet<>(properties);
|
||||
this.properties = new ReferenceArrayList<>(properties);
|
||||
|
||||
final List<Property<?>> sortedProperties = new ArrayList<>(properties);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_system.entity;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.monster.Shulker;
|
||||
@@ -19,6 +20,10 @@ public interface ChunkSystemEntity {
|
||||
|
||||
public void moonrise$setChunkStatus(final FullChunkStatus status);
|
||||
|
||||
public ChunkData moonrise$getChunkData();
|
||||
|
||||
public void moonrise$setChunkData(final ChunkData chunkData);
|
||||
|
||||
public int moonrise$getSectionX();
|
||||
|
||||
public void moonrise$setSectionX(final int x);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_system.level;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
@@ -19,4 +20,14 @@ public interface ChunkSystemLevel {
|
||||
|
||||
public void moonrise$midTickTasks();
|
||||
|
||||
public ChunkData moonrise$getChunkData(final long chunkKey);
|
||||
|
||||
public ChunkData moonrise$getChunkData(final int chunkX, final int chunkZ);
|
||||
|
||||
public ChunkData moonrise$requestChunkData(final long chunkKey);
|
||||
|
||||
public ChunkData moonrise$releaseChunkData(final long chunkKey);
|
||||
|
||||
public boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ);
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
@@ -60,4 +61,12 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
|
||||
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
|
||||
|
||||
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
|
||||
|
||||
public ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks();
|
||||
|
||||
public void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder);
|
||||
|
||||
public void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder);
|
||||
|
||||
public void moonrise$clearUnsyncedChunks();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user