Compare commits
39 Commits
v0.1.0-bet
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7510eda16 | ||
|
|
d9442c1492 | ||
|
|
93eb2786f2 | ||
|
|
1bef6823c5 | ||
|
|
01152eec95 | ||
|
|
1e39f5370a | ||
|
|
ea50ba38ea | ||
|
|
c00b9fcd7b | ||
|
|
bad5cae4d8 | ||
|
|
e3b1502bb6 | ||
|
|
54fc964987 | ||
|
|
0cbc9aa1a1 | ||
|
|
19e2136ae1 | ||
|
|
126cc03747 | ||
|
|
ceb4936d9d | ||
|
|
3cb888e894 | ||
|
|
7bedc1a7de | ||
|
|
718f6e1369 | ||
|
|
da9ab708a6 | ||
|
|
f22335f0b6 | ||
|
|
a3f2328000 | ||
|
|
529b9a44bb | ||
|
|
1e9a6504a1 | ||
|
|
9c46dcbb94 | ||
|
|
9a1e04389a | ||
|
|
29084d8e3f | ||
|
|
41790ecf1a | ||
|
|
8af7bccdfd | ||
|
|
5f9b3571f8 | ||
|
|
9adfb2514d | ||
|
|
bf2cd1c571 | ||
|
|
dad9a5c2eb | ||
|
|
a3acd46ee1 | ||
|
|
3e8cb80336 | ||
|
|
5c3e713be7 | ||
|
|
284631c321 | ||
|
|
666c4cb1a3 | ||
|
|
19b523eecd | ||
|
|
4a748778dc |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -31,6 +31,8 @@ jobs:
|
|||||||
key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-project-local-gradle-caches-
|
${{ runner.os }}-project-local-gradle-caches-
|
||||||
|
- name: "setup concurrentutil"
|
||||||
|
run: ./installConcurrentUtil.sh
|
||||||
- name: "execute gradle build"
|
- name: "execute gradle build"
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
- name: Determine Snapshot Status
|
- name: Determine Snapshot Status
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "ConcurrentUtil"]
|
||||||
|
path = ConcurrentUtil
|
||||||
|
url = https://github.com/Spottedleaf/ConcurrentUtil.git
|
||||||
|
|||||||
1
ConcurrentUtil
Submodule
1
ConcurrentUtil
Submodule
Submodule ConcurrentUtil added at 08d3ca3241
@@ -12,13 +12,13 @@ Fabric/NeoForge mod for optimising performance of the integrated (singleplayer/L
|
|||||||
Moonrise aims to optimise the game *without changing Vanilla behavior*. If you find that there are changes to Vanilla behavior,
|
Moonrise aims to optimise the game *without changing Vanilla behavior*. If you find that there are changes to Vanilla behavior,
|
||||||
please open an issue.
|
please open an issue.
|
||||||
|
|
||||||
Moonrise ports several important [Paper](https://github.com/PaperMC/Paper/)
|
Moonrise is an official port of several important [Paper](https://github.com/PaperMC/Paper/)
|
||||||
patches. Listed below are notable patches:
|
patches. Listed below are notable patches:
|
||||||
|
- [Starlight](https://github.com/PaperMC/Starlight/)
|
||||||
- Chunk system rewrite
|
- Chunk system rewrite
|
||||||
- Collision optimisations
|
- Collision optimisations
|
||||||
- Entity tracker optimisations
|
- Entity tracker optimisations
|
||||||
- Random ticking optimisations
|
- Random ticking optimisations
|
||||||
- [Starlight](https://github.com/PaperMC/Starlight/)
|
|
||||||
|
|
||||||
## Known Compatibility Issues
|
## Known Compatibility Issues
|
||||||
| Mod | Status |
|
| Mod | Status |
|
||||||
|
|||||||
11
build.gradle
11
build.gradle
@@ -22,8 +22,7 @@ def getGitCommit = { ->
|
|||||||
dependencies {
|
dependencies {
|
||||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||||
|
|
||||||
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||||
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
|
||||||
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
|
|
||||||
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
|
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
|
||||||
@@ -46,10 +45,9 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
mavenLocal {
|
||||||
url "https://repo.papermc.io/repository/maven-public/"
|
|
||||||
mavenContent {
|
mavenContent {
|
||||||
includeGroup("ca.spottedleaf")
|
includeModule("ca.spottedleaf", "concurrentutil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
@@ -100,7 +98,8 @@ subprojects {
|
|||||||
}
|
}
|
||||||
loom.runs.all {
|
loom.runs.all {
|
||||||
ideConfigGenerated true
|
ideConfigGenerated true
|
||||||
// property "mixin.debug", "true"
|
property "mixin.debug", "true"
|
||||||
|
property "Moonrise.MaxViewDistance", "128"
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.apply("me.modmuss50.mod-publish-plugin")
|
plugins.apply("me.modmuss50.mod-publish-plugin")
|
||||||
|
|||||||
@@ -17,13 +17,12 @@ dependencies {
|
|||||||
runtimeOnly(project(":").sourceSets.main.output)
|
runtimeOnly(project(":").sourceSets.main.output)
|
||||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||||
|
|
||||||
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||||
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
|
||||||
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
|
|
||||||
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||||
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||||
modImplementation "com.terraformersmc:modmenu:11.0.1"
|
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
|
||||||
|
|
||||||
modImplementation fabricApiLibs.command.api.v2
|
modImplementation fabricApiLibs.command.api.v2
|
||||||
modImplementation fabricApiLibs.lifecycle.events.v1
|
modImplementation fabricApiLibs.lifecycle.events.v1
|
||||||
@@ -44,7 +43,6 @@ shadowJar {
|
|||||||
destinationDirectory = layout.buildDirectory.dir("libs")
|
destinationDirectory = layout.buildDirectory.dir("libs")
|
||||||
configurations = [project.configurations.shadow]
|
configurations = [project.configurations.shadow]
|
||||||
relocate 'ca.spottedleaf.concurrentutil', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.concurrentutil'
|
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'
|
relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,29 +2,31 @@ package ca.spottedleaf.moonrise.fabric;
|
|||||||
|
|
||||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
import com.mojang.datafixers.DSL;
|
||||||
import net.fabricmc.fabric.api.event.Event;
|
import com.mojang.datafixers.DataFixer;
|
||||||
import net.fabricmc.fabric.api.event.EventFactory;
|
import com.mojang.serialization.Dynamic;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtOps;
|
||||||
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.GenerationChunkHolder;
|
import net.minecraft.server.level.GenerationChunkHolder;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.Explosion;
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
|
||||||
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@@ -32,19 +34,6 @@ public final class FabricHooks implements PlatformHooks {
|
|||||||
|
|
||||||
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
|
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
|
@Override
|
||||||
public String getBrand() {
|
public String getBrand() {
|
||||||
return "Moonrise";
|
return "Moonrise";
|
||||||
@@ -62,16 +51,6 @@ public final class FabricHooks implements PlatformHooks {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
|
|
||||||
ON_EXPLOSION_DETONATE.invoker().onExplosion(world, explosion, possiblyAffecting, diameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCurrentlyLoadingChunk() {
|
public boolean hasCurrentlyLoadingChunk() {
|
||||||
return false;
|
return false;
|
||||||
@@ -103,7 +82,7 @@ public final class FabricHooks implements PlatformHooks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
|
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +94,7 @@ public final class FabricHooks implements PlatformHooks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
|
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,12 +166,12 @@ public final class FabricHooks implements PlatformHooks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long configAutoSaveInterval() {
|
public long configAutoSaveInterval(final ServerLevel world) {
|
||||||
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int configMaxAutoSavePerTick() {
|
public int configMaxAutoSavePerTick(final ServerLevel world) {
|
||||||
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,4 +179,47 @@ public final class FabricHooks implements PlatformHooks {
|
|||||||
public boolean configFixMC159283() {
|
public boolean configFixMC159283() {
|
||||||
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean forceNoSave(final ChunkAccess chunk) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||||
|
final int fromVersion, final int toVersion) {
|
||||||
|
return (CompoundTag)dataFixer.update(
|
||||||
|
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
|
||||||
|
).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMainChunkLoadHook() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unloadEntity(final Entity entity) {
|
||||||
|
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
|
||||||
|
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
|
||||||
|
return currentRange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,13 +36,12 @@
|
|||||||
"accessWidener": "moonrise.accesswidener",
|
"accessWidener": "moonrise.accesswidener",
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=${loader_version}",
|
"fabricloader": ">=${loader_version}",
|
||||||
"minecraft": ">=1.21 <=1.21.1",
|
"minecraft": ">1.21.1 <1.21.4",
|
||||||
"fabric-command-api-v2": "*"
|
"fabric-command-api-v2": "*"
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"lithium:options": {
|
"lithium:options": {
|
||||||
"mixin.ai.poi": false,
|
"mixin.ai.poi": false,
|
||||||
"mixin.alloc.chunk_ticking": false,
|
|
||||||
"mixin.alloc.deep_passengers": false,
|
"mixin.alloc.deep_passengers": false,
|
||||||
"mixin.alloc.entity_tracker": false,
|
"mixin.alloc.entity_tracker": false,
|
||||||
"mixin.block.flatten_states": false,
|
"mixin.block.flatten_states": false,
|
||||||
@@ -66,7 +65,6 @@
|
|||||||
"mixin.world.block_entity_ticking": false,
|
"mixin.world.block_entity_ticking": false,
|
||||||
"mixin.world.chunk_access": false,
|
"mixin.world.chunk_access": false,
|
||||||
"mixin.world.explosions.block_raycast": false,
|
"mixin.world.explosions.block_raycast": false,
|
||||||
"mixin.world.explosions.cache_exposure": false,
|
|
||||||
"mixin.world.temperature_cache": false,
|
"mixin.world.temperature_cache": false,
|
||||||
"mixin.world.tick_scheduler": false
|
"mixin.world.tick_scheduler": false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,18 +3,19 @@ org.gradle.jvmargs=-Xmx2G
|
|||||||
org.gradle.daemon=false
|
org.gradle.daemon=false
|
||||||
# Fabric Properties
|
# Fabric Properties
|
||||||
# check these on https://modmuss50.me/fabric.html
|
# check these on https://modmuss50.me/fabric.html
|
||||||
minecraft_version=1.21.1
|
minecraft_version=1.21.3
|
||||||
supported_minecraft_versions=1.21,1.21.1
|
loader_version=0.16.7
|
||||||
loader_version=0.16.5
|
supported_minecraft_versions=1.21.3
|
||||||
neoforge_version=21.1.79
|
neoforge_version=21.3.31-beta
|
||||||
snakeyaml_version=2.3
|
fabric_api_version=0.107.0+1.21.3
|
||||||
concurrentutil_version=0.0.2
|
snakeyaml_version=2.2
|
||||||
yamlconfig_version=1.0.2
|
concurrentutil_version=0.0.2-SNAPSHOT
|
||||||
cloth_version=15.0.128
|
cloth_version=16.0.141
|
||||||
|
modmenu_version=12.0.0-beta.1
|
||||||
# version ids from modrinth
|
# version ids from modrinth
|
||||||
fabric_lithium_version=frXUdgvL
|
fabric_lithium_version=QhCwdt4l
|
||||||
neo_lithium_version=KhdehJ6l
|
neo_lithium_version=wDD955sb
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=0.1.0-beta.13
|
mod_version=0.2.0-beta.4
|
||||||
maven_group=ca.spottedleaf.moonrise
|
maven_group=ca.spottedleaf.moonrise
|
||||||
archives_base_name=moonrise
|
archives_base_name=moonrise
|
||||||
|
|||||||
6
installConcurrentUtil.sh
Executable file
6
installConcurrentUtil.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -eou pipefail
|
||||||
|
|
||||||
|
git submodule update --init --recursive
|
||||||
|
cd ConcurrentUtil
|
||||||
|
mvn install
|
||||||
@@ -21,8 +21,7 @@ dependencies {
|
|||||||
add('shadow', project([path: ":", configuration: "namedElements"]))
|
add('shadow', project([path: ":", configuration: "namedElements"]))
|
||||||
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
|
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
|
||||||
|
|
||||||
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
||||||
shadow("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
|
||||||
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
|
|
||||||
@@ -43,7 +42,6 @@ shadowJar {
|
|||||||
destinationDirectory = layout.buildDirectory.dir("libs")
|
destinationDirectory = layout.buildDirectory.dir("libs")
|
||||||
configurations = [project.configurations.shadow]
|
configurations = [project.configurations.shadow]
|
||||||
relocate 'ca.spottedleaf.concurrentutil', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.concurrentutil'
|
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'
|
relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ package ca.spottedleaf.moonrise.neoforge;
|
|||||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
import com.mojang.datafixers.DSL;
|
||||||
|
import com.mojang.datafixers.DataFixer;
|
||||||
|
import com.mojang.serialization.Dynamic;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtOps;
|
||||||
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.GenerationChunkHolder;
|
import net.minecraft.server.level.GenerationChunkHolder;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
@@ -13,16 +17,16 @@ import net.minecraft.world.entity.Entity;
|
|||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
import net.minecraft.world.level.EmptyBlockGetter;
|
||||||
import net.minecraft.world.level.Explosion;
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
|
||||||
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import net.neoforged.neoforge.common.CommonHooks;
|
import net.neoforged.neoforge.common.CommonHooks;
|
||||||
import net.neoforged.neoforge.common.NeoForge;
|
import net.neoforged.neoforge.common.NeoForge;
|
||||||
import net.neoforged.neoforge.entity.PartEntity;
|
import net.neoforged.neoforge.entity.PartEntity;
|
||||||
@@ -52,16 +56,6 @@ public final class NeoForgeHooks implements PlatformHooks {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
|
|
||||||
EventHooks.onExplosionDetonate(world, explosion, possiblyAffecting, diameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
|
|
||||||
return EventHooks.getExplosionKnockback(world, explosion, entity, original);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCurrentlyLoadingChunk() {
|
public boolean hasCurrentlyLoadingChunk() {
|
||||||
return true;
|
return true;
|
||||||
@@ -88,10 +82,12 @@ public final class NeoForgeHooks implements PlatformHooks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
|
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||||
|
final ChunkPos pos = holder.getPos();
|
||||||
|
|
||||||
EventHooks.fireChunkTicketLevelUpdated(
|
EventHooks.fireChunkTicketLevelUpdated(
|
||||||
world, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ),
|
world, CoordinateUtils.getChunkKey(pos.x, pos.z),
|
||||||
oldLevel, newLevel, holder.vanillaChunkHolder
|
oldLevel, newLevel, holder
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +97,7 @@ public final class NeoForgeHooks implements PlatformHooks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
|
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
|
||||||
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, world, data));
|
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, world, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,12 +189,12 @@ public final class NeoForgeHooks implements PlatformHooks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long configAutoSaveInterval() {
|
public long configAutoSaveInterval(final ServerLevel world) {
|
||||||
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int configMaxAutoSavePerTick() {
|
public int configMaxAutoSavePerTick(final ServerLevel world) {
|
||||||
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,4 +202,47 @@ public final class NeoForgeHooks implements PlatformHooks {
|
|||||||
public boolean configFixMC159283() {
|
public boolean configFixMC159283() {
|
||||||
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean forceNoSave(final ChunkAccess chunk) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||||
|
final int fromVersion, final int toVersion) {
|
||||||
|
return (CompoundTag)dataFixer.update(
|
||||||
|
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
|
||||||
|
).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMainChunkLoadHook() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
|
||||||
|
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, chunkData));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unloadEntity(final Entity entity) {
|
||||||
|
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
|
||||||
|
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
|
||||||
|
return currentRange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ side = "BOTH"
|
|||||||
[[dependencies.moonrise]]
|
[[dependencies.moonrise]]
|
||||||
modId = "minecraft"
|
modId = "minecraft"
|
||||||
type = "required"
|
type = "required"
|
||||||
versionRange = "[1.21,1.21.2)"
|
versionRange = "(1.21.1,1.21.4)"
|
||||||
ordering = "NONE"
|
ordering = "NONE"
|
||||||
side = "BOTH"
|
side = "BOTH"
|
||||||
|
|
||||||
@@ -57,7 +57,6 @@ config = "moonrise-neoforge.mixins.json"
|
|||||||
|
|
||||||
["lithium:options"]
|
["lithium:options"]
|
||||||
"mixin.ai.poi" = false
|
"mixin.ai.poi" = false
|
||||||
"mixin.alloc.chunk_ticking" = false
|
|
||||||
"mixin.alloc.deep_passengers" = false
|
"mixin.alloc.deep_passengers" = false
|
||||||
"mixin.alloc.entity_tracker" = false
|
"mixin.alloc.entity_tracker" = false
|
||||||
"mixin.block.flatten_states" = false
|
"mixin.block.flatten_states" = false
|
||||||
@@ -81,6 +80,5 @@ config = "moonrise-neoforge.mixins.json"
|
|||||||
"mixin.world.block_entity_ticking" = false
|
"mixin.world.block_entity_ticking" = false
|
||||||
"mixin.world.chunk_access" = false
|
"mixin.world.chunk_access" = false
|
||||||
"mixin.world.explosions.block_raycast" = false
|
"mixin.world.explosions.block_raycast" = false
|
||||||
"mixin.world.explosions.cache_exposure" = false
|
|
||||||
"mixin.world.temperature_cache" = false
|
"mixin.world.temperature_cache" = false
|
||||||
"mixin.world.tick_scheduler" = false
|
"mixin.world.tick_scheduler" = false
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
create("fabricApiLibs") {
|
create("fabricApiLibs") {
|
||||||
from("net.fabricmc.fabric-api:fabric-api-catalog:0.107.0+1.21.1")
|
from("net.fabricmc.fabric-api:fabric-api-catalog:${fabric_api_version}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,6 +48,3 @@ include("fabric")
|
|||||||
findProject(":fabric").name = "Moonrise-Fabric"
|
findProject(":fabric").name = "Moonrise-Fabric"
|
||||||
include("neoforge")
|
include("neoforge")
|
||||||
findProject(":neoforge").name = "Moonrise-NeoForge"
|
findProject(":neoforge").name = "Moonrise-NeoForge"
|
||||||
|
|
||||||
// includeBuild("../YamlConfig") // Uncomment to use local YamlConfig
|
|
||||||
// includeBuild("../ConcurrentUtil") // Uncomment to use local ConcurrentUtil
|
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
package ca.spottedleaf.moonrise.common;
|
package ca.spottedleaf.moonrise.common;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
import com.mojang.datafixers.DSL;
|
||||||
|
import com.mojang.datafixers.DataFixer;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.GenerationChunkHolder;
|
import net.minecraft.server.level.GenerationChunkHolder;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.util.datafix.DataFixTypes;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.Explosion;
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -33,10 +35,6 @@ public interface PlatformHooks {
|
|||||||
|
|
||||||
public Predicate<BlockState> maybeHasLightEmission();
|
public Predicate<BlockState> maybeHasLightEmission();
|
||||||
|
|
||||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
|
|
||||||
|
|
||||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original);
|
|
||||||
|
|
||||||
public boolean hasCurrentlyLoadingChunk();
|
public boolean hasCurrentlyLoadingChunk();
|
||||||
|
|
||||||
public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
|
public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
|
||||||
@@ -47,11 +45,11 @@ public interface PlatformHooks {
|
|||||||
|
|
||||||
public boolean allowAsyncTicketUpdates();
|
public boolean allowAsyncTicketUpdates();
|
||||||
|
|
||||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
|
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel);
|
||||||
|
|
||||||
public void chunkUnloadFromWorld(final LevelChunk chunk);
|
public void chunkUnloadFromWorld(final LevelChunk chunk);
|
||||||
|
|
||||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data);
|
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
|
||||||
|
|
||||||
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
|
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
|
||||||
|
|
||||||
@@ -82,12 +80,30 @@ public interface PlatformHooks {
|
|||||||
|
|
||||||
public int configPlayerMaxConcurrentGens();
|
public int configPlayerMaxConcurrentGens();
|
||||||
|
|
||||||
public long configAutoSaveInterval();
|
public long configAutoSaveInterval(final ServerLevel world);
|
||||||
|
|
||||||
public int configMaxAutoSavePerTick();
|
public int configMaxAutoSavePerTick(final ServerLevel world);
|
||||||
|
|
||||||
public boolean configFixMC159283();
|
public boolean configFixMC159283();
|
||||||
|
|
||||||
|
// support for CB chunk mustNotSave
|
||||||
|
public boolean forceNoSave(final ChunkAccess chunk);
|
||||||
|
|
||||||
|
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||||
|
final int fromVersion, final int toVersion);
|
||||||
|
|
||||||
|
public boolean hasMainChunkLoadHook();
|
||||||
|
|
||||||
|
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData);
|
||||||
|
|
||||||
|
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities);
|
||||||
|
|
||||||
|
public void unloadEntity(final Entity entity);
|
||||||
|
|
||||||
|
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk);
|
||||||
|
|
||||||
|
public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
|
||||||
|
|
||||||
public static final class Holder {
|
public static final class Holder {
|
||||||
private Holder() {
|
private Holder() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ca.spottedleaf.moonrise.common.config;
|
||||||
|
|
||||||
|
public interface InitialiseHook {
|
||||||
|
|
||||||
|
public void initialise();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,307 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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 "";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
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;
|
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.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.common.util.MoonriseCommon;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
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
|
@Adaptable
|
||||||
public final class MoonriseConfig {
|
public final class MoonriseConfig {
|
||||||
@@ -251,21 +251,4 @@ public final class MoonriseConfig {
|
|||||||
)
|
)
|
||||||
public boolean fixMC159283 = false;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -122,7 +122,7 @@ public final class NearbyPlayers {
|
|||||||
players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_SMALL_VIEW_DISTANCE);
|
players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_SMALL_VIEW_DISTANCE);
|
||||||
players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_REALLY_SMALL_VIEW_DISTANCE);
|
players[NearbyMapType.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.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getTickViewDistance(player));
|
||||||
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getViewDistance(player));
|
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getLoadViewDistance(player));
|
||||||
players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration
|
players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.common.misc;
|
|||||||
|
|
||||||
import ca.spottedleaf.concurrentutil.util.IntPairUtil;
|
import ca.spottedleaf.concurrentutil.util.IntPairUtil;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
||||||
|
|
||||||
@@ -14,6 +15,10 @@ public final class PositionCountingAreaMap<T> {
|
|||||||
return this.counters.keySet();
|
return this.counters.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LongSet getPositions() {
|
||||||
|
return this.positions.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
public int getTotalPositions() {
|
public int getTotalPositions() {
|
||||||
return this.positions.size();
|
return this.positions.size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public final class ChunkSystem {
|
|||||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||||
);
|
);
|
||||||
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
||||||
chunk.postProcessGeneration();
|
chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
|
||||||
}
|
}
|
||||||
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
|
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
|
||||||
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
|
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
|
||||||
@@ -152,8 +152,8 @@ public final class ChunkSystem {
|
|||||||
return RegionizedPlayerChunkLoader.getAPISendViewDistance(player);
|
return RegionizedPlayerChunkLoader.getAPISendViewDistance(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getViewDistance(final ServerPlayer player) {
|
public static int getLoadViewDistance(final ServerPlayer player) {
|
||||||
return RegionizedPlayerChunkLoader.getAPIViewDistance(player);
|
return RegionizedPlayerChunkLoader.getLoadViewDistance(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getTickViewDistance(final ServerPlayer player) {
|
public static int getTickViewDistance(final ServerPlayer player) {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package ca.spottedleaf.moonrise.common.util;
|
package ca.spottedleaf.moonrise.common.util;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
|
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.MoonriseConfig;
|
||||||
import ca.spottedleaf.yamlconfig.adapter.TypeAdapterRegistry;
|
import ca.spottedleaf.moonrise.common.config.moonrise.adapter.DefaultedTypeAdapter;
|
||||||
import ca.spottedleaf.yamlconfig.config.YamlConfig;
|
import ca.spottedleaf.moonrise.common.config.moonrise.type.DefaultedValue;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -11,17 +14,19 @@ public final class ConfigHolder {
|
|||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigHolder.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigHolder.class);
|
||||||
|
|
||||||
private static final File CONFIG_FILE = new File(System.getProperty("Moonrise.ConfigFile", "config/moonrise.yml"));
|
private static final File CONFIG_FILE = new File(System.getProperty(PlatformHooks.get().getBrand() + ".ConfigFile", "config/moonrise.yml"));
|
||||||
private static final TypeAdapterRegistry CONFIG_ADAPTERS = new TypeAdapterRegistry();
|
private static final TypeAdapterRegistry CONFIG_ADAPTERS = new TypeAdapterRegistry();
|
||||||
private static final YamlConfig<MoonriseConfig> CONFIG;
|
private static final YamlConfig<MoonriseConfig> CONFIG;
|
||||||
static {
|
static {
|
||||||
|
CONFIG_ADAPTERS.putAdapter(DefaultedValue.class, new DefaultedTypeAdapter());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
CONFIG = new YamlConfig<>(MoonriseConfig.class, new MoonriseConfig(), CONFIG_ADAPTERS);
|
CONFIG = new YamlConfig<>(MoonriseConfig.class, new MoonriseConfig(), CONFIG_ADAPTERS);
|
||||||
} catch (final Exception ex) {
|
} catch (final Exception ex) {
|
||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static final String CONFIG_HEADER = """
|
private static final String CONFIG_HEADER = String.format("""
|
||||||
This is the configuration file for Moonrise.
|
This is the configuration file for Moonrise.
|
||||||
|
|
||||||
Each configuration option is prefixed with a comment to explain what it does. Additional changes to this file
|
Each configuration option is prefixed with a comment to explain what it does. Additional changes to this file
|
||||||
@@ -29,10 +34,10 @@ public final class ConfigHolder {
|
|||||||
|
|
||||||
Below are the Moonrise startup flags. Note that startup flags must be placed in the JVM arguments, not
|
Below are the Moonrise startup flags. Note that startup flags must be placed in the JVM arguments, not
|
||||||
program arguments.
|
program arguments.
|
||||||
-DMoonrise.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
|
-D%1$s.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).
|
-D%1$s.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
|
||||||
-DMoonrise.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
|
-D%1$s.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
|
||||||
""";
|
""", PlatformHooks.get().getBrand());
|
||||||
|
|
||||||
static {
|
static {
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
@@ -47,6 +52,14 @@ public final class ConfigHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean reloadConfig() {
|
public static boolean reloadConfig() {
|
||||||
|
final boolean ret = reloadConfig0();
|
||||||
|
|
||||||
|
CONFIG.callInitialisers();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean reloadConfig0() {
|
||||||
synchronized (CONFIG) {
|
synchronized (CONFIG) {
|
||||||
if (CONFIG_FILE.exists()) {
|
if (CONFIG_FILE.exists()) {
|
||||||
try {
|
try {
|
||||||
@@ -57,8 +70,6 @@ public final class ConfigHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG.callInitialisers();
|
|
||||||
|
|
||||||
// write back any changes, or create if needed
|
// write back any changes, or create if needed
|
||||||
return saveConfig();
|
return saveConfig();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ package ca.spottedleaf.moonrise.common.util;
|
|||||||
public final class MixinWorkarounds {
|
public final class MixinWorkarounds {
|
||||||
|
|
||||||
// mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
|
// mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
|
||||||
|
// https://github.com/FabricMC/Mixin/pull/147
|
||||||
public static long[] clone(final long[] values) {
|
public static long[] clone(final long[] values) {
|
||||||
return values.clone();
|
return values.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] clone(final byte[] values) {
|
||||||
|
return values.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public final class MoonriseCommon {
|
|||||||
@Override
|
@Override
|
||||||
public void accept(Thread thread) {
|
public void accept(Thread thread) {
|
||||||
thread.setDaemon(true);
|
thread.setDaemon(true);
|
||||||
thread.setName("Moonrise Common Worker #" + this.idGenerator.getAndIncrement());
|
thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
|
||||||
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void uncaughtException(final Thread thread, final Throwable throwable) {
|
public void uncaughtException(final Thread thread, final Throwable throwable) {
|
||||||
@@ -44,7 +44,7 @@ public final class MoonriseCommon {
|
|||||||
} else {
|
} else {
|
||||||
defaultWorkerThreads = defaultWorkerThreads / 2;
|
defaultWorkerThreads = defaultWorkerThreads / 2;
|
||||||
}
|
}
|
||||||
defaultWorkerThreads = Integer.getInteger("Moonrise.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||||
|
|
||||||
int workerThreads = configWorkerThreads;
|
int workerThreads = configWorkerThreads;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package ca.spottedleaf.moonrise.common.util;
|
package ca.spottedleaf.moonrise.common.util;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
|
|
||||||
public final class MoonriseConstants {
|
public final class MoonriseConstants {
|
||||||
|
|
||||||
public static final int MAX_VIEW_DISTANCE = Integer.getInteger("Moonrise.MaxViewDistance", 32);
|
public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
|
||||||
|
|
||||||
private MoonriseConstants() {}
|
private MoonriseConstants() {}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ public final class SimpleRandom extends LegacyRandomSource {
|
|||||||
return (int)(seed >>> (BITS - Integer.SIZE));
|
return (int)(seed >>> (BITS - Integer.SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int nextInt(final int bound) {
|
public int nextInt(final int bound) {
|
||||||
if (bound <= 0) {
|
if (bound <= 0) {
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ public final class WorldUtil {
|
|||||||
// min, max are inclusive
|
// min, max are inclusive
|
||||||
|
|
||||||
public static int getMaxSection(final LevelHeightAccessor world) {
|
public static int getMaxSection(final LevelHeightAccessor world) {
|
||||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
return world.getMaxSectionY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getMaxSection(final Level world) {
|
public static int getMaxSection(final Level world) {
|
||||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
return world.getMaxSectionY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getMinSection(final LevelHeightAccessor world) {
|
public static int getMinSection(final LevelHeightAccessor world) {
|
||||||
return world.getMinSection();
|
return world.getMinSectionY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getMinSection(final Level world) {
|
public static int getMinSection(final Level world) {
|
||||||
return world.getMinSection();
|
return world.getMinSectionY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getMaxLightSection(final LevelHeightAccessor world) {
|
public static int getMaxLightSection(final LevelHeightAccessor world) {
|
||||||
|
|||||||
@@ -154,9 +154,9 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
|||||||
|
|
||||||
if (this.maybeHas((final BlockState state) -> !state.isAir())) {
|
if (this.maybeHas((final BlockState state) -> !state.isAir())) {
|
||||||
final PalettedContainer.Data<BlockState> data = this.states.data;
|
final PalettedContainer.Data<BlockState> data = this.states.data;
|
||||||
final Palette<BlockState> palette = data.palette;
|
final Palette<BlockState> palette = data.palette();
|
||||||
final int paletteSize = palette.getSize();
|
final int paletteSize = palette.getSize();
|
||||||
final BitStorage storage = data.storage;
|
final BitStorage storage = data.storage();
|
||||||
|
|
||||||
final Int2ObjectOpenHashMap<ShortArrayList> counts;
|
final Int2ObjectOpenHashMap<ShortArrayList> counts;
|
||||||
if (paletteSize == 1) {
|
if (paletteSize == 1) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
|||||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
import net.minecraft.world.level.block.state.properties.Property;
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
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.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Constant;
|
import org.spongepowered.asm.mixin.injection.Constant;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
@@ -17,6 +18,9 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
|
|||||||
super(string, class_);
|
super(string, class_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private static final Boolean[] BY_ID = new Boolean[]{ Boolean.FALSE, Boolean.TRUE };
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int moonrise$getIdFor(final Boolean value) {
|
public final int moonrise$getIdFor(final Boolean value) {
|
||||||
return value.booleanValue() ? 1 : 0;
|
return value.booleanValue() ? 1 : 0;
|
||||||
@@ -33,23 +37,6 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void init(final CallbackInfo ci) {
|
private void init(final CallbackInfo ci) {
|
||||||
this.moonrise$setById(new Boolean[]{ Boolean.FALSE, Boolean.TRUE });
|
this.moonrise$setById(BY_ID);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This skips all ops after the identity comparison in the original code.
|
|
||||||
*
|
|
||||||
* @reason Properties are identity comparable
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@WrapOperation(
|
|
||||||
method = "equals",
|
|
||||||
constant = @Constant(
|
|
||||||
classValue = BooleanProperty.class,
|
|
||||||
ordinal = 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private boolean skipFurtherComparison(final Object obj, final Operation<Boolean> orig) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ import java.util.Collection;
|
|||||||
@Mixin(EnumProperty.class)
|
@Mixin(EnumProperty.class)
|
||||||
abstract class EnumPropertyMixin<T extends Enum<T> & StringRepresentable> extends Property<T> implements PropertyAccess<T> {
|
abstract class EnumPropertyMixin<T extends Enum<T> & StringRepresentable> extends Property<T> implements PropertyAccess<T> {
|
||||||
|
|
||||||
@Shadow
|
|
||||||
public abstract Collection<T> getPossibleValues();
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int[] idLookupTable;
|
private int[] idLookupTable;
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove values arrays
|
// remove values arrays
|
||||||
this.values = null;
|
|
||||||
for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
|
for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
|
||||||
final S value = entry.getValue();
|
final S value = entry.getValue();
|
||||||
((StateHolderMixin<O, S>)(Object)(StateHolder<O, S>)value).values = null;
|
((StateHolderMixin<O, S>)(Object)(StateHolder<O, S>)value).values = null;
|
||||||
@@ -126,8 +125,8 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public <T extends Comparable<T>> Optional<T> getOptionalValue(final Property<T> property) {
|
public <T extends Comparable<T>> T getNullableValue(Property<T> property) {
|
||||||
return property == null ? Optional.empty() : Optional.ofNullable(this.optimisedTable.get(this.tableIndex, property));
|
return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
|||||||
@Unique
|
@Unique
|
||||||
private final ReferenceList<ServerPlayer> playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY);
|
private final ReferenceList<ServerPlayer> playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY);
|
||||||
|
|
||||||
@Unique
|
|
||||||
private boolean isMarkedDirtyForPlayers;
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private ChunkMap getChunkMap() {
|
private ChunkMap getChunkMap() {
|
||||||
return (ChunkMap)this.playerProvider;
|
return (ChunkMap)this.playerProvider;
|
||||||
@@ -123,16 +120,6 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean moonrise$isMarkedDirtyForPlayers() {
|
|
||||||
return this.isMarkedDirtyForPlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void moonrise$markDirtyForPlayers(final boolean value) {
|
|
||||||
this.isMarkedDirtyForPlayers = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final ServerPlayer[] EMPTY_PLAYER_ARRAY = new ServerPlayer[0];
|
private static final ServerPlayer[] EMPTY_PLAYER_ARRAY = new ServerPlayer[0];
|
||||||
|
|
||||||
@@ -300,14 +287,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
|||||||
// no players to sent to, so don't need to update anything
|
// no players to sent to, so don't need to update anything
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final LevelChunk ret = this.getChunkToSend();
|
return this.getChunkToSend();
|
||||||
|
|
||||||
if (ret != null) {
|
|
||||||
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,14 +306,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
|||||||
// no players to sent to, so don't need to update anything
|
// no players to sent to, so don't need to update anything
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final LevelChunk ret = this.getChunkToSend();
|
return this.getChunkToSend();
|
||||||
|
|
||||||
if (ret != null) {
|
|
||||||
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,14 +11,17 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
|||||||
import com.mojang.datafixers.DataFixer;
|
import com.mojang.datafixers.DataFixer;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.IntConsumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.StreamTagVisitor;
|
import net.minecraft.nbt.StreamTagVisitor;
|
||||||
import net.minecraft.server.level.ChunkGenerationTask;
|
import net.minecraft.server.level.ChunkGenerationTask;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ChunkResult;
|
import net.minecraft.server.level.ChunkResult;
|
||||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
|
import net.minecraft.server.level.ChunkTaskDispatcher;
|
||||||
import net.minecraft.server.level.ChunkTrackingView;
|
import net.minecraft.server.level.ChunkTrackingView;
|
||||||
import net.minecraft.server.level.GeneratingChunkMap;
|
import net.minecraft.server.level.GeneratingChunkMap;
|
||||||
import net.minecraft.server.level.GenerationChunkHolder;
|
import net.minecraft.server.level.GenerationChunkHolder;
|
||||||
@@ -28,7 +31,6 @@ import net.minecraft.server.level.progress.ChunkProgressListener;
|
|||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.util.StaticCache2D;
|
import net.minecraft.util.StaticCache2D;
|
||||||
import net.minecraft.util.thread.BlockableEventLoop;
|
import net.minecraft.util.thread.BlockableEventLoop;
|
||||||
import net.minecraft.util.thread.ProcessorHandle;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
@@ -78,13 +80,10 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
|
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private ChunkTaskPriorityQueueSorter queueSorter;
|
private ChunkTaskDispatcher worldgenTaskDispatcher;
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
|
private ChunkTaskDispatcher lightTaskDispatcher;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
|
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private int serverViewDistance;
|
private int serverViewDistance;
|
||||||
@@ -98,6 +97,12 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
@Shadow
|
@Shadow
|
||||||
private Queue<Runnable> unloadQueue;
|
private Queue<Runnable> unloadQueue;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
private LongSet chunksToEagerlySave;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
private AtomicInteger activeChunkWrites;
|
||||||
|
|
||||||
public ChunkMapMixin(RegionStorageInfo regionStorageInfo, Path path, DataFixer dataFixer, boolean bl) {
|
public ChunkMapMixin(RegionStorageInfo regionStorageInfo, Path path, DataFixer dataFixer, boolean bl) {
|
||||||
super(regionStorageInfo, path, dataFixer, bl);
|
super(regionStorageInfo, path, dataFixer, bl);
|
||||||
}
|
}
|
||||||
@@ -128,11 +133,12 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
this.updatingChunkMap = null;
|
this.updatingChunkMap = null;
|
||||||
this.visibleChunkMap = null;
|
this.visibleChunkMap = null;
|
||||||
this.pendingUnloads = null;
|
this.pendingUnloads = null;
|
||||||
this.queueSorter = null;
|
this.worldgenTaskDispatcher = null;
|
||||||
this.worldgenMailbox = null;
|
this.lightTaskDispatcher = null;
|
||||||
this.mainThreadMailbox = null;
|
|
||||||
this.pendingGenerationTasks = null;
|
this.pendingGenerationTasks = null;
|
||||||
this.unloadQueue = null;
|
this.unloadQueue = null;
|
||||||
|
this.chunksToEagerlySave = null;
|
||||||
|
this.activeChunkWrites = null;
|
||||||
|
|
||||||
// Dummy impl for mods that try to loadAsync directly
|
// Dummy impl for mods that try to loadAsync directly
|
||||||
this.worker = new IOWorker(
|
this.worker = new IOWorker(
|
||||||
@@ -152,7 +158,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Optional<CompoundTag>> loadAsync(final ChunkPos chunkPos) {
|
public CompletableFuture<Optional<CompoundTag>> loadAsync(final ChunkPos chunkPos) {
|
||||||
final CompletableFuture<Optional<CompoundTag>> future = new CompletableFuture<>();
|
final CompletableFuture<Optional<CompoundTag>> future = new CompletableFuture<>();
|
||||||
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (tag, throwable) -> {
|
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (final CompoundTag tag, final Throwable throwable) -> {
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
future.completeExceptionally(throwable);
|
future.completeExceptionally(throwable);
|
||||||
} else {
|
} else {
|
||||||
@@ -184,6 +190,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason This map is not needed, we maintain our own ordered set of chunks to autosave.
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public void setChunkUnsaved(final ChunkPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Route to new chunk system hooks
|
* @reason Route to new chunk system hooks
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
@@ -261,6 +276,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Destroy old chunk system hooks
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public void onLevelChange(final ChunkPos chunkPos, final IntSupplier intSupplier, final int i, final IntConsumer intConsumer) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Destroy old chunk system hooks
|
* @reason Destroy old chunk system hooks
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
@@ -309,6 +333,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.autoSave();
|
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.autoSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Destroy old chunk system hooks
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public void saveChunksEagerly(final BooleanSupplier hasTime) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Destroy old chunk system hooks
|
* @reason Destroy old chunk system hooks
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
@@ -403,7 +436,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void onChunkReadyToSend(final LevelChunk chunk) {
|
public void onChunkReadyToSend(final ChunkHolder holder, final LevelChunk chunk) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +455,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
* @see NewChunkHolder#save(boolean)
|
* @see NewChunkHolder#save(boolean)
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder) {
|
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder, final long time) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,6 +538,21 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Route to new chunk system
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Redirect(
|
||||||
|
method = "forEachSpawnCandidateChunk",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private <V> V redirectChunkHolderGet(final Long2ObjectLinkedOpenHashMap<V> instance, final long key) {
|
||||||
|
return (V)this.getVisibleChunkIfPresent(key);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Optional<CompoundTag>> read(final ChunkPos pos) {
|
public CompletableFuture<Optional<CompoundTag>> read(final ChunkPos pos) {
|
||||||
final CompletableFuture<Optional<CompoundTag>> ret = new CompletableFuture<>();
|
final CompletableFuture<Optional<CompoundTag>> ret = new CompletableFuture<>();
|
||||||
@@ -524,10 +572,11 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> write(final ChunkPos pos, final CompoundTag tag) {
|
public CompletableFuture<Void> write(final ChunkPos pos, final Supplier<CompoundTag> tag) {
|
||||||
MoonriseRegionFileIO.scheduleSave(
|
MoonriseRegionFileIO.scheduleSave(
|
||||||
this.level, pos.x, pos.z, tag,
|
this.level, pos.x, pos.z, tag.get(),
|
||||||
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
|
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,12 +22,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@Mixin(ChunkStorage.class)
|
@Mixin(ChunkStorage.class)
|
||||||
abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseable {
|
abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseable {
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private IOWorker worker;
|
public IOWorker worker;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final Logger LOGGER = LogUtils.getLogger();
|
private static final Logger LOGGER = LogUtils.getLogger();
|
||||||
@@ -118,13 +119,13 @@ abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseab
|
|||||||
method = "write",
|
method = "write",
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
|
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private CompletableFuture<Void> redirectWrite(final IOWorker instance, final ChunkPos chunkPos,
|
private CompletableFuture<Void> redirectWrite(final IOWorker instance, final ChunkPos chunkPos,
|
||||||
final CompoundTag compoundTag) {
|
final Supplier<CompoundTag> compoundTag) {
|
||||||
try {
|
try {
|
||||||
this.storage.write(chunkPos, compoundTag);
|
this.storage.write(chunkPos, compoundTag.get());
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
} catch (final Throwable throwable) {
|
} catch (final Throwable throwable) {
|
||||||
return CompletableFuture.failedFuture(throwable);
|
return CompletableFuture.failedFuture(throwable);
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
|||||||
@Final
|
@Final
|
||||||
private ClientChunkCache chunkSource;
|
private ClientChunkCache chunkSource;
|
||||||
|
|
||||||
protected ClientLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
protected ClientLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,9 +55,8 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
|||||||
value = "RETURN"
|
value = "RETURN"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData,
|
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey<Level> resourceKey,
|
||||||
ResourceKey<Level> resourceKey, Holder<DimensionType> holder, int i, int j, Supplier<ProfilerFiller> supplier,
|
Holder<DimensionType> holder, int i, int j, LevelRenderer levelRenderer, boolean bl, long l, int k, CallbackInfo ci) {
|
||||||
LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
|
|
||||||
this.entityStorage = null;
|
this.entityStorage = null;
|
||||||
|
|
||||||
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));
|
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
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.ChunkSystemServerLevel;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
||||||
@@ -9,14 +8,12 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
|||||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
|
|
||||||
import net.minecraft.server.level.DistanceManager;
|
import net.minecraft.server.level.DistanceManager;
|
||||||
|
import net.minecraft.server.level.ThrottlingChunkTaskDispatcher;
|
||||||
import net.minecraft.server.level.Ticket;
|
import net.minecraft.server.level.Ticket;
|
||||||
import net.minecraft.server.level.TicketType;
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.server.level.TickingTracker;
|
import net.minecraft.server.level.TickingTracker;
|
||||||
import net.minecraft.util.Mth;
|
|
||||||
import net.minecraft.util.SortedArraySet;
|
import net.minecraft.util.SortedArraySet;
|
||||||
import net.minecraft.util.thread.ProcessorHandle;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
@@ -47,13 +44,7 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
|||||||
Set<ChunkHolder> chunksToUpdateFutures;
|
Set<ChunkHolder> chunksToUpdateFutures;
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
ChunkTaskPriorityQueueSorter ticketThrottler;
|
ThrottlingChunkTaskDispatcher ticketDispatcher;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> ticketThrottlerInput;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
ProcessorHandle<ChunkTaskPriorityQueueSorter.Release> ticketThrottlerReleaser;
|
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
LongSet ticketsToRelease;
|
LongSet ticketsToRelease;
|
||||||
@@ -61,12 +52,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
|||||||
@Shadow
|
@Shadow
|
||||||
Executor mainThreadExecutor;
|
Executor mainThreadExecutor;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
|
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private int simulationDistance;
|
private int simulationDistance;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChunkMap moonrise$getChunkMap() {
|
public ChunkMap moonrise$getChunkMap() {
|
||||||
throw new AbstractMethodError();
|
throw new AbstractMethodError();
|
||||||
@@ -88,16 +77,14 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
|||||||
this.tickingTicketsTracker = null;
|
this.tickingTicketsTracker = null;
|
||||||
this.playerTicketManager = null;
|
this.playerTicketManager = null;
|
||||||
this.chunksToUpdateFutures = null;
|
this.chunksToUpdateFutures = null;
|
||||||
this.ticketThrottler = null;
|
this.ticketDispatcher = null;
|
||||||
this.ticketThrottlerInput = null;
|
|
||||||
this.ticketThrottlerReleaser = null;
|
|
||||||
this.ticketsToRelease = null;
|
this.ticketsToRelease = null;
|
||||||
this.mainThreadExecutor = null;
|
this.mainThreadExecutor = null;
|
||||||
this.simulationDistance = -1;
|
this.simulationDistance = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChunkHolderManager moonrise$getChunkHolderManager() {
|
public final ChunkHolderManager moonrise$getChunkHolderManager() {
|
||||||
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,10 +285,7 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
|||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void updateSimulationDistance(final int simulationDistance) {
|
public void updateSimulationDistance(final int simulationDistance) {
|
||||||
// note: vanilla does not clamp to 0, but we do simply because we need a min of 0
|
((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getPlayerChunkLoader().setTickDistance(simulationDistance);
|
||||||
final int clamped = Mth.clamp(simulationDistance, 0, MoonriseConstants.MAX_VIEW_DISTANCE);
|
|
||||||
|
|
||||||
((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getPlayerChunkLoader().setTickDistance(clamped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -331,6 +315,15 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Remove old chunk system hooks
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public LongSet getTickingChunks() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason This hack is not required anymore, see {@link MinecraftServerMixin}
|
* @reason This hack is not required anymore, see {@link MinecraftServerMixin}
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
|
||||||
|
|
||||||
import net.minecraft.core.SectionPos;
|
|
||||||
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Mixin(DynamicGameEventListener.class)
|
|
||||||
abstract class DynamicGameEventListenerMixin {
|
|
||||||
@Shadow @Nullable private SectionPos lastSection;
|
|
||||||
|
|
||||||
@Inject(method = "remove", at = @At("RETURN"))
|
|
||||||
private void onRemove(final CallbackInfo ci) {
|
|
||||||
// We need to unset the last section when removed, otherwise if the same instance is re-added at the same position it
|
|
||||||
// will assume there was no change and fail to re-register.
|
|
||||||
// In vanilla, chunks rarely unload and re-load quickly enough to trigger this issue. However, our chunk system has a
|
|
||||||
// quirk where fast chunk reload cycles will often occur on player login (see PR #22).
|
|
||||||
// So we fix this vanilla oversight as our changes cause it to manifest in bugs much more often (see issue #87).
|
|
||||||
this.lastSection = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -195,15 +195,6 @@ abstract class GenerationChunkHolderMixin {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Chunk system is not built on futures anymore
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public int getGenerationRefCount() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Route to new chunk holder
|
* @reason Route to new chunk holder
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
|
|||||||
@@ -93,11 +93,15 @@ abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystemLevelCh
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUnsaved(final boolean needsSaving) {
|
public boolean tryMarkSaved() {
|
||||||
if (!needsSaving) {
|
if (!this.isUnsaved()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
|
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
|
||||||
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
|
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
|
||||||
}
|
|
||||||
super.setUnsaved(needsSaving);
|
super.tryMarkSaved();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEnti
|
|||||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.FullChunkStatus;
|
import net.minecraft.server.level.FullChunkStatus;
|
||||||
|
import net.minecraft.util.profiling.Profiler;
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
@@ -37,9 +38,6 @@ import java.util.function.Predicate;
|
|||||||
@Mixin(Level.class)
|
@Mixin(Level.class)
|
||||||
abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter, LevelAccessor, AutoCloseable {
|
abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter, LevelAccessor, AutoCloseable {
|
||||||
|
|
||||||
@Shadow
|
|
||||||
public abstract ProfilerFiller getProfiler();
|
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
public abstract LevelChunk getChunk(int i, int j);
|
public abstract LevelChunk getChunk(int i, int j);
|
||||||
|
|
||||||
@@ -59,7 +57,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moonrise$setEntityLookup(final EntityLookup entityLookup) {
|
public final void moonrise$setEntityLookup(final EntityLookup entityLookup) {
|
||||||
if (this.entityLookup != null && !(this.entityLookup instanceof DefaultEntityLookup)) {
|
if (this.entityLookup != null && !(this.entityLookup instanceof DefaultEntityLookup)) {
|
||||||
throw new IllegalStateException("Entity lookup already initialised");
|
throw new IllegalStateException("Entity lookup already initialised");
|
||||||
}
|
}
|
||||||
@@ -87,7 +85,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
|||||||
@Overwrite
|
@Overwrite
|
||||||
@Override
|
@Override
|
||||||
public List<Entity> getEntities(final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate) {
|
public List<Entity> getEntities(final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate) {
|
||||||
this.getProfiler().incrementCounter("getEntities");
|
Profiler.get().incrementCounter("getEntities");
|
||||||
final List<Entity> ret = new ArrayList<>();
|
final List<Entity> ret = new ArrayList<>();
|
||||||
|
|
||||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entity, boundingBox, ret, predicate);
|
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entity, boundingBox, ret, predicate);
|
||||||
@@ -105,7 +103,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
|||||||
public <T extends Entity> void getEntities(final EntityTypeTest<Entity, T> entityTypeTest,
|
public <T extends Entity> void getEntities(final EntityTypeTest<Entity, T> entityTypeTest,
|
||||||
final AABB boundingBox, final Predicate<? super T> predicate,
|
final AABB boundingBox, final Predicate<? super T> predicate,
|
||||||
final List<? super T> into, final int maxCount) {
|
final List<? super T> into, final int maxCount) {
|
||||||
this.getProfiler().incrementCounter("getEntities");
|
Profiler.get().incrementCounter("getEntities");
|
||||||
|
|
||||||
if (entityTypeTest instanceof EntityType<T> byType) {
|
if (entityTypeTest instanceof EntityType<T> byType) {
|
||||||
if (maxCount != Integer.MAX_VALUE) {
|
if (maxCount != Integer.MAX_VALUE) {
|
||||||
@@ -178,7 +176,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
|
public final <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
|
||||||
this.getProfiler().incrementCounter("getEntities");
|
Profiler.get().incrementCounter("getEntities");
|
||||||
final List<T> ret = new ArrayList<>();
|
final List<T> ret = new ArrayList<>();
|
||||||
|
|
||||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate);
|
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate);
|
||||||
@@ -196,7 +194,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final List<Entity> moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
|
public final List<Entity> moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
|
||||||
this.getProfiler().incrementCounter("getEntities");
|
Profiler.get().incrementCounter("getEntities");
|
||||||
final List<Entity> ret = new ArrayList<>();
|
final List<Entity> ret = new ArrayList<>();
|
||||||
|
|
||||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate);
|
((ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate);
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop<TickTask
|
|||||||
private void closeIOThreads(final CallbackInfo ci) {
|
private void closeIOThreads(final CallbackInfo ci) {
|
||||||
LOGGER.info("Waiting for I/O tasks to complete...");
|
LOGGER.info("Waiting for I/O tasks to complete...");
|
||||||
MoonriseRegionFileIO.flush((MinecraftServer)(Object)this);
|
MoonriseRegionFileIO.flush((MinecraftServer)(Object)this);
|
||||||
|
LOGGER.info("All I/O tasks to complete");
|
||||||
if ((Object)this instanceof DedicatedServer) {
|
if ((Object)this instanceof DedicatedServer) {
|
||||||
MoonriseCommon.haltExecutors();
|
MoonriseCommon.haltExecutors();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,17 +43,4 @@ abstract class PlayerListMixin {
|
|||||||
)
|
)
|
||||||
private void doNotAdjustVD(final PlayerList instance, final Packet<?> packet) {}
|
private void doNotAdjustVD(final PlayerList instance, final Packet<?> packet) {}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason The RegionizedPlayerChunkLoader will handle the SD packet
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "setSimulationDistance",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/players/PlayerList;broadcastAll(Lnet/minecraft/network/protocol/Packet;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private void doNotAdjustSD(final PlayerList instance, final Packet<?> packet) {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -44,7 +45,14 @@ import java.util.stream.Stream;
|
|||||||
@Mixin(PoiManager.class)
|
@Mixin(PoiManager.class)
|
||||||
// Declare the generic type as Object so that our Overrides match the method signature of the superclass
|
// Declare the generic type as Object so that our Overrides match the method signature of the superclass
|
||||||
// Specifically, getOrCreate must return Object so that existing invokes do not route to the superclass
|
// Specifically, getOrCreate must return Object so that existing invokes do not route to the superclass
|
||||||
public abstract class PoiManagerMixin extends SectionStorage<Object> implements ChunkSystemPoiManager {
|
public abstract class PoiManagerMixin extends SectionStorage<Object, Object> implements ChunkSystemPoiManager {
|
||||||
|
|
||||||
|
public PoiManagerMixin(final SimpleRegionStorage simpleRegionStorage, final Codec<Object> codec, final Function<Object, Object> function,
|
||||||
|
final BiFunction<Object, Runnable, Object> biFunction, final Function<Runnable, Object> function2,
|
||||||
|
final RegistryAccess registryAccess, final ChunkIOErrorReporter chunkIOErrorReporter,
|
||||||
|
final LevelHeightAccessor levelHeightAccessor) {
|
||||||
|
super(simpleRegionStorage, codec, function, biFunction, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
||||||
|
}
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
abstract boolean isVillageCenter(long l);
|
abstract boolean isVillageCenter(long l);
|
||||||
@@ -52,10 +60,6 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
|||||||
@Shadow
|
@Shadow
|
||||||
public abstract void checkConsistencyWithBlocks(SectionPos sectionPos, LevelChunkSection levelChunkSection);
|
public abstract void checkConsistencyWithBlocks(SectionPos sectionPos, LevelChunkSection levelChunkSection);
|
||||||
|
|
||||||
public PoiManagerMixin(SimpleRegionStorage simpleRegionStorage, Function<Runnable, Codec<Object>> function, Function<Runnable, Object> function2, RegistryAccess registryAccess, ChunkIOErrorReporter chunkIOErrorReporter, LevelHeightAccessor levelHeightAccessor) {
|
|
||||||
super(simpleRegionStorage, function, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private ServerLevel world;
|
private ServerLevel world;
|
||||||
|
|
||||||
@@ -151,8 +155,8 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
|||||||
|
|
||||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
|
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
|
||||||
|
|
||||||
final ChunkHolderManager manager = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
final PoiChunk ret = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
||||||
final PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
||||||
|
|
||||||
return ret == null ? Optional.empty() : (Optional)ret.getSectionForVanilla(chunkY);
|
return ret == null ? Optional.empty() : (Optional)ret.getSectionForVanilla(chunkY);
|
||||||
}
|
}
|
||||||
@@ -206,9 +210,13 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
|||||||
public final void moonrise$onUnload(final long coordinate) { // Paper - rewrite chunk system
|
public final void moonrise$onUnload(final long coordinate) { // Paper - rewrite chunk system
|
||||||
final int chunkX = CoordinateUtils.getChunkX(coordinate);
|
final int chunkX = CoordinateUtils.getChunkX(coordinate);
|
||||||
final int chunkZ = CoordinateUtils.getChunkZ(coordinate);
|
final int chunkZ = CoordinateUtils.getChunkZ(coordinate);
|
||||||
|
|
||||||
|
final int minY = WorldUtil.getMinSection(this.world);
|
||||||
|
final int maxY = WorldUtil.getMaxSection(this.world);
|
||||||
|
|
||||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
|
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
|
||||||
for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
|
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
||||||
final long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
|
final long sectionPos = SectionPos.asLong(chunkX, sectionY, chunkZ);
|
||||||
this.updateDistanceTracking(sectionPos);
|
this.updateDistanceTracking(sectionPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,8 +225,12 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
|||||||
public final void moonrise$loadInPoiChunk(final PoiChunk poiChunk) {
|
public final void moonrise$loadInPoiChunk(final PoiChunk poiChunk) {
|
||||||
final int chunkX = poiChunk.chunkX;
|
final int chunkX = poiChunk.chunkX;
|
||||||
final int chunkZ = poiChunk.chunkZ;
|
final int chunkZ = poiChunk.chunkZ;
|
||||||
|
|
||||||
|
final int minY = WorldUtil.getMinSection(this.world);
|
||||||
|
final int maxY = WorldUtil.getMaxSection(this.world);
|
||||||
|
|
||||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
|
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
|
||||||
for (int sectionY = this.levelHeightAccessor.getMinSection(); sectionY < this.levelHeightAccessor.getMaxSection(); ++sectionY) {
|
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
||||||
final PoiSection section = poiChunk.getSection(sectionY);
|
final PoiSection section = poiChunk.getSection(sectionY);
|
||||||
if (section != null && !((ChunkSystemPoiSection)section).moonrise$isEmpty()) {
|
if (section != null && !((ChunkSystemPoiSection)section).moonrise$isEmpty()) {
|
||||||
this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
|
this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
|
||||||
@@ -254,20 +266,4 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
|||||||
private <T> Stream<T> skipLoadedSet(final Stream<T> instance, final Predicate<? super T> predicate) {
|
private <T> Stream<T> skipLoadedSet(final Stream<T> instance, final Predicate<? super T> predicate) {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void moonrise$close() throws IOException {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final CompoundTag moonrise$read(final int chunkX, final int chunkZ) throws IOException {
|
|
||||||
return MoonriseRegionFileIO.loadData(
|
|
||||||
this.world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.POI_DATA,
|
|
||||||
MoonriseRegionFileIO.getIOBlockingPriorityForCurrentThread()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void moonrise$write(final int chunkX, final int chunkZ, final CompoundTag data) throws IOException {
|
|
||||||
MoonriseRegionFileIO.scheduleSave(this.world, chunkX, chunkZ, data, MoonriseRegionFileIO.RegionFileType.POI_DATA);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ abstract class PoiSectionMixin implements ChunkSystemPoiSection {
|
|||||||
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);;
|
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean moonrise$isEmpty() {
|
public final boolean moonrise$isEmpty() {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
|||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.storage.ChunkSystemSectionStorage;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.storage.ChunkSystemSectionStorage;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.resources.RegistryOps;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
||||||
import net.minecraft.world.level.chunk.storage.SectionStorage;
|
import net.minecraft.world.level.chunk.storage.SectionStorage;
|
||||||
@@ -23,15 +21,11 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@Mixin(SectionStorage.class)
|
@Mixin(SectionStorage.class)
|
||||||
abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoCloseable {
|
abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, AutoCloseable {
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private SimpleRegionStorage simpleRegionStorage;
|
private SimpleRegionStorage simpleRegionStorage;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private static Logger LOGGER;
|
|
||||||
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private RegionFileStorage storage;
|
private RegionFileStorage storage;
|
||||||
@@ -41,6 +35,9 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
|||||||
return this.storage;
|
return this.storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void moonrise$close() throws IOException {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Retrieve storage from IOWorker, and then nuke it
|
* @reason Retrieve storage from IOWorker, and then nuke it
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
@@ -61,12 +58,8 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public final CompletableFuture<Optional<CompoundTag>> tryRead(final ChunkPos pos) {
|
public final CompletableFuture<Optional<SectionStorage.PackedChunk<P>>> tryRead(final ChunkPos pos) {
|
||||||
try {
|
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
|
||||||
return CompletableFuture.completedFuture(Optional.ofNullable(this.moonrise$read(pos.x, pos.z)));
|
|
||||||
} catch (final Throwable thr) {
|
|
||||||
return CompletableFuture.failedFuture(thr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,29 +67,17 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void readColumn(final ChunkPos pos, final RegistryOps<Tag> ops, final CompoundTag data) {
|
public void unpackChunk(final ChunkPos chunkPos, final SectionStorage.PackedChunk<P> packedChunk) {
|
||||||
throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName());
|
throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Route to new chunk system hook
|
* @reason Destroy old chunk system hook
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Redirect(
|
@Overwrite
|
||||||
method = "writeColumn(Lnet/minecraft/world/level/ChunkPos;)V",
|
private void writeChunk(final ChunkPos chunkPos) {
|
||||||
at = @At(
|
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/world/level/chunk/storage/SimpleRegionStorage;write(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private CompletableFuture<Void> redirectWrite(final SimpleRegionStorage instance, final ChunkPos pos,
|
|
||||||
final CompoundTag tag) {
|
|
||||||
try {
|
|
||||||
this.moonrise$write(pos.x, pos.z, tag);
|
|
||||||
} catch (final IOException ex) {
|
|
||||||
LOGGER.error("Error writing poi chunk data to disk for chunk " + pos, ex);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
|||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(ChunkSerializer.class)
|
@Mixin(SerializableChunkData.class)
|
||||||
abstract class ChunkSerializerMixin {
|
abstract class SerializableChunkDataMixin {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Chunk system handles this during full transition
|
* @reason Chunk system handles this during full transition
|
||||||
@@ -23,6 +23,6 @@ abstract class ChunkSerializerMixin {
|
|||||||
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
|
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private static void skipConsistencyCheck(PoiManager instance, SectionPos sectionPos, LevelChunkSection levelChunkSection) {}
|
private void skipConsistencyCheck(final PoiManager instance, final SectionPos sectionPos, final LevelChunkSection levelChunkSection) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import net.minecraft.world.level.chunk.ChunkSource;
|
|||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.chunk.LightChunk;
|
import net.minecraft.world.level.chunk.LightChunk;
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
@@ -51,6 +52,10 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
|||||||
@Final
|
@Final
|
||||||
public ServerLevel level;
|
public ServerLevel level;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private DimensionDataStorage dataStorage;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
|
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||||
|
|
||||||
@@ -81,13 +86,6 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
|||||||
completable::complete
|
completable::complete
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!completable.isDone() && chunkTaskScheduler.hasShutdown()) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Chunk system has shut down, cannot process chunk requests in world '" + ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(this.level) + "' at "
|
|
||||||
+ "(" + chunkX + "," + chunkZ + ") status: " + toStatus
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TickThread.isTickThreadFor(this.level, chunkX, chunkZ)) {
|
if (TickThread.isTickThreadFor(this.level, chunkX, chunkZ)) {
|
||||||
ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ);
|
ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ);
|
||||||
this.mainThreadProcessor.managedBlock(completable::isDone);
|
this.mainThreadProcessor.managedBlock(completable::isDone);
|
||||||
@@ -268,6 +266,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
|||||||
@Override
|
@Override
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
this.dataStorage.close();
|
||||||
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(true, true);
|
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +320,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Inject(
|
@Inject(
|
||||||
method = "tickChunks",
|
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
shift = At.Shift.AFTER,
|
shift = At.Shift.AFTER,
|
||||||
@@ -342,23 +341,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Redirect(
|
@Redirect(
|
||||||
method = "tickChunks",
|
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;isNaturalSpawningAllowed(Lnet/minecraft/world/level/ChunkPos;)Z"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private boolean shortNaturalSpawning(final ServerLevel instance, final ChunkPos chunkPos) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason In the chunk system, ticking chunks always have loaded entities. Of course, they are also always
|
|
||||||
* marked to be as ticking as well.
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "tickChunks",
|
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
|
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
|
||||||
@@ -367,35 +350,4 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
|||||||
private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) {
|
private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Since chunks in non-simulation range are only brought up to FULL status, not TICKING,
|
|
||||||
* those chunks may not be present in the ticking list and as a result we need to use our own list
|
|
||||||
* to ensure these chunks broadcast changes
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "tickChunks",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private void fixBroadcastChanges(final List<ServerChunkCache.ChunkAndHolder> instance,
|
|
||||||
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
|
|
||||||
final ReferenceList<ChunkHolder> unsyncedChunks = ((ChunkSystemServerLevel)this.level).moonrise$getUnsyncedChunks();
|
|
||||||
final ChunkHolder[] chunkHolders = unsyncedChunks.getRawDataUnchecked();
|
|
||||||
final int totalUnsyncedChunks = unsyncedChunks.size();
|
|
||||||
|
|
||||||
Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length);
|
|
||||||
for (int i = 0; i < totalUnsyncedChunks; ++i) {
|
|
||||||
final ChunkHolder chunkHolder = chunkHolders[i];
|
|
||||||
final LevelChunk chunk = chunkHolder.getChunkToSend();
|
|
||||||
if (chunk != null) {
|
|
||||||
chunkHolder.broadcastChanges(chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
((ChunkSystemServerLevel)this.level).moonrise$clearUnsyncedChunks();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
|||||||
@Final
|
@Final
|
||||||
private ServerChunkCache chunkSource;
|
private ServerChunkCache chunkSource;
|
||||||
|
|
||||||
protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
protected ServerLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
@@ -123,9 +123,6 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
|||||||
@Unique
|
@Unique
|
||||||
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
|
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
|
@Unique
|
||||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
|
private final ReferenceList<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
|
||||||
|
|
||||||
@@ -135,9 +132,6 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
|||||||
@Unique
|
@Unique
|
||||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
|
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
|
* @reason Initialise fields / destroy entity manager state
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
@@ -354,45 +348,6 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
|||||||
return this.entityTickingChunks;
|
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
|
@Override
|
||||||
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||||
final ServerChunkCache chunkSource = this.chunkSource;
|
final ServerChunkCache chunkSource = this.chunkSource;
|
||||||
|
|||||||
@@ -85,11 +85,20 @@ abstract class ChunkMapMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Use nearby players to avoid iterating over all online players
|
* @reason Avoid checking first if there are nearby players, as we make internal perform this implicitly.
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
|
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
|
||||||
|
return this.anyPlayerCloseEnoughForSpawningInternal(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Use nearby players to avoid iterating over all online players
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos) {
|
||||||
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
|
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
|
||||||
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
|
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap;
|
|||||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
|
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager;
|
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.server.level.DistanceManager;
|
import net.minecraft.server.level.DistanceManager;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
@@ -106,4 +107,13 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
|
|||||||
public boolean hasPlayersNearby(final long pos) {
|
public boolean hasPlayersNearby(final long pos) {
|
||||||
return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
|
return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Use spawnChunkTracker instead
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public LongIterator getSpawnCandidateChunks() {
|
||||||
|
return this.spawnChunkTracker.getPositions().iterator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ 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.ChunkSystemChunkHolder;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
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.Util;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
@@ -18,22 +16,15 @@ import net.minecraft.util.RandomSource;
|
|||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkSource;
|
import net.minecraft.world.level.chunk.ChunkSource;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import org.objectweb.asm.Opcodes;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import 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.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.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
@Mixin(ServerChunkCache.class)
|
@Mixin(ServerChunkCache.class)
|
||||||
abstract class ServerChunkCacheMixin extends ChunkSource {
|
abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||||
@@ -42,109 +33,17 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
|||||||
@Final
|
@Final
|
||||||
public ServerLevel level;
|
public ServerLevel level;
|
||||||
|
|
||||||
@Unique
|
@Shadow
|
||||||
private ServerChunkCache.ChunkAndHolder[] iterationCopy;
|
@Final
|
||||||
|
public ChunkMap chunkMap;
|
||||||
|
|
||||||
@Unique
|
|
||||||
private int iterationCopyLen;
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private final SimpleRandom shuffleRandom = new SimpleRandom(0L);
|
private final SimpleRandom shuffleRandom = new SimpleRandom(0L);
|
||||||
|
|
||||||
/**
|
@Unique
|
||||||
* @reason Avoid creating the list, which is sized at the chunkholder count. The actual number of ticking
|
private boolean isChunkNearPlayer(final ChunkMap chunkMap, final ChunkPos chunkPos, final LevelChunk levelChunk) {
|
||||||
* chunks is always lower. The mixin below will initialise the list to non-null.
|
final ChunkData chunkData = ((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "tickChunks",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lcom/google/common/collect/Lists;newArrayListWithCapacity(I)Ljava/util/ArrayList;"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private <T> ArrayList<T> avoidListCreation(final int initialArraySize) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Initialise the list to contain only the ticking chunks.
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@ModifyVariable(
|
|
||||||
method = "tickChunks",
|
|
||||||
at = @At(
|
|
||||||
value = "STORE",
|
|
||||||
opcode = Opcodes.ASTORE,
|
|
||||||
ordinal = 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private List<ServerChunkCache.ChunkAndHolder> initTickChunks(final List<ServerChunkCache.ChunkAndHolder> shouldBeNull) {
|
|
||||||
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
|
||||||
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
|
||||||
|
|
||||||
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
|
||||||
final int size = tickingChunks.size();
|
|
||||||
|
|
||||||
if (this.iterationCopy == null || this.iterationCopy.length < size) {
|
|
||||||
this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length];
|
|
||||||
}
|
|
||||||
this.iterationCopyLen = size;
|
|
||||||
System.arraycopy(raw, 0, this.iterationCopy, 0, size);
|
|
||||||
|
|
||||||
return ObjectArrayList.wrap(
|
|
||||||
this.iterationCopy, size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
|
|
||||||
* function
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "tickChunks",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
|
|
||||||
this.shuffleRandom.setSeed(randomSource.nextLong());
|
|
||||||
Util.shuffle(list, this.shuffleRandom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Do not initialise ticking chunk list, as we did that above.
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "tickChunks",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Ljava/util/Iterator;hasNext()Z",
|
|
||||||
ordinal = 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private <E> boolean skipTickAdd(final Iterator<E> instance) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Use the nearby players cache
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "tickChunks",
|
|
||||||
at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/level/ChunkMap;anyPlayerCloseEnoughForSpawning(Lnet/minecraft/world/level/ChunkPos;)Z"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private boolean useNearbyCache(final ChunkMap instance, final ChunkPos chunkPos,
|
|
||||||
@Local(ordinal = 0, argsOnly = false) final LevelChunk levelChunk) {
|
|
||||||
final ChunkData chunkData =
|
|
||||||
((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
|
|
||||||
.moonrise$getRealChunkHolder().holderData;
|
.moonrise$getRealChunkHolder().holderData;
|
||||||
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
||||||
if (nearbyPlayers == null) {
|
if (nearbyPlayers == null) {
|
||||||
@@ -162,7 +61,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
|||||||
|
|
||||||
Objects.checkFromIndexSize(0, len, raw.length);
|
Objects.checkFromIndexSize(0, len, raw.length);
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
if (instance.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
|
if (chunkMap.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,19 +70,47 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Clear the iteration array after the list is done being used.
|
* @reason Use the player ticking chunks list, which already contains chunks that are:
|
||||||
|
* 1. block ticking
|
||||||
|
* 2. within spawn range (8 chunks on any axis)
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Inject(
|
@Overwrite
|
||||||
method = "tickChunks",
|
private void collectTickingChunks(final List<LevelChunk> list) {
|
||||||
|
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||||
|
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
||||||
|
|
||||||
|
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
||||||
|
final int size = tickingChunks.size();
|
||||||
|
|
||||||
|
final ChunkMap chunkMap = this.chunkMap;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i];
|
||||||
|
final LevelChunk levelChunk = chunkAndHolder.chunk();
|
||||||
|
|
||||||
|
if (!this.isChunkNearPlayer(chunkMap, levelChunk.getPos(), levelChunk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.add(levelChunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
|
||||||
|
* function
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Redirect(
|
||||||
|
method = "tickChunks()V",
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
|
||||||
ordinal = 0,
|
|
||||||
shift = At.Shift.AFTER
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void broadcastChanges(final CallbackInfo ci) {
|
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
|
||||||
Arrays.fill(this.iterationCopy, 0, this.iterationCopyLen, null);
|
this.shuffleRandom.setSeed(randomSource.nextLong());
|
||||||
|
Util.shuffle(list, this.shuffleRandom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,37 +6,32 @@ import net.minecraft.world.entity.LivingEntity;
|
|||||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||||
import net.minecraft.world.entity.vehicle.AbstractMinecart;
|
import net.minecraft.world.entity.vehicle.AbstractMinecart;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import net.minecraft.world.phys.AABB;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@Mixin(ArmorStand.class)
|
@Mixin(ArmorStand.class)
|
||||||
abstract class ArmorStandMixin extends LivingEntity {
|
abstract class ArmorStandMixin extends LivingEntity {
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private static Predicate<Entity> RIDABLE_MINECARTS;
|
|
||||||
|
|
||||||
protected ArmorStandMixin(EntityType<? extends LivingEntity> entityType, Level level) {
|
protected ArmorStandMixin(EntityType<? extends LivingEntity> entityType, Level level) {
|
||||||
super(entityType, level);
|
super(entityType, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Optimise this method by making it use the class lookup
|
* @reason Optimise by making it use the class lookup
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Redirect(
|
||||||
public void pushEntities() {
|
method = "pushEntities",
|
||||||
final List<AbstractMinecart> nearby = this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), RIDABLE_MINECARTS);
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
for (int i = 0, len = nearby.size(); i < len; ++i) {
|
target = "Lnet/minecraft/world/level/Level;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;"
|
||||||
final AbstractMinecart minecart = nearby.get(i);
|
)
|
||||||
if (this.distanceToSqr(minecart) <= 0.2) {
|
)
|
||||||
minecart.push(this);
|
private List<Entity> redirectGetEntities(final Level instance, final Entity entity, final AABB list, final Predicate<? super Entity> arg) {
|
||||||
}
|
return (List)this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), arg);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
|||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
import it.unimi.dsi.fastutil.HashCommon;
|
import it.unimi.dsi.fastutil.HashCommon;
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateHolder;
|
import net.minecraft.world.level.block.state.StateHolder;
|
||||||
import net.minecraft.world.level.block.state.properties.Property;
|
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.Shapes;
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -28,7 +32,13 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
|||||||
protected BlockBehaviour.BlockStateBase.Cache cache;
|
protected BlockBehaviour.BlockStateBase.Cache cache;
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
public abstract boolean isAir();
|
public abstract VoxelShape getCollisionShape(BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext);
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public VoxelShape[] occlusionShapesByFace;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public VoxelShape occlusionShape;
|
||||||
|
|
||||||
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
|
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
|
||||||
super(object, reference2ObjectArrayMap, mapCodec);
|
super(object, reference2ObjectArrayMap, mapCodec);
|
||||||
@@ -70,7 +80,7 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
|||||||
}
|
}
|
||||||
if (neighbours) {
|
if (neighbours) {
|
||||||
for (final Direction direction : DIRECTIONS_CACHED) {
|
for (final Direction direction : DIRECTIONS_CACHED) {
|
||||||
initCaches(Shapes.getFaceShape(shape, direction), false);
|
initCaches(((CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction), false);
|
||||||
initCaches(shape.getFaceShape(direction), false);
|
initCaches(shape.getFaceShape(direction), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,9 +99,10 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
|||||||
private void initCollisionState(final CallbackInfo ci) {
|
private void initCollisionState(final CallbackInfo ci) {
|
||||||
if (this.cache != null) {
|
if (this.cache != null) {
|
||||||
final VoxelShape collisionShape = this.cache.collisionShape;
|
final VoxelShape collisionShape = this.cache.collisionShape;
|
||||||
if (this.isAir()) {
|
try {
|
||||||
this.constantCollisionShape = Shapes.empty();
|
this.constantCollisionShape = this.getCollisionShape(null, null, null);
|
||||||
} else {
|
} catch (final Throwable throwable) {
|
||||||
|
// :(
|
||||||
this.constantCollisionShape = null;
|
this.constantCollisionShape = null;
|
||||||
}
|
}
|
||||||
this.occludesFullBlock = ((CollisionVoxelShape)collisionShape).moonrise$occludesFullBlock();
|
this.occludesFullBlock = ((CollisionVoxelShape)collisionShape).moonrise$occludesFullBlock();
|
||||||
@@ -102,17 +113,21 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
|||||||
if (this.constantCollisionShape != null) {
|
if (this.constantCollisionShape != null) {
|
||||||
initCaches(this.constantCollisionShape, true);
|
initCaches(this.constantCollisionShape, true);
|
||||||
}
|
}
|
||||||
if (this.cache.occlusionShapes != null) {
|
|
||||||
for (final VoxelShape shape : this.cache.occlusionShapes) {
|
|
||||||
initCaches(shape, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.occludesFullBlock = false;
|
this.occludesFullBlock = false;
|
||||||
this.emptyCollisionShape = false;
|
this.emptyCollisionShape = false;
|
||||||
this.emptyConstantCollisionShape = false;
|
this.emptyConstantCollisionShape = false;
|
||||||
this.constantCollisionShape = null;
|
this.constantCollisionShape = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.occlusionShape != null) {
|
||||||
|
initCaches(this.occlusionShape, true);
|
||||||
|
}
|
||||||
|
if (this.occlusionShapesByFace != null) {
|
||||||
|
for (final VoxelShape shape : this.occlusionShapesByFace) {
|
||||||
|
initCaches(shape, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ interface EntityGetterMixin {
|
|||||||
@Overwrite
|
@Overwrite
|
||||||
default boolean isUnobstructed(final Entity entity, final VoxelShape voxel) {
|
default boolean isUnobstructed(final Entity entity, final VoxelShape voxel) {
|
||||||
if (voxel.isEmpty()) {
|
if (voxel.isEmpty()) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AABB singleAABB = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
|
final AABB singleAABB = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
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.CollisionUtil;
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
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.FloatArraySet;
|
||||||
import it.unimi.dsi.fastutil.floats.FloatArrays;
|
import it.unimi.dsi.fastutil.floats.FloatArrays;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.tags.BlockTags;
|
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityDimensions;
|
import net.minecraft.world.entity.EntityDimensions;
|
||||||
import net.minecraft.world.level.Level;
|
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.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkSource;
|
import net.minecraft.world.level.chunk.ChunkSource;
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
@@ -27,11 +23,8 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Mixin(Entity.class)
|
@Mixin(Entity.class)
|
||||||
abstract class EntityMixin {
|
abstract class EntityMixin {
|
||||||
@@ -57,12 +50,6 @@ abstract class EntityMixin {
|
|||||||
@Shadow
|
@Shadow
|
||||||
private boolean onGround;
|
private boolean onGround;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
public abstract boolean isAlive();
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
protected abstract void onInsideBlock(final BlockState blockState);
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static float[] calculateStepHeights(final AABB box, final List<VoxelShape> voxels, final List<AABB> aabbs, final float stepHeight,
|
private static float[] calculateStepHeights(final AABB box, final List<VoxelShape> voxels, final List<AABB> aabbs, final float stepHeight,
|
||||||
final float collidedY) {
|
final float collidedY) {
|
||||||
@@ -296,163 +283,4 @@ abstract class EntityMixin {
|
|||||||
|
|
||||||
return false;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
|||||||
public boolean isUnobstructed(final Entity entity) {
|
public boolean isUnobstructed(final Entity entity) {
|
||||||
final AABB boundingBox = entity.getBoundingBox();
|
final AABB boundingBox = entity.getBoundingBox();
|
||||||
if (CollisionUtil.isEmpty(boundingBox)) {
|
if (CollisionUtil.isEmpty(boundingBox)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Entity> entities = this.getEntities(
|
final List<Entity> entities = this.getEntities(
|
||||||
@@ -83,7 +83,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
|||||||
final Vec3 to = clipContext.getTo();
|
final Vec3 to = clipContext.getTo();
|
||||||
final Vec3 from = clipContext.getFrom();
|
final Vec3 from = clipContext.getFrom();
|
||||||
|
|
||||||
return BlockHitResult.miss(to, Direction.getNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
return BlockHitResult.miss(to, Direction.getApproximateNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
|||||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||||
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||||
import net.minecraft.client.renderer.block.LiquidBlockRenderer;
|
import net.minecraft.client.renderer.block.LiquidBlockRenderer;
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.shapes.ArrayVoxelShape;
|
import net.minecraft.world.phys.shapes.ArrayVoxelShape;
|
||||||
import net.minecraft.world.phys.shapes.BooleanOp;
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||||
@@ -23,12 +21,7 @@ abstract class LiquidBlockRendererMixin {
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
private static boolean isFaceOccludedByState(final BlockGetter world, final Direction direction, final float height,
|
private static boolean isFaceOccludedByState(final Direction direction, final float height, final BlockState state) {
|
||||||
final BlockPos pos, final BlockState state) {
|
|
||||||
if (!state.canOcclude()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for created shape is empty
|
// check for created shape is empty
|
||||||
if (height < (float)CollisionUtil.COLLISION_EPSILON) {
|
if (height < (float)CollisionUtil.COLLISION_EPSILON) {
|
||||||
return false;
|
return false;
|
||||||
@@ -57,18 +50,19 @@ abstract class LiquidBlockRendererMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final VoxelShape stateShape = ((CollisionVoxelShape)state.getOcclusionShape(world, pos)).moonrise$getFaceShapeClamped(direction.getOpposite());
|
final VoxelShape occlusionShape = ((CollisionVoxelShape)state.getFaceOcclusionShape(direction.getOpposite()))
|
||||||
|
.moonrise$getFaceShapeClamped(direction.getOpposite());
|
||||||
|
|
||||||
if (stateShape.isEmpty()) {
|
if (occlusionShape.isEmpty()) {
|
||||||
// cannot occlude
|
// cannot occlude
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fast check for box
|
// fast check for box
|
||||||
if (heightShape == stateShape) {
|
if (heightShape == occlusionShape) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !Shapes.joinIsNotEmpty(heightShape, stateShape, BooleanOp.ONLY_FIRST);
|
return !Shapes.joinIsNotEmpty(heightShape, occlusionShape, BooleanOp.ONLY_FIRST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.Attackable;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.entity.EntitySelector;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.level.GameRules;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mixin(LivingEntity.class)
|
|
||||||
abstract class LivingEntityMixin extends Entity implements Attackable {
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
protected abstract void doPush(Entity entity);
|
|
||||||
|
|
||||||
public LivingEntityMixin(EntityType<?> entityType, Level level) {
|
|
||||||
super(entityType, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Optimise this method
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public void pushEntities() {
|
|
||||||
if (this.level().isClientSide()) {
|
|
||||||
final List<Player> players = this.level().getEntitiesOfClass(Player.class, this.getBoundingBox(), EntitySelector.pushableBy(this));
|
|
||||||
for (int i = 0, len = players.size(); i < len; ++i) {
|
|
||||||
this.doPush(players.get(i));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final List<Entity> nearby = this.level().getEntities(this, this.getBoundingBox(), EntitySelector.pushableBy(this));
|
|
||||||
|
|
||||||
// only iterate ONCE
|
|
||||||
int nonPassengers = 0;
|
|
||||||
for (int i = 0, len = nearby.size(); i < len; ++i) {
|
|
||||||
final Entity entity = nearby.get(i);
|
|
||||||
nonPassengers += (entity.isPassenger() ? 0 : 1);
|
|
||||||
this.doPush(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxCramming;
|
|
||||||
if (nonPassengers != 0 && (maxCramming = this.level().getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING)) > 0
|
|
||||||
&& nonPassengers > (maxCramming - 1) && this.random.nextInt(4) == 0) {
|
|
||||||
this.hurt(this.damageSources().cramming(), 6.0F);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -79,8 +79,7 @@ abstract class ParticleMixin {
|
|||||||
final List<VoxelShape> voxels = new ArrayList<>();
|
final List<VoxelShape> voxels = new ArrayList<>();
|
||||||
final boolean collided = CollisionUtil.getCollisionsForBlocksOrWorldBorder(
|
final boolean collided = CollisionUtil.getCollisionsForBlocksOrWorldBorder(
|
||||||
world, entity, collisionBox, voxels, boxes,
|
world, entity, collisionBox, voxels, boxes,
|
||||||
0,
|
0, null
|
||||||
null
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!collided) {
|
if (!collided) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
||||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||||
@@ -10,20 +9,15 @@ import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
|||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.damagesource.DamageSource;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
||||||
import net.minecraft.world.entity.item.PrimedTnt;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.Explosion;
|
import net.minecraft.world.level.Explosion;
|
||||||
import net.minecraft.world.level.ExplosionDamageCalculator;
|
import net.minecraft.world.level.ExplosionDamageCalculator;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.ServerExplosion;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.gameevent.GameEvent;
|
|
||||||
import net.minecraft.world.level.material.FluidState;
|
import net.minecraft.world.level.material.FluidState;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
@@ -33,33 +27,20 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Mixin(Explosion.class)
|
@Mixin(ServerExplosion.class)
|
||||||
abstract class ExplosionMixin {
|
abstract class ServerExplosionMixin {
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
private Level level;
|
private ServerLevel level;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private Entity source;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private double x;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private double y;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private double z;
|
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
@@ -69,21 +50,13 @@ abstract class ExplosionMixin {
|
|||||||
@Final
|
@Final
|
||||||
private float radius;
|
private float radius;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private ObjectArrayList<BlockPos> toBlow;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
@Final
|
|
||||||
private Map<Player, Vec3> hitPlayers;
|
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
private boolean fire;
|
private boolean fire;
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
private DamageSource damageSource;
|
private Vec3 center;
|
||||||
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
@@ -142,6 +115,12 @@ abstract class ExplosionMixin {
|
|||||||
@Unique
|
@Unique
|
||||||
private LevelChunk[] chunkCache = null;
|
private LevelChunk[] chunkCache = null;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private ExplosionBlockCache[] directMappedBlockCache;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private BlockPos.MutableBlockPos mutablePos;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z,
|
private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z,
|
||||||
final long key, final boolean calculateResistance) {
|
final long key, final boolean calculateResistance) {
|
||||||
@@ -343,29 +322,45 @@ abstract class ExplosionMixin {
|
|||||||
return (float)missedRays / (float)totalRays;
|
return (float)missedRays / (float)totalRays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Rewrite ray casting and seen fraction calculation for performance
|
* @reason Init cache fields
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "explode",
|
||||||
|
at = @At(
|
||||||
|
value = "HEAD"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private void initCacheFields(final CallbackInfo ci) {
|
||||||
|
this.blockCache = new Long2ObjectOpenHashMap<>();
|
||||||
|
this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
||||||
|
Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS);
|
||||||
|
this.chunkCache = new LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
||||||
|
this.directMappedBlockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
|
||||||
|
this.mutablePos = new BlockPos.MutableBlockPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Rewrite ray casting for performance
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void explode() {
|
public List<BlockPos> calculateExplodedPositions() {
|
||||||
this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
|
final ObjectArrayList<BlockPos> ret = new ObjectArrayList<>();
|
||||||
|
|
||||||
this.blockCache = new Long2ObjectOpenHashMap<>();
|
final Vec3 center = this.center;
|
||||||
|
|
||||||
this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
final ExplosionBlockCache[] blockCache = this.directMappedBlockCache;
|
||||||
Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS);
|
|
||||||
|
|
||||||
this.chunkCache = new LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
|
||||||
|
|
||||||
final ExplosionBlockCache[] blockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
|
|
||||||
|
|
||||||
// use initial cache value that is most likely to be used: the source position
|
// use initial cache value that is most likely to be used: the source position
|
||||||
final ExplosionBlockCache initialCache;
|
final ExplosionBlockCache initialCache;
|
||||||
{
|
{
|
||||||
final int blockX = Mth.floor(this.x);
|
final int blockX = Mth.floor(center.x);
|
||||||
final int blockY = Mth.floor(this.y);
|
final int blockY = Mth.floor(center.y);
|
||||||
final int blockZ = Mth.floor(this.z);
|
final int blockZ = Mth.floor(center.z);
|
||||||
|
|
||||||
final long key = BlockPos.asLong(blockX, blockY, blockZ);
|
final long key = BlockPos.asLong(blockX, blockY, blockZ);
|
||||||
|
|
||||||
@@ -381,9 +376,9 @@ abstract class ExplosionMixin {
|
|||||||
for (int ray = 0, len = CACHED_RAYS.length; ray < len;) {
|
for (int ray = 0, len = CACHED_RAYS.length; ray < len;) {
|
||||||
ExplosionBlockCache cachedBlock = initialCache;
|
ExplosionBlockCache cachedBlock = initialCache;
|
||||||
|
|
||||||
double currX = this.x;
|
double currX = center.x;
|
||||||
double currY = this.y;
|
double currY = center.y;
|
||||||
double currZ = this.z;
|
double currZ = center.z;
|
||||||
|
|
||||||
final double incX = CACHED_RAYS[ray];
|
final double incX = CACHED_RAYS[ray];
|
||||||
final double incY = CACHED_RAYS[ray + 1];
|
final double incY = CACHED_RAYS[ray + 1];
|
||||||
@@ -424,7 +419,7 @@ abstract class ExplosionMixin {
|
|||||||
cachedBlock.shouldExplode = shouldExplode ? Boolean.TRUE : Boolean.FALSE;
|
cachedBlock.shouldExplode = shouldExplode ? Boolean.TRUE : Boolean.FALSE;
|
||||||
if (shouldExplode) {
|
if (shouldExplode) {
|
||||||
if (this.fire || !cachedBlock.blockState.isAir()) {
|
if (this.fire || !cachedBlock.blockState.isAir()) {
|
||||||
this.toBlow.add(cachedBlock.immutablePos);
|
ret.add(cachedBlock.immutablePos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,83 +431,42 @@ abstract class ExplosionMixin {
|
|||||||
} while (power > 0.0f);
|
} while (power > 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
final double diameter = (double)this.radius * 2.0;
|
return ret;
|
||||||
final List<Entity> entities = this.level.getEntities(this.source,
|
}
|
||||||
new AABB(
|
|
||||||
(double)Mth.floor(this.x - (diameter + 1.0)),
|
|
||||||
(double)Mth.floor(this.y - (diameter + 1.0)),
|
|
||||||
(double)Mth.floor(this.z - (diameter + 1.0)),
|
|
||||||
|
|
||||||
(double)Mth.floor(this.x + (diameter + 1.0)),
|
/**
|
||||||
(double)Mth.floor(this.y + (diameter + 1.0)),
|
* @reason Use optimised getSeenPercent implementation
|
||||||
(double)Mth.floor(this.z + (diameter + 1.0))
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Redirect(
|
||||||
|
method = {
|
||||||
|
"hurtEntities()V",
|
||||||
|
"hurtEntities(Ljava/util/List;)V" // Neo moves logic into this new method
|
||||||
|
},
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/level/ServerExplosion;getSeenPercent(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)F"
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
final Vec3 center = new Vec3(this.x, this.y, this.z);
|
private float useBetterSeenPercent(final Vec3 center, final Entity target) {
|
||||||
|
return this.getSeenFraction(center, target, this.directMappedBlockCache, this.mutablePos);
|
||||||
final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
|
|
||||||
|
|
||||||
final PlatformHooks platformHooks = PlatformHooks.get();
|
|
||||||
|
|
||||||
platformHooks.onExplosion(this.level, (Explosion)(Object)this, entities, diameter);
|
|
||||||
for (int i = 0, len = entities.size(); i < len; ++i) {
|
|
||||||
final Entity entity = entities.get(i);
|
|
||||||
if (entity.ignoreExplosion((Explosion)(Object)this)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final double normalizedDistanceToCenter = Math.sqrt(entity.distanceToSqr(center)) / diameter;
|
|
||||||
if (normalizedDistanceToCenter > 1.0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
double distX = entity.getX() - this.x;
|
|
||||||
double distY = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y;
|
|
||||||
double distZ = entity.getZ() - this.z;
|
|
||||||
final double distMag = Math.sqrt(distX * distX + distY * distY + distZ * distZ);
|
|
||||||
|
|
||||||
if (distMag == 0.0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
distX /= distMag;
|
|
||||||
distY /= distMag;
|
|
||||||
distZ /= distMag;
|
|
||||||
|
|
||||||
// route to new visible fraction calculation, using the existing block cache
|
|
||||||
final double seenFraction = (double)this.getSeenFraction(center, entity, blockCache, blockPos);
|
|
||||||
if (this.damageCalculator.shouldDamageEntity((Explosion)(Object)this, entity)) {
|
|
||||||
// inline getEntityDamageAmount so that we can avoid double calling getSeenPercent, which is the MOST
|
|
||||||
// expensive part of this loop!!!!
|
|
||||||
final double factor = (1.0 - normalizedDistanceToCenter) * seenFraction;
|
|
||||||
entity.hurt(this.damageSource, (float)((factor * factor + factor) / 2.0 * 7.0 * diameter + 1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
final double intensityFraction = (1.0 - normalizedDistanceToCenter) * seenFraction * (double)this.damageCalculator.getKnockbackMultiplier(entity);
|
|
||||||
|
|
||||||
|
|
||||||
final double knockbackFraction;
|
|
||||||
if (entity instanceof LivingEntity livingEntity) {
|
|
||||||
knockbackFraction = intensityFraction * (1.0 - livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE));
|
|
||||||
} else {
|
|
||||||
knockbackFraction = intensityFraction;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 knockback = new Vec3(distX * knockbackFraction, distY * knockbackFraction, distZ * knockbackFraction);
|
|
||||||
knockback = platformHooks.modifyExplosionKnockback(this.level, (Explosion)(Object)this, entity, knockback);
|
|
||||||
entity.setDeltaMovement(entity.getDeltaMovement().add(knockback));
|
|
||||||
|
|
||||||
if (entity instanceof Player player) {
|
|
||||||
if (!player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying)) {
|
|
||||||
this.hitPlayers.put(player, knockback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entity.onExplosionHit(this.source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Destroy cache fields
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "explode",
|
||||||
|
at = @At(
|
||||||
|
value = "RETURN"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private void destroyCacheFields(final CallbackInfo ci) {
|
||||||
this.blockCache = null;
|
this.blockCache = null;
|
||||||
this.chunkPosCache = null;
|
this.chunkPosCache = null;
|
||||||
this.chunkCache = null;
|
this.chunkCache = null;
|
||||||
|
this.directMappedBlockCache = null;
|
||||||
|
this.mutablePos = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,15 +278,6 @@ abstract class ShapesMixin {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @reason Route to use cache
|
|
||||||
* @author Spottedleaf
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public static VoxelShape getFaceShape(final VoxelShape shape, final Direction direction) {
|
|
||||||
return ((CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static boolean mergedMayOccludeBlock(final VoxelShape shape1, final VoxelShape shape2) {
|
private static boolean mergedMayOccludeBlock(final VoxelShape shape1, final VoxelShape shape2) {
|
||||||
// if the combined bounds of the two shapes cannot occlude, then neither can the merged
|
// if the combined bounds of the two shapes cannot occlude, then neither can the merged
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.phys.shapes.SliceShape;
|
import net.minecraft.world.phys.shapes.SliceShape;
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
@@ -22,7 +20,7 @@ abstract class SliceShapeMixin {
|
|||||||
value = "RETURN"
|
value = "RETURN"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void initState(final VoxelShape parent, final Direction.Axis forAxis, final int forIndex, final CallbackInfo ci) {
|
private void initState(final CallbackInfo ci) {
|
||||||
((CollisionVoxelShape)this).moonrise$initCache();
|
((CollisionVoxelShape)this).moonrise$initCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -687,13 +687,13 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
|||||||
final AABB singleAABB = this.singleAABBRepresentation;
|
final AABB singleAABB = this.singleAABBRepresentation;
|
||||||
if (singleAABB != null) {
|
if (singleAABB != null) {
|
||||||
if (singleAABB.contains(fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
|
if (singleAABB.contains(fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
|
||||||
return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
||||||
}
|
}
|
||||||
return clip(singleAABB, from, to, offset);
|
return clip(singleAABB, from, to, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CollisionUtil.strictlyContains((VoxelShape)(Object)this, fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
|
if (CollisionUtil.strictlyContains((VoxelShape)(Object)this, fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
|
||||||
return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AABB.clip(((VoxelShape)(Object)this).toAabbs(), from, to, offset);
|
return AABB.clip(((VoxelShape)(Object)this).toAabbs(), from, to, offset);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.entity_tracker;
|
package ca.spottedleaf.moonrise.mixin.entity_tracker;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||||
@@ -144,8 +145,8 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
|||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public int getEffectiveRange() {
|
public int getEffectiveRange() {
|
||||||
int range = this.range;
|
|
||||||
final Entity entity = this.entity;
|
final Entity entity = this.entity;
|
||||||
|
int range = PlatformHooks.get().modifyEntityTrackingRange(entity, this.range);
|
||||||
|
|
||||||
if (entity.getPassengers() == ImmutableList.<Entity>of()) {
|
if (entity.getPassengers() == ImmutableList.<Entity>of()) {
|
||||||
return this.scaledRange(range);
|
return this.scaledRange(range);
|
||||||
@@ -154,8 +155,9 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
|||||||
// note: we change to List
|
// note: we change to List
|
||||||
final List<Entity> passengers = (List<Entity>)entity.getIndirectPassengers();
|
final List<Entity> passengers = (List<Entity>)entity.getIndirectPassengers();
|
||||||
for (int i = 0, len = passengers.size(); i < len; ++i) {
|
for (int i = 0, len = passengers.size(); i < len; ++i) {
|
||||||
|
final Entity passenger = passengers.get(i);
|
||||||
// note: max should be branchless
|
// note: max should be branchless
|
||||||
range = Math.max(range, passengers.get(i).getType().clientTrackingRange() << 4);
|
range = Math.max(range, PlatformHooks.get().modifyEntityTrackingRange(passenger, passenger.getType().clientTrackingRange() << 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.scaledRange(range);
|
return this.scaledRange(range);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
|||||||
private void updateData(final PalettedContainer.Data<T> data) {
|
private void updateData(final PalettedContainer.Data<T> data) {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
((FastPaletteData<T>)(Object)data).moonrise$setPalette(
|
((FastPaletteData<T>)(Object)data).moonrise$setPalette(
|
||||||
((FastPalette<T>)data.palette).moonrise$getRawPalette((FastPaletteData<T>)(Object)data)
|
((FastPalette<T>)data.palette()).moonrise$getRawPalette((FastPaletteData<T>)(Object)data)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
|||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private T readPaletteSlow(final PalettedContainer.Data<T> data, final int paletteIdx) {
|
private T readPaletteSlow(final PalettedContainer.Data<T> data, final int paletteIdx) {
|
||||||
return data.palette.valueFor(paletteIdx);
|
return data.palette().valueFor(paletteIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
@@ -103,9 +103,9 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
|||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public T getAndSet(final int index, final T value) {
|
public T getAndSet(final int index, final T value) {
|
||||||
final int paletteIdx = this.data.palette.idFor(value);
|
final int paletteIdx = this.data.palette().idFor(value);
|
||||||
final PalettedContainer.Data<T> data = this.data;
|
final PalettedContainer.Data<T> data = this.data;
|
||||||
final int prev = data.storage.getAndSet(index, paletteIdx);
|
final int prev = data.storage().getAndSet(index, paletteIdx);
|
||||||
return this.readPalette(data, prev);
|
return this.readPalette(data, prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +116,6 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
|||||||
@Overwrite
|
@Overwrite
|
||||||
public T get(final int index) {
|
public T get(final int index) {
|
||||||
final PalettedContainer.Data<T> data = this.data;
|
final PalettedContainer.Data<T> data = this.data;
|
||||||
return this.readPalette(data, data.storage.get(index));
|
return this.readPalette(data, data.storage().get(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,14 +114,14 @@ abstract class FlowingFluidMixin extends Fluid {
|
|||||||
private static final int COLLISION_OCCLUSION_CACHE_SIZE = 2048;
|
private static final int COLLISION_OCCLUSION_CACHE_SIZE = 2048;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final FluidOcclusionCacheKey[] COLLISION_OCCLUSION_CACHE = new FluidOcclusionCacheKey[COLLISION_OCCLUSION_CACHE_SIZE];
|
private static final ThreadLocal<FluidOcclusionCacheKey[]> COLLISION_OCCLUSION_CACHE = ThreadLocal.withInitial(() -> new FluidOcclusionCacheKey[COLLISION_OCCLUSION_CACHE_SIZE]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Try to avoid going to the cache for simple cases; additionally use better caching strategy
|
* @reason Try to avoid going to the cache for simple cases; additionally use better caching strategy
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
private boolean canPassThroughWall(final Direction direction, final BlockGetter level,
|
public static boolean canPassThroughWall(final Direction direction, final BlockGetter level,
|
||||||
final BlockPos fromPos, final BlockState fromState,
|
final BlockPos fromPos, final BlockState fromState,
|
||||||
final BlockPos toPos, final BlockState toState) {
|
final BlockPos toPos, final BlockState toState) {
|
||||||
if (((CollisionBlockState)fromState).moonrise$emptyCollisionShape() & ((CollisionBlockState)toState).moonrise$emptyCollisionShape()) {
|
if (((CollisionBlockState)fromState).moonrise$emptyCollisionShape() & ((CollisionBlockState)toState).moonrise$emptyCollisionShape()) {
|
||||||
@@ -135,7 +135,7 @@ abstract class FlowingFluidMixin extends Fluid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final FluidOcclusionCacheKey[] cache = ((CollisionBlockState)fromState).moonrise$hasCache() & ((CollisionBlockState)toState).moonrise$hasCache() ?
|
final FluidOcclusionCacheKey[] cache = ((CollisionBlockState)fromState).moonrise$hasCache() & ((CollisionBlockState)toState).moonrise$hasCache() ?
|
||||||
COLLISION_OCCLUSION_CACHE : null;
|
COLLISION_OCCLUSION_CACHE.get() : null;
|
||||||
|
|
||||||
final int keyIndex
|
final int keyIndex
|
||||||
= (((CollisionBlockState)fromState).moonrise$uniqueId1() ^ ((CollisionBlockState)toState).moonrise$uniqueId2() ^ ((CollisionDirection)(Object)direction).moonrise$uniqueId())
|
= (((CollisionBlockState)fromState).moonrise$uniqueId1() ^ ((CollisionBlockState)toState).moonrise$uniqueId2() ^ ((CollisionDirection)(Object)direction).moonrise$uniqueId())
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> implements
|
|||||||
private BlockState legacyBlock;
|
private BlockState legacyBlock;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moonrise$initCaches() {
|
public final void moonrise$initCaches() {
|
||||||
this.amount = this.getType().getAmount((FluidState)(Object)this);
|
this.amount = this.getType().getAmount((FluidState)(Object)this);
|
||||||
this.isEmpty = this.getType().isEmpty();
|
this.isEmpty = this.getType().isEmpty();
|
||||||
this.isSource = this.getType().isSource((FluidState)(Object)this);
|
this.isSource = this.getType().isSource((FluidState)(Object)this);
|
||||||
|
|||||||
@@ -16,20 +16,20 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
@Mixin(value = Level.class, priority = 1100)
|
@Mixin(value = Level.class, priority = 1100)
|
||||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private int minY;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int height;
|
private int height;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int minBuildHeight;
|
private int maxY;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int maxBuildHeight;
|
private int minSectionY;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int minSection;
|
private int maxSectionY;
|
||||||
|
|
||||||
@Unique
|
|
||||||
private int maxSection;
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int sectionsCount;
|
private int sectionsCount;
|
||||||
@@ -47,12 +47,17 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
|||||||
private void init(final CallbackInfo ci,
|
private void init(final CallbackInfo ci,
|
||||||
@Local(ordinal = 0, argsOnly = true) final Holder<DimensionType> dimensionTypeHolder) {
|
@Local(ordinal = 0, argsOnly = true) final Holder<DimensionType> dimensionTypeHolder) {
|
||||||
final DimensionType dimType = dimensionTypeHolder.value();
|
final DimensionType dimType = dimensionTypeHolder.value();
|
||||||
|
this.minY = dimType.minY();
|
||||||
this.height = dimType.height();
|
this.height = dimType.height();
|
||||||
this.minBuildHeight = dimType.minY();
|
this.maxY = this.minY + this.height - 1;
|
||||||
this.maxBuildHeight = this.minBuildHeight + this.height;
|
this.minSectionY = this.minY >> 4;
|
||||||
this.minSection = this.minBuildHeight >> 4;
|
this.maxSectionY = this.maxY >> 4;
|
||||||
this.maxSection = ((this.maxBuildHeight - 1) >> 4) + 1;
|
this.sectionsCount = this.maxSectionY - this.minSectionY + 1;
|
||||||
this.sectionsCount = this.maxSection - this.minSection;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinY() {
|
||||||
|
return this.minY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,52 +66,52 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMinBuildHeight() {
|
public int getMaxY() {
|
||||||
return this.minBuildHeight;
|
return this.maxY;
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
@Override
|
||||||
public int getSectionsCount() {
|
public int getSectionsCount() {
|
||||||
return this.sectionsCount;
|
return this.sectionsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinSectionY() {
|
||||||
|
return this.minSectionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxSectionY() {
|
||||||
|
return this.maxSectionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsideBuildHeight(final int blockY) {
|
||||||
|
return blockY >= this.minY && blockY <= this.maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOutsideBuildHeight(final BlockPos pos) {
|
||||||
|
return this.isOutsideBuildHeight(pos.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOutsideBuildHeight(final int blockY) {
|
||||||
|
return blockY < this.minY || blockY > this.maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSectionIndex(final int blockY) {
|
||||||
|
return (blockY >> 4) - this.minSectionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSectionIndexFromSectionY(final int sectionY) {
|
||||||
|
return sectionY - this.minSectionY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSectionYFromSectionIndex(final int sectionIdx) {
|
||||||
|
return sectionIdx + this.minSectionY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.loading_screen;
|
package ca.spottedleaf.moonrise.mixin.loading_screen;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
|
||||||
import net.minecraft.client.multiplayer.LevelLoadStatusManager;
|
import net.minecraft.client.multiplayer.LevelLoadStatusManager;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
@@ -26,9 +25,6 @@ abstract class LevelLoadStatusManagerMixin {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void immediatelyClose(final CallbackInfo ci) {
|
private void immediatelyClose(final CallbackInfo ci) {
|
||||||
if (!ConfigHolder.getConfig().misc.immediatelyCloseLoadingScreen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.status == LevelLoadStatusManager.Status.WAITING_FOR_SERVER) {
|
if (this.status == LevelLoadStatusManager.Status.WAITING_FOR_SERVER) {
|
||||||
this.status = LevelLoadStatusManager.Status.LEVEL_READY;
|
this.status = LevelLoadStatusManager.Status.LEVEL_READY;
|
||||||
ci.cancel();
|
ci.cancel();
|
||||||
|
|||||||
@@ -20,16 +20,17 @@ import org.spongepowered.asm.mixin.Overwrite;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Mixin(PoiManager.class)
|
@Mixin(PoiManager.class)
|
||||||
abstract class PoiManagerMixin extends SectionStorage<PoiSection> {
|
abstract class PoiManagerMixin extends SectionStorage<PoiSection, PoiSection.Packed> {
|
||||||
|
|
||||||
public PoiManagerMixin(SimpleRegionStorage simpleRegionStorage, Function<Runnable, Codec<PoiSection>> function, Function<Runnable, PoiSection> function2, RegistryAccess registryAccess, ChunkIOErrorReporter chunkIOErrorReporter, LevelHeightAccessor levelHeightAccessor) {
|
public PoiManagerMixin(final SimpleRegionStorage simpleRegionStorage, final Codec<PoiSection.Packed> codec, final Function<PoiSection, PoiSection.Packed> function, final BiFunction<PoiSection.Packed, Runnable, PoiSection> biFunction, final Function<Runnable, PoiSection> function2, final RegistryAccess registryAccess, final ChunkIOErrorReporter chunkIOErrorReporter, final LevelHeightAccessor levelHeightAccessor) {
|
||||||
super(simpleRegionStorage, function, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
super(simpleRegionStorage, codec, function, biFunction, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ abstract class MinecraftMixin extends ReentrantBlockableEventLoop<Runnable> impl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cir.setReturnValue(ret == null || ret == InactiveProfiler.INSTANCE ? this.leafProfiler : ProfilerFiller.tee(this.leafProfiler, ret));
|
cir.setReturnValue(ret == null || ret == InactiveProfiler.INSTANCE ? this.leafProfiler : ProfilerFiller.combine(this.leafProfiler, ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import org.spongepowered.asm.mixin.Shadow;
|
|||||||
abstract class BiomeMixin {
|
abstract class BiomeMixin {
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
protected abstract float getHeightAdjustedTemperature(BlockPos blockPos);
|
protected abstract float getHeightAdjustedTemperature(BlockPos blockPos, int seaLevel);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Cache appears ineffective
|
* @reason Cache appears ineffective
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public float getTemperature(final BlockPos pos) {
|
public float getTemperature(final BlockPos pos, final int seaLevel) {
|
||||||
return this.getHeightAdjustedTemperature(pos);
|
return this.getHeightAdjustedTemperature(pos, seaLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import net.minecraft.core.RegistryAccess;
|
|||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.util.RandomSource;
|
import net.minecraft.util.RandomSource;
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
@@ -27,13 +26,12 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
@Mixin(ServerLevel.class)
|
@Mixin(ServerLevel.class)
|
||||||
abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
||||||
|
|
||||||
protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
protected ServerLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||||
abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implements StarlightAbstractBlockState {
|
abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implements StarlightAbstractBlockState {
|
||||||
|
|
||||||
|
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
|
||||||
|
super(object, reference2ObjectArrayMap, mapCodec);
|
||||||
|
}
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
private boolean useShapeForLightOcclusion;
|
private boolean useShapeForLightOcclusion;
|
||||||
@@ -27,29 +31,26 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
|||||||
@Final
|
@Final
|
||||||
private boolean canOcclude;
|
private boolean canOcclude;
|
||||||
|
|
||||||
@Shadow
|
|
||||||
protected BlockBehaviour.BlockStateBase.Cache cache;
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private boolean isConditionallyFullOpaque;
|
private boolean isConditionallyFullOpaque;
|
||||||
|
|
||||||
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
|
|
||||||
super(object, reference2ObjectArrayMap, mapCodec);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises our light state for this block.
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "initCache",
|
|
||||||
at = @At("RETURN")
|
|
||||||
)
|
|
||||||
public void initLightAccessState(final CallbackInfo ci) {
|
|
||||||
this.isConditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean starlight$isConditionallyFullOpaque() {
|
public final boolean starlight$isConditionallyFullOpaque() {
|
||||||
return this.isConditionallyFullOpaque;
|
return this.isConditionallyFullOpaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Initialises our light state for this block.
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "initCache",
|
||||||
|
at = @At(
|
||||||
|
value = "RETURN"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public void initLightAccessState(final CallbackInfo ci) {
|
||||||
|
this.isConditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,16 @@ package ca.spottedleaf.moonrise.mixin.starlight.chunk;
|
|||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk;
|
import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk;
|
||||||
import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray;
|
import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.LevelHeightAccessor;
|
||||||
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
import net.minecraft.world.level.chunk.UpgradeData;
|
import net.minecraft.world.level.chunk.UpgradeData;
|
||||||
|
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
@@ -18,8 +23,8 @@ abstract class ImposterProtoChunkMixin extends ProtoChunk implements StarlightCh
|
|||||||
@Shadow
|
@Shadow
|
||||||
private LevelChunk wrapped;
|
private LevelChunk wrapped;
|
||||||
|
|
||||||
public ImposterProtoChunkMixin(final LevelChunk levelChunk, final boolean bl) {
|
public ImposterProtoChunkMixin(final ChunkPos chunkPos, final UpgradeData upgradeData, final LevelHeightAccessor levelHeightAccessor, final Registry<Biome> registry, @Nullable final BlendingData blendingData) {
|
||||||
super(levelChunk.getPos(), UpgradeData.EMPTY, levelChunk, levelChunk.getLevel().registryAccess().registryOrThrow(Registries.BIOME), levelChunk.getBlendingData());
|
super(chunkPos, upgradeData, levelHeightAccessor, registry, blendingData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -54,7 +54,24 @@ abstract class LevelLightEngineMixin implements LightEventListener, StarLightLig
|
|||||||
* TODO since this is a constructor inject, check on update for new constructors
|
* TODO since this is a constructor inject, check on update for new constructors
|
||||||
*/
|
*/
|
||||||
@Inject(
|
@Inject(
|
||||||
method = "<init>", at = @At("TAIL")
|
method = "<init>()V",
|
||||||
|
at = @At(
|
||||||
|
value = "TAIL"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public void constructEmpty(final CallbackInfo ci) {
|
||||||
|
this.lightEngine = new StarLightInterface(null, false, false, (LevelLightEngine)(Object)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* TODO since this is a constructor inject, check on update for new constructors
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "<init>(Lnet/minecraft/world/level/chunk/LightChunkGetter;ZZ)V",
|
||||||
|
at = @At(
|
||||||
|
value = "TAIL"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
public void construct(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight,
|
public void construct(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight,
|
||||||
final CallbackInfo ci) {
|
final CallbackInfo ci) {
|
||||||
@@ -179,8 +196,10 @@ abstract class LevelLightEngineMixin implements LightEventListener, StarLightLig
|
|||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public boolean lightOnInSection(final SectionPos pos) {
|
public boolean lightOnInColumn(final long pos) {
|
||||||
final long key = CoordinateUtils.getChunkKey(pos.getX(), pos.getZ());
|
final long key = CoordinateUtils.getChunkKey(
|
||||||
|
CoordinateUtils.getChunkSectionX(pos), CoordinateUtils.getChunkSectionZ(pos)
|
||||||
|
);
|
||||||
return (!this.lightEngine.hasBlockLight() || this.blockLightMap.get(key) != null) && (!this.lightEngine.hasSkyLight() || this.skyLightMap.get(key) != null);
|
return (!this.lightEngine.hasBlockLight() || this.blockLightMap.get(key) != null) && (!this.lightEngine.hasSkyLight() || this.skyLightMap.get(key) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.network.protocol.Packet;
|
import net.minecraft.network.protocol.Packet;
|
||||||
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
|
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
|
||||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
|
import net.minecraft.server.level.ChunkTaskDispatcher;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||||
import net.minecraft.util.thread.ProcessorHandle;
|
import net.minecraft.util.thread.ConsecutiveExecutor;
|
||||||
import net.minecraft.util.thread.ProcessorMailbox;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.LightLayer;
|
import net.minecraft.world.level.LightLayer;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
@@ -50,10 +49,10 @@ import java.util.function.Supplier;
|
|||||||
abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider {
|
abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider {
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private ProcessorMailbox<Runnable> taskMailbox;
|
private ConsecutiveExecutor consecutiveExecutor;
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> sorterMailbox;
|
private ChunkTaskDispatcher taskDispatcher;
|
||||||
|
|
||||||
public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) {
|
public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) {
|
||||||
super(chunkProvider, hasBlockLight, hasSkyLight);
|
super(chunkProvider, hasBlockLight, hasSkyLight);
|
||||||
@@ -184,8 +183,8 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void initHook(final CallbackInfo ci) {
|
private void initHook(final CallbackInfo ci) {
|
||||||
this.taskMailbox = null;
|
this.consecutiveExecutor = null;
|
||||||
this.sorterMailbox = null;
|
this.taskDispatcher = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
protected abstract void applyLightData(final int chunkX, final int chunkZ, final ClientboundLightUpdatePacketData clientboundLightUpdatePacketData);
|
protected abstract void applyLightData(final int chunkX, final int chunkZ, final ClientboundLightUpdatePacketData clientboundLightUpdatePacketData,
|
||||||
|
final boolean markDirty);
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
protected abstract void enableChunkLight(final LevelChunk levelChunk, final int chunkX, final int chunkZ);
|
protected abstract void enableChunkLight(final LevelChunk levelChunk, final int chunkX, final int chunkZ);
|
||||||
@@ -127,7 +128,7 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// load in light data from packet immediately
|
// load in light data from packet immediately
|
||||||
this.applyLightData(chunkX, chunkZ, clientboundLevelChunkWithLightPacket.getLightData());
|
this.applyLightData(chunkX, chunkZ, clientboundLevelChunkWithLightPacket.getLightData(), false);
|
||||||
((StarLightLightingProvider)this.level.getChunkSource().getLightEngine()).starlight$clientChunkLoad(new ChunkPos(chunkX, chunkZ), chunk);
|
((StarLightLightingProvider)this.level.getChunkSource().getLightEngine()).starlight$clientChunkLoad(new ChunkPos(chunkX, chunkZ), chunk);
|
||||||
|
|
||||||
// we need this for the update chunk status call, so that it can tell starlight what sections are empty and such
|
// we need this for the update chunk status call, so that it can tell starlight what sections are empty and such
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user