Compare commits
29 Commits
v0.1.0-bet
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceb4936d9d | ||
|
|
3cb888e894 | ||
|
|
7bedc1a7de | ||
|
|
718f6e1369 | ||
|
|
da9ab708a6 | ||
|
|
f22335f0b6 | ||
|
|
a3f2328000 | ||
|
|
529b9a44bb | ||
|
|
1e9a6504a1 | ||
|
|
9c46dcbb94 | ||
|
|
9a1e04389a | ||
|
|
29084d8e3f | ||
|
|
41790ecf1a | ||
|
|
8af7bccdfd | ||
|
|
5f9b3571f8 | ||
|
|
9adfb2514d | ||
|
|
bf2cd1c571 | ||
|
|
dad9a5c2eb | ||
|
|
a3acd46ee1 | ||
|
|
3e8cb80336 | ||
|
|
5c3e713be7 | ||
|
|
284631c321 | ||
|
|
666c4cb1a3 | ||
|
|
19b523eecd | ||
|
|
4a748778dc | ||
|
|
a70073ae3e | ||
|
|
6724814c02 | ||
|
|
f32a08738e | ||
|
|
56e48ed069 |
53
.github/ISSUE_TEMPLATE/a_incompatibility.yml
vendored
Normal file
53
.github/ISSUE_TEMPLATE/a_incompatibility.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
name: Mod Incompatibility
|
||||||
|
description: Report an incompatibility with another mod.
|
||||||
|
title: '[Incompatibility: <NeoForge/Fabric/NeoForge & Fabric>]: Mod Name'
|
||||||
|
labels:
|
||||||
|
- mod-incompatibility
|
||||||
|
- needs-triage
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
id: mc_ver
|
||||||
|
attributes:
|
||||||
|
label: Minecraft Version
|
||||||
|
placeholder: e.g. 1.21.1 or 1.21.2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: moonrise_ver
|
||||||
|
attributes:
|
||||||
|
label: Moonrise Version
|
||||||
|
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: mod_loader
|
||||||
|
attributes:
|
||||||
|
label: Mod Loader
|
||||||
|
description: >-
|
||||||
|
The mod loader(s) this conflict happens with. When selecting 'Both' it
|
||||||
|
is expected you will submit logs for both as well. Be sure to update the
|
||||||
|
issue title.
|
||||||
|
options:
|
||||||
|
- NeoForge
|
||||||
|
- Fabric
|
||||||
|
- Both (NeoForge and Fabric)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: logs_crashes
|
||||||
|
attributes:
|
||||||
|
label: Logs and Crash Reports
|
||||||
|
description: >-
|
||||||
|
Include links to all relevant logs and crash reports (prefer using
|
||||||
|
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||||
|
applicable fill in 'N/A' (it is expected you will provide context
|
||||||
|
below).
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Provide any relevant context, including version of the conflicting mod.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
55
.github/ISSUE_TEMPLATE/b_bug.yml
vendored
Normal file
55
.github/ISSUE_TEMPLATE/b_bug.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Report a bug or Vanilla behavior parity issue.
|
||||||
|
title: '[Bug: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
- needs-triage
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
id: mc_ver
|
||||||
|
attributes:
|
||||||
|
label: Minecraft Version
|
||||||
|
placeholder: e.g. 1.21.1 or 1.21.2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: moonrise_ver
|
||||||
|
attributes:
|
||||||
|
label: Moonrise Version
|
||||||
|
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: mod_loader
|
||||||
|
attributes:
|
||||||
|
label: Mod Loader
|
||||||
|
description: >-
|
||||||
|
The mod loader(s) this bug happens with. When selecting 'Both' it
|
||||||
|
is expected you will submit logs for both as well. Be sure to update the
|
||||||
|
issue title.
|
||||||
|
options:
|
||||||
|
- NeoForge
|
||||||
|
- Fabric
|
||||||
|
- Both (NeoForge and Fabric)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: logs_crashes
|
||||||
|
attributes:
|
||||||
|
label: Logs and Crash Reports
|
||||||
|
description: >-
|
||||||
|
Include links to all relevant logs and crash reports (prefer using
|
||||||
|
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||||
|
applicable fill in 'N/A' (it is expected you will provide context
|
||||||
|
below).
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: >-
|
||||||
|
Provide any relevant context, including expected vs. actual behavior,
|
||||||
|
and reproduction steps.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
65
.github/ISSUE_TEMPLATE/c_performance.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/c_performance.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Performance Problem
|
||||||
|
description: Report a performance problem that doesn't fall under the bug category.
|
||||||
|
title: '[Performance: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||||
|
labels:
|
||||||
|
- performance
|
||||||
|
- needs-triage
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
id: mc_ver
|
||||||
|
attributes:
|
||||||
|
label: Minecraft Version
|
||||||
|
placeholder: e.g. 1.21.1, 1.21.2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: moonrise_ver
|
||||||
|
attributes:
|
||||||
|
label: Moonrise Version
|
||||||
|
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: mod_loader
|
||||||
|
attributes:
|
||||||
|
label: Mod Loader
|
||||||
|
description: >-
|
||||||
|
The mod loader(s) this bug happens with. When selecting 'Both' it is
|
||||||
|
expected you have tested both as well. Be sure to update the issue
|
||||||
|
title.
|
||||||
|
options:
|
||||||
|
- NeoForge
|
||||||
|
- Fabric
|
||||||
|
- Both (NeoForge and Fabric)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: logs_crashes
|
||||||
|
attributes:
|
||||||
|
label: Logs and Crash Reports
|
||||||
|
description: >-
|
||||||
|
Include links to all relevant logs and crash reports (prefer using
|
||||||
|
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||||
|
applicable fill in 'N/A' (it is expected you will provide context
|
||||||
|
below).
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: profiler
|
||||||
|
attributes:
|
||||||
|
label: Profiler Data
|
||||||
|
description: >-
|
||||||
|
Provide profiler data showing the problem, from Spark or other tools. In
|
||||||
|
case this is not applicable fill in 'N/A' (it is expected you will
|
||||||
|
provide context below).
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: >-
|
||||||
|
Provide any relevant context, including reproduction steps, world data,
|
||||||
|
videos, etc.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Discord
|
||||||
|
url: https://discord.gg/tuinity
|
||||||
|
about: If you are unsure about something or have general questions, ask in our Discord before opening an issue.
|
||||||
34
.github/ISSUE_TEMPLATE/d_feature.yml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/d_feature.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: Request a new feature.
|
||||||
|
title: '[Feature Request: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
- needs-triage
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
id: mc_ver
|
||||||
|
attributes:
|
||||||
|
label: Minecraft Version
|
||||||
|
placeholder: e.g. 1.21.1, 1.21.2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: mod_loader
|
||||||
|
attributes:
|
||||||
|
label: Mod Loader
|
||||||
|
description: The mod loader(s) this feature request is relevant for.
|
||||||
|
options:
|
||||||
|
- NeoForge
|
||||||
|
- Fabric
|
||||||
|
- Both (NeoForge and Fabric)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: >-
|
||||||
|
Describe the feature request in detail, including alternatives
|
||||||
|
considered.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
@@ -24,7 +24,6 @@ patches. Listed below are notable patches:
|
|||||||
| Mod | Status |
|
| Mod | Status |
|
||||||
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| Lithium | <details><summary>✅ compatible</summary>Lithium optimises many of the same parts of the game as Moonrise, for example the chunk system. Moonrise will automatically disable conflicting parts of Lithium. This mechanism needs to be manually validated for each Moonrise and Lithium release.</details> |
|
| Lithium | <details><summary>✅ compatible</summary>Lithium optimises many of the same parts of the game as Moonrise, for example the chunk system. Moonrise will automatically disable conflicting parts of Lithium. This mechanism needs to be manually validated for each Moonrise and Lithium release.</details> |
|
||||||
| Radium | <details><summary>✅ compatible</summary>Radium is an unofficial port of Lithium to NeoForge. Radium will automatically disable conflicting parts of itself when Moonrise is present. Any compatibility issues should be reported to Radium first.</details> |
|
|
||||||
| FerriteCore | <details><summary>📝 requires config changes</summary>In `config/ferritecore-mixin.toml`:<br/>Set `replaceNeighborLookup` and `replacePropertyMap` to `false`</details> |
|
| FerriteCore | <details><summary>📝 requires config changes</summary>In `config/ferritecore-mixin.toml`:<br/>Set `replaceNeighborLookup` and `replacePropertyMap` to `false`</details> |
|
||||||
| C2ME | <details><summary>❌ incompatible</summary>C2ME is based around modifications to the chunk system, which Moonrise replaces wholesale. This makes them fundamentally incompatible.</details> |
|
| C2ME | <details><summary>❌ incompatible</summary>C2ME is based around modifications to the chunk system, which Moonrise replaces wholesale. This makes them fundamentally incompatible.</details> |
|
||||||
|
|
||||||
|
|||||||
28
build.gradle
28
build.gradle
@@ -98,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")
|
||||||
@@ -126,6 +127,31 @@ subprojects {
|
|||||||
minecraftVersions = supportedMcVersions
|
minecraftVersions = supportedMcVersions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup a run with lithium for compatibility testing
|
||||||
|
sourceSets.create("lithium")
|
||||||
|
configurations.create("lithium")
|
||||||
|
loom {
|
||||||
|
createRemapConfigurations(sourceSets.lithium)
|
||||||
|
runs {
|
||||||
|
register("lithiumClient") {
|
||||||
|
client()
|
||||||
|
property "mixin.debug", "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||||
|
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
String coordinates = "maven.modrinth:lithium:"
|
||||||
|
if (getProject().name == "Moonrise-NeoForge") {
|
||||||
|
coordinates += rootProject.neo_lithium_version
|
||||||
|
} else {
|
||||||
|
coordinates += rootProject.fabric_lithium_version
|
||||||
|
}
|
||||||
|
modLithiumRuntimeOnly coordinates
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loom.runs.all {
|
loom.runs.all {
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ dependencies {
|
|||||||
|
|
||||||
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.fabric.api
|
modImplementation fabricApiLibs.command.api.v2
|
||||||
|
modImplementation fabricApiLibs.lifecycle.events.v1
|
||||||
include fabricApiLibs.command.api.v2
|
include fabricApiLibs.command.api.v2
|
||||||
include fabricApiLibs.base
|
include fabricApiLibs.base
|
||||||
}
|
}
|
||||||
@@ -63,22 +64,3 @@ publishMods {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a run with lithium for compatibility testing
|
|
||||||
sourceSets.create("lithium")
|
|
||||||
configurations.create("lithium")
|
|
||||||
loom {
|
|
||||||
createRemapConfigurations(sourceSets.lithium)
|
|
||||||
runs {
|
|
||||||
register("lithiumClient") {
|
|
||||||
client()
|
|
||||||
property "mixin.debug", "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
|
||||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
modLithiumRuntimeOnly "maven.modrinth:lithium:${rootProject.lithium_version}"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,44 +2,37 @@ 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.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.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;
|
||||||
|
|
||||||
public final class FabricHooks implements PlatformHooks {
|
public final class FabricHooks implements PlatformHooks {
|
||||||
|
|
||||||
public interface OnExplosionDetonate {
|
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
|
||||||
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() {
|
||||||
@@ -58,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;
|
||||||
@@ -85,7 +68,12 @@ public final class FabricHooks implements PlatformHooks {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
|
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
|
||||||
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel) newChunk.getLevel(), newChunk);
|
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||||
|
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel)newChunk.getLevel(), newChunk);
|
||||||
|
if (!(original instanceof ImposterProtoChunk)) {
|
||||||
|
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -94,17 +82,19 @@ 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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void chunkUnloadFromWorld(final LevelChunk chunk) {
|
public void chunkUnloadFromWorld(final LevelChunk chunk) {
|
||||||
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel) chunk.getLevel(), chunk);
|
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||||
|
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel)chunk.getLevel(), chunk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package ca.spottedleaf.moonrise.fabric.mixin.collisions;
|
package ca.spottedleaf.moonrise.fabric.mixin.collisions;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
@@ -9,7 +9,6 @@ import net.minecraft.world.entity.Entity;
|
|||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
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.ChunkSource;
|
import net.minecraft.world.level.chunk.ChunkSource;
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
@@ -61,7 +60,7 @@ abstract class EntityMixin {
|
|||||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||||
|
|
||||||
final Level world = this.level;
|
final Level world = this.level;
|
||||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
final int minSection = WorldUtil.getMinSection(world);
|
||||||
|
|
||||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||||
@@ -69,7 +68,7 @@ abstract class EntityMixin {
|
|||||||
|
|
||||||
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
|
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
|
||||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||||
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||||
|
|
||||||
final boolean isPushable = this.isPushedByFluid();
|
final boolean isPushable = this.isPushedByFluid();
|
||||||
|
|||||||
@@ -36,39 +36,39 @@
|
|||||||
"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.alloc.chunk_ticking": false,
|
||||||
|
"mixin.alloc.deep_passengers": false,
|
||||||
|
"mixin.alloc.entity_tracker": false,
|
||||||
|
"mixin.block.flatten_states": false,
|
||||||
|
"mixin.chunk.entity_class_groups": false,
|
||||||
|
"mixin.chunk.no_validation": false,
|
||||||
"mixin.collections.chunk_tickets": false,
|
"mixin.collections.chunk_tickets": false,
|
||||||
"mixin.collections.entity_ticking": false,
|
"mixin.collections.entity_ticking": false,
|
||||||
"mixin.world.temperature_cache": false,
|
|
||||||
"mixin.world.block_entity_ticking": false,
|
|
||||||
"mixin.world.chunk_access": false,
|
|
||||||
"mixin.world.tick_scheduler": false,
|
|
||||||
"mixin.world.explosions.block_raycast": false,
|
|
||||||
"mixin.world.explosions.cache_exposure": false,
|
|
||||||
"mixin.block.flatten_states": false,
|
|
||||||
"mixin.math.fast_util": false,
|
|
||||||
"mixin.math.fast_blockpos": false,
|
|
||||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
|
||||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
|
||||||
"mixin.alloc.deep_passengers": false,
|
|
||||||
"mixin.alloc.chunk_ticking": false,
|
|
||||||
"mixin.alloc.entity_tracker": false,
|
|
||||||
"mixin.shapes.blockstate_cache": false,
|
|
||||||
"mixin.shapes.specialized_shapes": false,
|
|
||||||
"mixin.shapes.optimized_matching": false,
|
|
||||||
"mixin.ai.poi": false,
|
|
||||||
"mixin.chunk.no_validation": false,
|
|
||||||
"mixin.chunk.entity_class_groups": false,
|
|
||||||
"mixin.util.block_tracking": false,
|
|
||||||
"mixin.util.entity_movement_tracking": false,
|
|
||||||
"mixin.entity.replace_entitytype_predicates": false,
|
|
||||||
"mixin.entity.collisions.intersection": false,
|
"mixin.entity.collisions.intersection": false,
|
||||||
"mixin.entity.collisions.movement": false,
|
"mixin.entity.collisions.movement": false,
|
||||||
"mixin.entity.collisions.unpushable_cramming": false
|
"mixin.entity.collisions.unpushable_cramming": false,
|
||||||
|
"mixin.entity.replace_entitytype_predicates": false,
|
||||||
|
"mixin.math.fast_blockpos": false,
|
||||||
|
"mixin.math.fast_util": false,
|
||||||
|
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||||
|
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||||
|
"mixin.shapes.blockstate_cache": false,
|
||||||
|
"mixin.shapes.optimized_matching": false,
|
||||||
|
"mixin.shapes.specialized_shapes": false,
|
||||||
|
"mixin.util.block_tracking": false,
|
||||||
|
"mixin.util.entity_movement_tracking": false,
|
||||||
|
"mixin.world.block_entity_ticking": false,
|
||||||
|
"mixin.world.chunk_access": false,
|
||||||
|
"mixin.world.explosions.block_raycast": false,
|
||||||
|
"mixin.world.explosions.cache_exposure": false,
|
||||||
|
"mixin.world.temperature_cache": false,
|
||||||
|
"mixin.world.tick_scheduler": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +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.62
|
neoforge_version=21.3.0-beta
|
||||||
|
fabric_api_version=0.107.0+1.21.3
|
||||||
snakeyaml_version=2.2
|
snakeyaml_version=2.2
|
||||||
concurrentutil_version=0.0.2-SNAPSHOT
|
concurrentutil_version=0.0.2-SNAPSHOT
|
||||||
cloth_version=15.0.128
|
cloth_version=16.0.141
|
||||||
lithium_version=mc1.21.1-0.13.1
|
modmenu_version=12.0.0-beta.1
|
||||||
|
# version ids from modrinth
|
||||||
|
fabric_lithium_version=mc1.21.1-0.14.0-beta.1
|
||||||
|
neo_lithium_version=BrMIoIMv
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=0.1.0-beta.6
|
mod_version=0.2.0-beta.2
|
||||||
maven_group=ca.spottedleaf.moonrise
|
maven_group=ca.spottedleaf.moonrise
|
||||||
archives_base_name=moonrise
|
archives_base_name=moonrise
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ca.spottedleaf.moonrise.neoforge.mixin.collisions;
|
package ca.spottedleaf.moonrise.neoforge.mixin.collisions;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
import ca.spottedleaf.moonrise.neoforge.patches.collisions.FluidPushCalculation;
|
import ca.spottedleaf.moonrise.neoforge.patches.collisions.FluidPushCalculation;
|
||||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
|
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@@ -10,7 +10,6 @@ import net.minecraft.world.entity.Entity;
|
|||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
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.ChunkSource;
|
import net.minecraft.world.level.chunk.ChunkSource;
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
@@ -59,7 +58,7 @@ abstract class EntityMixin implements IEntityExtension {
|
|||||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||||
|
|
||||||
final Level world = this.level;
|
final Level world = this.level;
|
||||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
final int minSection = WorldUtil.getMinSection(world);
|
||||||
|
|
||||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||||
@@ -67,7 +66,7 @@ abstract class EntityMixin implements IEntityExtension {
|
|||||||
|
|
||||||
// note: bounds are exclusive in Vanilla, so we subtract 1
|
// note: bounds are exclusive in Vanilla, so we subtract 1
|
||||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||||
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||||
|
|
||||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||||
|
|||||||
@@ -24,7 +24,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"
|
||||||
|
|
||||||
@@ -36,10 +36,6 @@ type = "incompatible"
|
|||||||
modId = "starlight"
|
modId = "starlight"
|
||||||
type = "incompatible"
|
type = "incompatible"
|
||||||
|
|
||||||
[[dependencies.moonrise]]
|
|
||||||
modId = "lithium"
|
|
||||||
type = "incompatible"
|
|
||||||
|
|
||||||
[[dependencies.moonrise]]
|
[[dependencies.moonrise]]
|
||||||
# unofficial lithium port
|
# unofficial lithium port
|
||||||
modId = "canary"
|
modId = "canary"
|
||||||
@@ -54,3 +50,33 @@ config = "moonrise.mixins.json"
|
|||||||
|
|
||||||
[[mixins]]
|
[[mixins]]
|
||||||
config = "moonrise-neoforge.mixins.json"
|
config = "moonrise-neoforge.mixins.json"
|
||||||
|
|
||||||
|
[mods."lithium:options"]
|
||||||
|
"mixin.ai.poi" = false
|
||||||
|
"mixin.alloc.chunk_ticking" = false
|
||||||
|
"mixin.alloc.deep_passengers" = false
|
||||||
|
"mixin.alloc.entity_tracker" = false
|
||||||
|
"mixin.block.flatten_states" = false
|
||||||
|
"mixin.chunk.entity_class_groups" = false
|
||||||
|
"mixin.chunk.no_validation" = false
|
||||||
|
"mixin.collections.chunk_tickets" = false
|
||||||
|
"mixin.collections.entity_ticking" = false
|
||||||
|
"mixin.entity.collisions.intersection" = false
|
||||||
|
"mixin.entity.collisions.movement" = false
|
||||||
|
"mixin.entity.collisions.unpushable_cramming" = false
|
||||||
|
"mixin.entity.replace_entitytype_predicates" = false
|
||||||
|
"mixin.math.fast_blockpos" = false
|
||||||
|
"mixin.math.fast_util" = false
|
||||||
|
"mixin.minimal_nonvanilla.collisions.empty_space" = false
|
||||||
|
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets" = false
|
||||||
|
"mixin.shapes.blockstate_cache" = false
|
||||||
|
"mixin.shapes.optimized_matching" = false
|
||||||
|
"mixin.shapes.specialized_shapes" = false
|
||||||
|
"mixin.util.block_tracking" = false
|
||||||
|
"mixin.util.entity_movement_tracking" = false
|
||||||
|
"mixin.world.block_entity_ticking" = false
|
||||||
|
"mixin.world.chunk_access" = false
|
||||||
|
"mixin.world.explosions.block_raycast" = false
|
||||||
|
"mixin.world.explosions.cache_exposure" = false
|
||||||
|
"mixin.world.temperature_cache" = false
|
||||||
|
"mixin.world.tick_scheduler" = false
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||||
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.295" apply false
|
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.297" apply false
|
||||||
id 'com.gradleup.shadow' version '8.3.0' apply false
|
id 'com.gradleup.shadow' version '8.3.0' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
versionCatalogs {
|
versionCatalogs {
|
||||||
create("fabricApiLibs") {
|
create("fabricApiLibs") {
|
||||||
from("net.fabricmc.fabric-api:fabric-api-catalog:0.103.0+1.21.1")
|
from("net.fabricmc.fabric-api:fabric-api-catalog:${fabric_api_version}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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.adapter.TypeAdapterRegistry;
|
||||||
import ca.spottedleaf.moonrise.common.config.config.YamlConfig;
|
import ca.spottedleaf.moonrise.common.config.config.YamlConfig;
|
||||||
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
|
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
|
||||||
@@ -13,7 +14,7 @@ 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 {
|
||||||
@@ -25,7 +26,7 @@ public final class ConfigHolder {
|
|||||||
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
|
||||||
@@ -33,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();
|
||||||
|
|||||||
@@ -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,11 +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) {
|
||||||
|
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) {
|
||||||
|
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()));
|
||||||
|
|||||||
@@ -8,13 +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.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;
|
||||||
@@ -45,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;
|
||||||
@@ -59,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();
|
||||||
@@ -86,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,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
|
||||||
|
|||||||
@@ -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()) {
|
||||||
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
|
return false;
|
||||||
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
|
|
||||||
}
|
}
|
||||||
super.setUnsaved(needsSaving);
|
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
|
||||||
|
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,4 +94,4 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
|||||||
private void redirectClose(final SimpleRegionStorage instance) throws IOException {
|
private void redirectClose(final SimpleRegionStorage instance) throws IOException {
|
||||||
this.moonrise$close();
|
this.moonrise$close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -17,12 +17,12 @@ abstract class ChunkSerializerMixin {
|
|||||||
* @see ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask
|
* @see ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask
|
||||||
*/
|
*/
|
||||||
@Redirect(
|
@Redirect(
|
||||||
method = "read",
|
method = "read",
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
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<>();
|
||||||
|
|
||||||
@@ -261,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,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,
|
||||||
@@ -335,60 +341,13 @@ 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(
|
at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;isNaturalSpawningAllowed(Lnet/minecraft/world/level/ChunkPos;)Z"
|
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)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(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
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,110 +33,18 @@ 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
|
.moonrise$getRealChunkHolder().holderData;
|
||||||
*/
|
|
||||||
@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;
|
|
||||||
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
||||||
if (nearbyPlayers == null) {
|
if (nearbyPlayers == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -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) {
|
||||||
at = @At(
|
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||||
value = "INVOKE",
|
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
||||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
|
||||||
ordinal = 0,
|
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
||||||
shift = At.Shift.AFTER
|
final int size = tickingChunks.size();
|
||||||
)
|
|
||||||
|
final ChunkMap chunkMap = this.chunkMap;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i];
|
||||||
|
final LevelChunk levelChunk = chunkAndHolder.chunk();
|
||||||
|
|
||||||
|
if (!this.isChunkNearPlayer(chunkMap, levelChunk.getPos(), levelChunk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.add(levelChunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
|
||||||
|
* function
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Redirect(
|
||||||
|
method = "tickChunks()V",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
|||||||
@Shadow
|
@Shadow
|
||||||
public abstract VoxelShape getCollisionShape(BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext);
|
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);
|
||||||
}
|
}
|
||||||
@@ -74,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,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
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
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 ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
|
||||||
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.ChunkAccess;
|
|
||||||
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;
|
||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
@@ -28,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 {
|
||||||
@@ -58,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) {
|
||||||
@@ -222,7 +208,7 @@ abstract class EntityMixin {
|
|||||||
final int maxChunkY = maxBlockY >> 4;
|
final int maxChunkY = maxBlockY >> 4;
|
||||||
final int maxChunkZ = maxBlockZ >> 4;
|
final int maxChunkZ = maxBlockZ >> 4;
|
||||||
|
|
||||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
final int minSection = WorldUtil.getMinSection(world);
|
||||||
final ChunkSource chunkSource = world.getChunkSource();
|
final ChunkSource chunkSource = world.getChunkSource();
|
||||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||||
|
|
||||||
@@ -297,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 = ((GetBlockLevel)world).moonrise$getMinSection();
|
|
||||||
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 = ((GetBlockLevel)world).moonrise$getMinSection();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||||
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.getblock.GetBlockLevel;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
@@ -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
|
||||||
@@ -139,7 +139,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
|||||||
int lastChunkY = Integer.MIN_VALUE;
|
int lastChunkY = Integer.MIN_VALUE;
|
||||||
int lastChunkZ = Integer.MIN_VALUE;
|
int lastChunkZ = Integer.MIN_VALUE;
|
||||||
|
|
||||||
final int minSection = ((GetBlockLevel)level).moonrise$getMinSection();
|
final int minSection = WorldUtil.getMinSection(level);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
currPos.set(currX, currY, currZ);
|
currPos.set(currX, currY, currZ);
|
||||||
@@ -334,13 +334,13 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Optional<BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
public Optional<BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
||||||
final int minSection = ((GetBlockLevel)(Level)(Object)this).moonrise$getMinSection();
|
final int minSection = WorldUtil.getMinSection((Level)(Object)this);
|
||||||
|
|
||||||
final int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
final int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||||
final int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
final int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||||
|
|
||||||
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1);
|
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1);
|
||||||
final int maxBlockY = Math.min((((GetBlockLevel)(Level)(Object)this).moonrise$getMaxSection() << 4) + 16, Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1);
|
final int maxBlockY = Math.min((WorldUtil.getMaxSection((Level)(Object)this) << 4) + 16, Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1);
|
||||||
|
|
||||||
final int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
final int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||||
final int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
final int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -50,25 +43,26 @@ abstract class LiquidBlockRendererMixin {
|
|||||||
} else {
|
} else {
|
||||||
// the extrusion includes the height
|
// the extrusion includes the height
|
||||||
heightShape = new ArrayVoxelShape(
|
heightShape = new ArrayVoxelShape(
|
||||||
Shapes.block().shape,
|
Shapes.block().shape,
|
||||||
CollisionUtil.ZERO_ONE,
|
CollisionUtil.ZERO_ONE,
|
||||||
DoubleArrayList.wrap(new double[] { 0.0, heightDouble }),
|
DoubleArrayList.wrap(new double[] { 0.0, heightDouble }),
|
||||||
CollisionUtil.ZERO_ONE
|
CollisionUtil.ZERO_ONE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
||||||
@@ -402,7 +397,7 @@ abstract class ExplosionMixin {
|
|||||||
|
|
||||||
if (cachedBlock.key != key) {
|
if (cachedBlock.key != key) {
|
||||||
final int cacheKey =
|
final int cacheKey =
|
||||||
(blockX & BLOCK_EXPLOSION_CACHE_MASK) |
|
(blockX & BLOCK_EXPLOSION_CACHE_MASK) |
|
||||||
(blockY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) |
|
(blockY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) |
|
||||||
(blockZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
|
(blockZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
|
||||||
cachedBlock = blockCache[cacheKey];
|
cachedBlock = blockCache[cacheKey];
|
||||||
@@ -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,39 @@ 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(
|
||||||
final Vec3 center = new Vec3(this.x, this.y, this.z);
|
method = "hurtEntities",
|
||||||
|
at = @At(
|
||||||
final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/level/ServerExplosion;getSeenPercent(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)F"
|
||||||
final PlatformHooks platformHooks = PlatformHooks.get();
|
)
|
||||||
|
)
|
||||||
platformHooks.onExplosion(this.level, (Explosion)(Object)this, entities, diameter);
|
private float useBetterSeenPercent(final Vec3 center, final Entity target) {
|
||||||
for (int i = 0, len = entities.size(); i < len; ++i) {
|
return this.getSeenFraction(center, target, this.directMappedBlockCache, this.mutablePos);
|
||||||
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,16 +114,16 @@ 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()) {
|
||||||
// don't even try to cache simple cases
|
// don't even try to cache simple cases
|
||||||
return true;
|
return true;
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ abstract class MappedRegistryMixin<T> {
|
|||||||
final RegistrationInfo registrationInfo,
|
final RegistrationInfo registrationInfo,
|
||||||
final CallbackInfoReturnable<Holder.Reference<T>> cir
|
final CallbackInfoReturnable<Holder.Reference<T>> cir
|
||||||
) {
|
) {
|
||||||
if (resourceKey.registryKey() == (Object) Registries.FLUID) {
|
if (resourceKey.registryKey() == (Object)Registries.FLUID) {
|
||||||
for (final FluidState possibleState : ((Fluid) object).getStateDefinition().getPossibleStates()) {
|
for (final FluidState possibleState : ((Fluid)object).getStateDefinition().getPossibleStates()) {
|
||||||
((FluidFluidState) (Object) possibleState).moonrise$initCaches();
|
((FluidFluidState)(Object)possibleState).moonrise$initCaches();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
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;
|
||||||
@@ -13,39 +14,25 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
|
|
||||||
// Higher priority to apply after Lithium mixin.world.inline_height.WorldMixin
|
// Higher priority to apply after Lithium mixin.world.inline_height.WorldMixin
|
||||||
@Mixin(value = Level.class, priority = 1100)
|
@Mixin(value = Level.class, priority = 1100)
|
||||||
abstract class LevelMixin implements GetBlockLevel, LevelAccessor, AutoCloseable {
|
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int minSection;
|
private int minY;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int maxSection;
|
private int height;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int minBuildHeight;
|
private int maxY;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private int maxBuildHeight;
|
private int minSectionY;
|
||||||
|
|
||||||
@Override
|
@Unique
|
||||||
public final int moonrise$getMinSection() {
|
private int maxSectionY;
|
||||||
return this.minSection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Unique
|
||||||
public final int moonrise$getMaxSection() {
|
private int sectionsCount;
|
||||||
return this.maxSection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final int moonrise$getMinBuildHeight() {
|
|
||||||
return this.minBuildHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final int moonrise$getMaxBuildHeight() {
|
|
||||||
return this.maxBuildHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Init min/max section
|
* @reason Init min/max section
|
||||||
@@ -54,23 +41,77 @@ abstract class LevelMixin implements GetBlockLevel, LevelAccessor, AutoCloseable
|
|||||||
@Inject(
|
@Inject(
|
||||||
method = "<init>",
|
method = "<init>",
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "RETURN"
|
value = "CTOR_HEAD"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void init(final CallbackInfo ci) {
|
private void init(final CallbackInfo ci,
|
||||||
this.minSection = WorldUtil.getMinSection(this);
|
@Local(ordinal = 0, argsOnly = true) final Holder<DimensionType> dimensionTypeHolder) {
|
||||||
this.maxSection = WorldUtil.getMaxSection(this);
|
final DimensionType dimType = dimensionTypeHolder.value();
|
||||||
this.minBuildHeight = this.getMinBuildHeight();
|
this.minY = dimType.minY();
|
||||||
this.maxBuildHeight = this.getMaxBuildHeight();
|
this.height = dimType.height();
|
||||||
|
this.maxY = this.minY + this.height - 1;
|
||||||
|
this.minSectionY = this.minY >> 4;
|
||||||
|
this.maxSectionY = this.maxY >> 4;
|
||||||
|
this.sectionsCount = this.maxSectionY - this.minSectionY + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOutsideBuildHeight(final int y) {
|
public int getMinY() {
|
||||||
return y < this.minBuildHeight || y >= this.maxBuildHeight;
|
return this.minY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOutsideBuildHeight(final BlockPos blockPos) {
|
public int getHeight() {
|
||||||
return this.isOutsideBuildHeight(blockPos.getY());
|
return this.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxY() {
|
||||||
|
return this.maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSectionsCount() {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ abstract class ServerSelectionListMixin {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @reason Massively increase the threadpool count so that slow servers do not stall the pinging of other servers
|
* @reason Massively increase the threadpool count so that slow servers do not stall the pinging of other servers
|
||||||
* on the status list
|
* on the status list
|
||||||
* @author Spottedleaf
|
* @author Spottedleaf
|
||||||
*/
|
*/
|
||||||
@ModifyConstant(
|
@ModifyConstant(
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,7 +193,7 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
|
|||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void addTask(final int x, final int z, final ThreadedLevelLightEngine.TaskType type,
|
public void addTask(final int x, final int z, final ThreadedLevelLightEngine.TaskType type,
|
||||||
final Runnable task) {
|
final Runnable task) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.starlight.world;
|
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
|
||||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
|
||||||
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
@Mixin(ChunkSerializer.class)
|
|
||||||
abstract class ChunkSerializerMixin {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overwrites vanilla's light data with our own.
|
|
||||||
* TODO this needs to be checked on update to account for format changes
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "write",
|
|
||||||
at = @At("RETURN")
|
|
||||||
)
|
|
||||||
private static void saveLightHook(final ServerLevel world, final ChunkAccess chunk, final CallbackInfoReturnable<CompoundTag> cir) {
|
|
||||||
SaveUtil.saveLightHook(world, chunk, cir.getReturnValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads our light data into the returned chunk object from the tag.
|
|
||||||
* TODO this needs to be checked on update to account for format changes
|
|
||||||
*/
|
|
||||||
@Inject(
|
|
||||||
method = "read",
|
|
||||||
at = @At("RETURN")
|
|
||||||
)
|
|
||||||
private static void loadLightHook(final ServerLevel serverLevel, final PoiManager poiManager,
|
|
||||||
final RegionStorageInfo regionStorageInfo, final ChunkPos chunkPos,
|
|
||||||
final CompoundTag compoundTag, final CallbackInfoReturnable<ProtoChunk> cir) {
|
|
||||||
SaveUtil.loadLightHook(serverLevel, chunkPos, compoundTag, cir.getReturnValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package ca.spottedleaf.moonrise.mixin.starlight.world;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData;
|
||||||
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
@Mixin(SerializableChunkData.SectionData.class)
|
||||||
|
abstract class SerializableChunkData$SectionData implements StarlightSectionData {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private int blockLightState = -1;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private int skyLightState = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int starlight$getBlockLightState() {
|
||||||
|
return this.blockLightState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void starlight$setBlockLightState(final int state) {
|
||||||
|
this.blockLightState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int starlight$getSkyLightState() {
|
||||||
|
return this.skyLightState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void starlight$setSkyLightState(final int state) {
|
||||||
|
this.skyLightState = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
package ca.spottedleaf.moonrise.mixin.starlight.world;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.util.MixinWorkarounds;
|
||||||
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
|
import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk;
|
||||||
|
import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray;
|
||||||
|
import ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine;
|
||||||
|
import ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData;
|
||||||
|
import ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil;
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.DataLayer;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||||
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
|
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
|
||||||
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
|
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// note: keep in-sync with SaveUtil
|
||||||
|
@Mixin(SerializableChunkData.class)
|
||||||
|
abstract class SerializableChunkDataMixin {
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private ChunkStatus chunkStatus;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private boolean lightCorrect;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private List<SerializableChunkData.SectionData> sectionData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Replace light correctness check with our own
|
||||||
|
* Our light check is versioned in case we change the light format OR fix a bug
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Redirect(
|
||||||
|
method = "parse",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/nbt/CompoundTag;getBoolean(Ljava/lang/String;)Z",
|
||||||
|
ordinal = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private static boolean setLightCorrect(final CompoundTag instance, final String string,
|
||||||
|
@Local(ordinal = 0, argsOnly = false) final ChunkStatus status) {
|
||||||
|
final boolean starlightCorrect = instance.get("isLightOn") != null && instance.getInt(SaveUtil.STARLIGHT_VERSION_TAG) == SaveUtil.STARLIGHT_LIGHT_VERSION;
|
||||||
|
return status.isOrAfter(ChunkStatus.LIGHT) && starlightCorrect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Add starlight block/sky state to SectionData
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Redirect(
|
||||||
|
method = "parse",
|
||||||
|
at = @At(
|
||||||
|
value = "NEW",
|
||||||
|
target = "(ILnet/minecraft/world/level/chunk/LevelChunkSection;Lnet/minecraft/world/level/chunk/DataLayer;Lnet/minecraft/world/level/chunk/DataLayer;)Lnet/minecraft/world/level/chunk/storage/SerializableChunkData$SectionData;",
|
||||||
|
ordinal = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private static SerializableChunkData.SectionData readStarlightState(final int y, final LevelChunkSection chunkSection,
|
||||||
|
final DataLayer blockLight, final DataLayer skyLight,
|
||||||
|
@Local(ordinal = 3, argsOnly = false) final CompoundTag sectionData) {
|
||||||
|
final SerializableChunkData.SectionData ret = new SerializableChunkData.SectionData(
|
||||||
|
y, chunkSection, blockLight, skyLight
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sectionData.contains(SaveUtil.BLOCKLIGHT_STATE_TAG, Tag.TAG_ANY_NUMERIC)) {
|
||||||
|
((StarlightSectionData)(Object)ret).starlight$setBlockLightState(sectionData.getInt(SaveUtil.BLOCKLIGHT_STATE_TAG));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionData.contains(SaveUtil.SKYLIGHT_STATE_TAG, Tag.TAG_ANY_NUMERIC)) {
|
||||||
|
((StarlightSectionData)(Object)ret).starlight$setSkyLightState(sectionData.getInt(SaveUtil.SKYLIGHT_STATE_TAG));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Load light data from the section data and store them in the returned value's SWMRNibbleArrays
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "read",
|
||||||
|
at = @At(
|
||||||
|
value = "RETURN"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private void loadStarlightLightData(final ServerLevel world, final PoiManager poiManager,
|
||||||
|
final RegionStorageInfo regionStorageInfo, final ChunkPos pos,
|
||||||
|
final CallbackInfoReturnable<ProtoChunk> cir) {
|
||||||
|
final ProtoChunk ret = cir.getReturnValue();
|
||||||
|
|
||||||
|
final boolean hasSkyLight = world.dimensionType().hasSkyLight();
|
||||||
|
final int minSection = WorldUtil.getMinLightSection(world);
|
||||||
|
|
||||||
|
final SWMRNibbleArray[] blockNibbles = StarLightEngine.getFilledEmptyLight(world);
|
||||||
|
final SWMRNibbleArray[] skyNibbles = StarLightEngine.getFilledEmptyLight(world);
|
||||||
|
|
||||||
|
for (final SerializableChunkData.SectionData sectionData : this.sectionData) {
|
||||||
|
final int y = sectionData.y();
|
||||||
|
final DataLayer blockLight = sectionData.blockLight();
|
||||||
|
final DataLayer skyLight = sectionData.skyLight();
|
||||||
|
|
||||||
|
final int blockState = ((StarlightSectionData)(Object)sectionData).starlight$getBlockLightState();
|
||||||
|
final int skyState = ((StarlightSectionData)(Object)sectionData).starlight$getSkyLightState();
|
||||||
|
|
||||||
|
if (blockState >= 0) {
|
||||||
|
if (blockLight != null) {
|
||||||
|
blockNibbles[y - minSection] = new SWMRNibbleArray(MixinWorkarounds.clone(blockLight.getData()), blockState); // clone for data safety
|
||||||
|
} else {
|
||||||
|
blockNibbles[y - minSection] = new SWMRNibbleArray(null, blockState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skyState >= 0 && hasSkyLight) {
|
||||||
|
if (skyLight != null) {
|
||||||
|
skyNibbles[y - minSection] = new SWMRNibbleArray(MixinWorkarounds.clone(skyLight.getData()), skyState); // clone for data safety
|
||||||
|
} else {
|
||||||
|
skyNibbles[y - minSection] = new SWMRNibbleArray(null, skyState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
((StarlightChunk)ret).starlight$setBlockNibbles(blockNibbles);
|
||||||
|
((StarlightChunk)ret).starlight$setSkyNibbles(skyNibbles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Rewrite the section copying so that we can store Starlight's data
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Redirect(
|
||||||
|
method = "copyOf",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;getMinLightSection()I",
|
||||||
|
ordinal = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private static int rewriteSectionCopy(final LevelLightEngine instance,
|
||||||
|
@Local(ordinal = 0, argsOnly = true) final ServerLevel world,
|
||||||
|
@Local(ordinal = 0, argsOnly = true) final ChunkAccess chunk,
|
||||||
|
@Local(ordinal = 0, argsOnly = false) final List<SerializableChunkData.SectionData> sections) {
|
||||||
|
|
||||||
|
final int minLightSection = WorldUtil.getMinLightSection(world);
|
||||||
|
final int maxLightSection = WorldUtil.getMaxLightSection(world);
|
||||||
|
final int minBlockSection = WorldUtil.getMinSection(world);
|
||||||
|
|
||||||
|
final LevelChunkSection[] chunkSections = chunk.getSections();
|
||||||
|
final SWMRNibbleArray[] blockNibbles = ((StarlightChunk)chunk).starlight$getBlockNibbles();
|
||||||
|
final SWMRNibbleArray[] skyNibbles = ((StarlightChunk)chunk).starlight$getSkyNibbles();
|
||||||
|
|
||||||
|
for (int lightSection = minLightSection; lightSection <= maxLightSection; ++lightSection) {
|
||||||
|
final int lightSectionIdx = lightSection - minLightSection;
|
||||||
|
final int blockSectionIdx = lightSection - minBlockSection;
|
||||||
|
|
||||||
|
final LevelChunkSection chunkSection = (blockSectionIdx >= 0 && blockSectionIdx < chunkSections.length) ? chunkSections[blockSectionIdx].copy() : null;
|
||||||
|
final SWMRNibbleArray.SaveState blockNibble = blockNibbles[lightSectionIdx].getSaveState();
|
||||||
|
final SWMRNibbleArray.SaveState skyNibble = skyNibbles[lightSectionIdx].getSaveState();
|
||||||
|
|
||||||
|
if (chunkSection == null && blockNibble == null && skyNibble == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SerializableChunkData.SectionData sectionData = new SerializableChunkData.SectionData(
|
||||||
|
lightSection, chunkSection,
|
||||||
|
blockNibble == null ? null : (blockNibble.data == null ? null : new DataLayer(blockNibble.data)),
|
||||||
|
skyNibble == null ? null : (skyNibble.data == null ? null : new DataLayer(skyNibble.data))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (blockNibble != null) {
|
||||||
|
((StarlightSectionData)(Object)sectionData).starlight$setBlockLightState(blockNibble.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skyNibble != null) {
|
||||||
|
((StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
sections.add(sectionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// force the Vanilla loop to never run
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Store the per-section block/sky state from Starlight
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "write",
|
||||||
|
at = @At(
|
||||||
|
value = "FIELD",
|
||||||
|
target = "Lnet/minecraft/world/level/chunk/storage/SerializableChunkData$SectionData;chunkSection:Lnet/minecraft/world/level/chunk/LevelChunkSection;",
|
||||||
|
ordinal = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private void storeStarlightState(final CallbackInfoReturnable<CompoundTag> cir,
|
||||||
|
@Local(ordinal = 0, argsOnly = false) final SerializableChunkData.SectionData sectionData,
|
||||||
|
@Local(ordinal = 1, argsOnly = false) final CompoundTag sectionNBT) {
|
||||||
|
final int blockState = ((StarlightSectionData)(Object)sectionData).starlight$getBlockLightState();
|
||||||
|
final int skyState = ((StarlightSectionData)(Object)sectionData).starlight$getSkyLightState();
|
||||||
|
|
||||||
|
if (blockState > 0) {
|
||||||
|
sectionNBT.putInt(SaveUtil.BLOCKLIGHT_STATE_TAG, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skyState > 0) {
|
||||||
|
sectionNBT.putInt(SaveUtil.SKYLIGHT_STATE_TAG, skyState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Store Starlight's light version
|
||||||
|
* @author Spottedleaf
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "write",
|
||||||
|
at = @At(
|
||||||
|
value = "RETURN"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private void writeStarlightCorrectLight(final CallbackInfoReturnable<CompoundTag> cir) {
|
||||||
|
if (this.chunkStatus.isBefore(ChunkStatus.LIGHT) || !this.lightCorrect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CompoundTag ret = cir.getReturnValue();
|
||||||
|
|
||||||
|
// clobber vanilla value to force vanilla to relight
|
||||||
|
ret.putBoolean("isLightOn", false);
|
||||||
|
// store our light version
|
||||||
|
ret.putInt(SaveUtil.STARLIGHT_VERSION_TAG, SaveUtil.STARLIGHT_LIGHT_VERSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.chunk_system;
|
package ca.spottedleaf.moonrise.patches.chunk_system;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
import net.minecraft.SharedConstants;
|
import net.minecraft.SharedConstants;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.util.datafix.DataFixTypes;
|
import net.minecraft.util.datafix.fixes.References;
|
||||||
|
|
||||||
public final class ChunkSystemConverters {
|
public final class ChunkSystemConverters {
|
||||||
|
|
||||||
@@ -25,13 +26,13 @@ public final class ChunkSystemConverters {
|
|||||||
public static CompoundTag convertPoiCompoundTag(final CompoundTag data, final ServerLevel world) {
|
public static CompoundTag convertPoiCompoundTag(final CompoundTag data, final ServerLevel world) {
|
||||||
final int dataVersion = getDataVersion(data, DEFAULT_POI_DATA_VERSION);
|
final int dataVersion = getDataVersion(data, DEFAULT_POI_DATA_VERSION);
|
||||||
|
|
||||||
return DataFixTypes.POI_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
return PlatformHooks.get().convertNBT(References.POI_CHUNK, world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompoundTag convertEntityChunkCompoundTag(final CompoundTag data, final ServerLevel world) {
|
public static CompoundTag convertEntityChunkCompoundTag(final CompoundTag data, final ServerLevel world) {
|
||||||
final int dataVersion = getDataVersion(data, DEFAULT_ENTITY_CHUNK_DATA_VERSION);
|
final int dataVersion = getDataVersion(data, DEFAULT_ENTITY_CHUNK_DATA_VERSION);
|
||||||
|
|
||||||
return DataFixTypes.ENTITY_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
return PlatformHooks.get().convertNBT(References.ENTITY_CHUNK, world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChunkSystemConverters() {}
|
private ChunkSystemConverters() {}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.chunk_system;
|
|
||||||
|
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.async_save.AsyncChunkSaveData;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
|
|
||||||
public final class ChunkSystemFeatures {
|
|
||||||
|
|
||||||
public static boolean supportsAsyncChunkSave() {
|
|
||||||
// uncertain how to properly pass AsyncSaveData to ChunkSerializer#write
|
|
||||||
// additionally, there may be mods hooking into the write() call which may not be thread-safe to call
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncChunkSaveData getAsyncSaveData(final ServerLevel world, final ChunkAccess chunk) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompoundTag saveChunkAsync(final ServerLevel world, final ChunkAccess chunk, final AsyncChunkSaveData asyncSaveData) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean forceNoSave(final ChunkAccess chunk) {
|
|
||||||
// support for CB chunk mustNotSave
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean supportsAsyncChunkDeserialization() {
|
|
||||||
// as it stands, the current problem with supporting this in Moonrise is that we are unsure that any mods
|
|
||||||
// hooking into ChunkSerializer#read() are thread-safe to call
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChunkSystemFeatures() {}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.chunk_system.async_save;
|
|
||||||
|
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
|
|
||||||
public record AsyncChunkSaveData(
|
|
||||||
Tag blockTickList, // non-null if we had to go to the server's tick list
|
|
||||||
Tag fluidTickList, // non-null if we had to go to the server's tick list
|
|
||||||
ListTag blockEntities,
|
|
||||||
long worldTime
|
|
||||||
) {}
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.chunk_system.io;
|
package ca.spottedleaf.moonrise.patches.chunk_system.io;
|
||||||
|
|
||||||
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
||||||
|
import ca.spottedleaf.concurrentutil.completable.CallbackCompletable;
|
||||||
|
import ca.spottedleaf.concurrentutil.completable.Completable;
|
||||||
import ca.spottedleaf.concurrentutil.executor.Cancellable;
|
import ca.spottedleaf.concurrentutil.executor.Cancellable;
|
||||||
import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
||||||
import ca.spottedleaf.concurrentutil.executor.queue.PrioritisedTaskQueue;
|
import ca.spottedleaf.concurrentutil.executor.queue.PrioritisedTaskQueue;
|
||||||
@@ -12,6 +14,7 @@ import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|||||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
@@ -23,14 +26,12 @@ import java.io.DataInputStream;
|
|||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public final class MoonriseRegionFileIO {
|
public final class MoonriseRegionFileIO {
|
||||||
|
|
||||||
@@ -215,30 +216,6 @@ public final class MoonriseRegionFileIO {
|
|||||||
return Priority.HIGHEST;
|
return Priority.HIGHEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current {@code CompoundTag} pending for write for the specified chunk & regionfile type.
|
|
||||||
* Note that this does not copy the result, so do not modify the result returned.
|
|
||||||
*
|
|
||||||
* @param world Specified world.
|
|
||||||
* @param chunkX Specified chunk x.
|
|
||||||
* @param chunkZ Specified chunk z.
|
|
||||||
* @param type Specified regionfile type.
|
|
||||||
*
|
|
||||||
* @return The compound tag associated for the specified chunk. {@code null} if no write was pending, or if {@code null} is the write pending.
|
|
||||||
*/
|
|
||||||
public static CompoundTag getPendingWrite(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
|
|
||||||
final RegionDataController taskController = getControllerFor(world, type);
|
|
||||||
final ChunkIOTask task = taskController.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
||||||
|
|
||||||
if (task == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final CompoundTag ret = task.inProgressWrite;
|
|
||||||
|
|
||||||
return ret == ChunkIOTask.NOTHING_TO_WRITE ? null : ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the priority for the specified regionfile type for the specified chunk.
|
* Returns the priority for the specified regionfile type for the specified chunk.
|
||||||
* @param world Specified world.
|
* @param world Specified world.
|
||||||
@@ -444,27 +421,106 @@ public final class MoonriseRegionFileIO {
|
|||||||
*/
|
*/
|
||||||
public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
|
public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
|
||||||
final RegionFileType type, final Priority priority) {
|
final RegionFileType type, final Priority priority) {
|
||||||
|
scheduleSave(
|
||||||
|
world, chunkX, chunkZ,
|
||||||
|
(final BiConsumer<CompoundTag, Throwable> consumer) -> {
|
||||||
|
consumer.accept(data, null);
|
||||||
|
}, null, type, priority
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules the chunk data to be written asynchronously.
|
||||||
|
* <p>
|
||||||
|
* Impl notes:
|
||||||
|
* </p>
|
||||||
|
* <li>
|
||||||
|
* This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
||||||
|
* saves must be scheduled before a chunk is unloaded.
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* Writes may be called concurrently, although only the "later" write will go through.
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* The specified write task, if not null, will have its priority controlled by the scheduler.
|
||||||
|
* </li>
|
||||||
|
*
|
||||||
|
* @param world Chunk's world
|
||||||
|
* @param chunkX Chunk's x coordinate
|
||||||
|
* @param chunkZ Chunk's z coordinate
|
||||||
|
* @param completable Chunk's pending data
|
||||||
|
* @param writeTask The task responsible for completing the pending chunk data
|
||||||
|
* @param type The regionfile type to write to.
|
||||||
|
* @param priority The minimum priority to schedule at.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException If the file io thread has shutdown.
|
||||||
|
*/
|
||||||
|
public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CallbackCompletable<CompoundTag> completable,
|
||||||
|
final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
|
||||||
|
scheduleSave(world, chunkX, chunkZ, completable::addWaiter, writeTask, type, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules the chunk data to be written asynchronously.
|
||||||
|
* <p>
|
||||||
|
* Impl notes:
|
||||||
|
* </p>
|
||||||
|
* <li>
|
||||||
|
* This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
||||||
|
* saves must be scheduled before a chunk is unloaded.
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* Writes may be called concurrently, although only the "later" write will go through.
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* The specified write task, if not null, will have its priority controlled by the scheduler.
|
||||||
|
* </li>
|
||||||
|
*
|
||||||
|
* @param world Chunk's world
|
||||||
|
* @param chunkX Chunk's x coordinate
|
||||||
|
* @param chunkZ Chunk's z coordinate
|
||||||
|
* @param completable Chunk's pending data
|
||||||
|
* @param writeTask The task responsible for completing the pending chunk data
|
||||||
|
* @param type The regionfile type to write to.
|
||||||
|
* @param priority The minimum priority to schedule at.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException If the file io thread has shutdown.
|
||||||
|
*/
|
||||||
|
public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final Completable<CompoundTag> completable,
|
||||||
|
final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
|
||||||
|
scheduleSave(world, chunkX, chunkZ, completable::whenComplete, writeTask, type, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final Consumer<BiConsumer<CompoundTag, Throwable>> scheduler,
|
||||||
|
final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
|
||||||
final RegionDataController taskController = getControllerFor(world, type);
|
final RegionDataController taskController = getControllerFor(world, type);
|
||||||
|
|
||||||
final boolean[] created = new boolean[1];
|
final boolean[] created = new boolean[1];
|
||||||
final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
final ChunkIOTask.InProgressWrite write = new ChunkIOTask.InProgressWrite(writeTask);
|
||||||
final ChunkIOTask task = taskController.chunkTasks.compute(key, (final long keyInMap, final ChunkIOTask taskRunning) -> {
|
final ChunkIOTask task = taskController.chunkTasks.compute(CoordinateUtils.getChunkKey(chunkX, chunkZ),
|
||||||
if (taskRunning == null || taskRunning.failedWrite) {
|
(final long keyInMap, final ChunkIOTask taskRunning) -> {
|
||||||
// no task is scheduled or the previous write failed - meaning we need to overwrite it
|
if (taskRunning == null || taskRunning.failedWrite) {
|
||||||
|
// no task is scheduled or the previous write failed - meaning we need to overwrite it
|
||||||
|
|
||||||
// create task
|
// create task
|
||||||
final ChunkIOTask newTask = new ChunkIOTask(
|
final ChunkIOTask newTask = new ChunkIOTask(
|
||||||
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead(), data
|
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
|
||||||
);
|
);
|
||||||
created[0] = true;
|
|
||||||
|
|
||||||
return newTask;
|
newTask.pushPendingWrite(write);
|
||||||
|
|
||||||
|
created[0] = true;
|
||||||
|
|
||||||
|
return newTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
taskRunning.pushPendingWrite(write);
|
||||||
|
|
||||||
|
return taskRunning;
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
taskRunning.inProgressWrite = data;
|
write.schedule(task, scheduler);
|
||||||
|
|
||||||
return taskRunning;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (created[0]) {
|
if (created[0]) {
|
||||||
taskController.startTask(task);
|
taskController.startTask(task);
|
||||||
@@ -711,7 +767,7 @@ public final class MoonriseRegionFileIO {
|
|||||||
|
|
||||||
// set up task
|
// set up task
|
||||||
final ChunkIOTask newTask = new ChunkIOTask(
|
final ChunkIOTask newTask = new ChunkIOTask(
|
||||||
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead(), ChunkIOTask.NOTHING_TO_WRITE
|
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
|
||||||
);
|
);
|
||||||
newTask.inProgressRead.addToAsyncWaiters(onComplete);
|
newTask.inProgressRead.addToAsyncWaiters(onComplete);
|
||||||
|
|
||||||
@@ -719,22 +775,34 @@ public final class MoonriseRegionFileIO {
|
|||||||
return newTask;
|
return newTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CompoundTag pendingWrite = running.inProgressWrite;
|
final ChunkIOTask.InProgressWrite pendingWrite = running.inProgressWrite;
|
||||||
|
|
||||||
if (pendingWrite == ChunkIOTask.NOTHING_TO_WRITE) {
|
if (pendingWrite == null) {
|
||||||
// need to add to waiters here, because the regionfile thread will use compute() to lock and check for cancellations
|
// need to add to waiters here, because the regionfile thread will use compute() to lock and check for cancellations
|
||||||
if (!running.inProgressRead.addToAsyncWaiters(onComplete)) {
|
if (!running.inProgressRead.addToAsyncWaiters(onComplete)) {
|
||||||
callbackInfo.data = running.inProgressRead.value;
|
callbackInfo.data = running.inProgressRead.value;
|
||||||
callbackInfo.throwable = running.inProgressRead.throwable;
|
callbackInfo.throwable = running.inProgressRead.throwable;
|
||||||
callbackInfo.completeNow = true;
|
callbackInfo.completeNow = true;
|
||||||
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callbackInfo.read = running.inProgressRead;
|
||||||
|
|
||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this stage we have to use the in progress write's data to avoid an order issue
|
// at this stage we have to use the in progress write's data to avoid an order issue
|
||||||
callbackInfo.data = pendingWrite;
|
|
||||||
callbackInfo.throwable = null;
|
if (!pendingWrite.addToAsyncWaiters(onComplete)) {
|
||||||
callbackInfo.completeNow = true;
|
// data is ready now
|
||||||
|
callbackInfo.data = pendingWrite.value;
|
||||||
|
callbackInfo.throwable = pendingWrite.throwable;
|
||||||
|
callbackInfo.completeNow = true;
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackInfo.write = pendingWrite;
|
||||||
|
|
||||||
return running;
|
return running;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -755,7 +823,7 @@ public final class MoonriseRegionFileIO {
|
|||||||
ret.raisePriority(priority);
|
ret.raisePriority(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CancellableRead(onComplete, ret);
|
return new CancellableRead(onComplete, callbackInfo.read, callbackInfo.write);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ImmediateCallbackCompletion {
|
private static final class ImmediateCallbackCompletion {
|
||||||
@@ -764,6 +832,8 @@ public final class MoonriseRegionFileIO {
|
|||||||
private Throwable throwable;
|
private Throwable throwable;
|
||||||
private boolean completeNow;
|
private boolean completeNow;
|
||||||
private boolean tasksNeedReadScheduling;
|
private boolean tasksNeedReadScheduling;
|
||||||
|
private ChunkIOTask.InProgressRead read;
|
||||||
|
private ChunkIOTask.InProgressWrite write;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -802,26 +872,40 @@ public final class MoonriseRegionFileIO {
|
|||||||
private static final class CancellableRead implements Cancellable {
|
private static final class CancellableRead implements Cancellable {
|
||||||
|
|
||||||
private BiConsumer<CompoundTag, Throwable> callback;
|
private BiConsumer<CompoundTag, Throwable> callback;
|
||||||
private ChunkIOTask task;
|
private ChunkIOTask.InProgressRead read;
|
||||||
|
private ChunkIOTask.InProgressWrite write;
|
||||||
|
|
||||||
private CancellableRead(final BiConsumer<CompoundTag, Throwable> callback, final ChunkIOTask task) {
|
private CancellableRead(final BiConsumer<CompoundTag, Throwable> callback,
|
||||||
|
final ChunkIOTask.InProgressRead read,
|
||||||
|
final ChunkIOTask.InProgressWrite write) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.task = task;
|
this.read = read;
|
||||||
|
this.write = write;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean cancel() {
|
public boolean cancel() {
|
||||||
final BiConsumer<CompoundTag, Throwable> callback = this.callback;
|
final BiConsumer<CompoundTag, Throwable> callback = this.callback;
|
||||||
final ChunkIOTask task = this.task;
|
final ChunkIOTask.InProgressRead read = this.read;
|
||||||
|
final ChunkIOTask.InProgressWrite write = this.write;
|
||||||
|
|
||||||
if (callback == null || task == null) {
|
if (callback == null || (read == null && write == null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.callback = null;
|
this.callback = null;
|
||||||
this.task = null;
|
this.read = null;
|
||||||
|
this.write = null;
|
||||||
return task.inProgressRead.cancel(callback);
|
|
||||||
|
if (read != null) {
|
||||||
|
return read.cancel(callback);
|
||||||
|
}
|
||||||
|
if (write != null) {
|
||||||
|
return write.cancel(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unreachable
|
||||||
|
throw new InternalError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -854,8 +938,6 @@ public final class MoonriseRegionFileIO {
|
|||||||
|
|
||||||
private static final class ChunkIOTask {
|
private static final class ChunkIOTask {
|
||||||
|
|
||||||
private static final CompoundTag NOTHING_TO_WRITE = new CompoundTag();
|
|
||||||
|
|
||||||
private final ServerLevel world;
|
private final ServerLevel world;
|
||||||
private final RegionDataController regionDataController;
|
private final RegionDataController regionDataController;
|
||||||
private final int chunkX;
|
private final int chunkX;
|
||||||
@@ -864,22 +946,21 @@ public final class MoonriseRegionFileIO {
|
|||||||
private PrioritisedExecutor.PrioritisedTask currentTask;
|
private PrioritisedExecutor.PrioritisedTask currentTask;
|
||||||
|
|
||||||
private final InProgressRead inProgressRead;
|
private final InProgressRead inProgressRead;
|
||||||
private volatile CompoundTag inProgressWrite;
|
private volatile InProgressWrite inProgressWrite;
|
||||||
|
private final ReferenceOpenHashSet<InProgressWrite> allPendingWrites = new ReferenceOpenHashSet<>();
|
||||||
|
|
||||||
private RegionDataController.ReadData readData;
|
private RegionDataController.ReadData readData;
|
||||||
private RegionDataController.WriteData writeData;
|
private RegionDataController.WriteData writeData;
|
||||||
private boolean failedWrite;
|
private boolean failedWrite;
|
||||||
|
|
||||||
public ChunkIOTask(final ServerLevel world, final RegionDataController regionDataController,
|
public ChunkIOTask(final ServerLevel world, final RegionDataController regionDataController,
|
||||||
final int chunkX, final int chunkZ, final Priority priority, final InProgressRead inProgressRead,
|
final int chunkX, final int chunkZ, final Priority priority, final InProgressRead inProgressRead) {
|
||||||
final CompoundTag inProgressWrite) {
|
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.regionDataController = regionDataController;
|
this.regionDataController = regionDataController;
|
||||||
this.chunkX = chunkX;
|
this.chunkX = chunkX;
|
||||||
this.chunkZ = chunkZ;
|
this.chunkZ = chunkZ;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.inProgressRead = inProgressRead;
|
this.inProgressRead = inProgressRead;
|
||||||
this.inProgressWrite = inProgressWrite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Priority getPriority() {
|
public Priority getPriority() {
|
||||||
@@ -888,16 +969,26 @@ public final class MoonriseRegionFileIO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// must hold lock on this object
|
||||||
|
private void updatePriority(final Priority priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
if (this.currentTask != null) {
|
||||||
|
this.currentTask.setPriority(priority);
|
||||||
|
}
|
||||||
|
for (final InProgressWrite write : this.allPendingWrites) {
|
||||||
|
if (write.writeTask != null) {
|
||||||
|
write.writeTask.setPriority(priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean setPriority(final Priority priority) {
|
public boolean setPriority(final Priority priority) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (this.priority == priority) {
|
if (this.priority == priority) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.priority = priority;
|
this.updatePriority(priority);
|
||||||
if (this.currentTask != null) {
|
|
||||||
this.currentTask.setPriority(priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -909,10 +1000,7 @@ public final class MoonriseRegionFileIO {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.priority = priority;
|
this.updatePriority(priority);
|
||||||
if (this.currentTask != null) {
|
|
||||||
this.currentTask.setPriority(priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -924,15 +1012,28 @@ public final class MoonriseRegionFileIO {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.priority = priority;
|
this.updatePriority(priority);
|
||||||
if (this.currentTask != null) {
|
|
||||||
this.currentTask.setPriority(priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void pushPendingWrite(final InProgressWrite write) {
|
||||||
|
this.inProgressWrite = write;
|
||||||
|
synchronized (this) {
|
||||||
|
this.allPendingWrites.add(write);
|
||||||
|
if (write.writeTask != null) {
|
||||||
|
write.writeTask.setPriority(this.priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pendingWriteComplete(final InProgressWrite write) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.allPendingWrites.remove(write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void scheduleReadIO() {
|
public void scheduleReadIO() {
|
||||||
final PrioritisedExecutor.PrioritisedTask task;
|
final PrioritisedExecutor.PrioritisedTask task;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@@ -964,7 +1065,7 @@ public final class MoonriseRegionFileIO {
|
|||||||
canRead[0] = false;
|
canRead[0] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueInMap.inProgressWrite != NOTHING_TO_WRITE) {
|
if (valueInMap.inProgressWrite != null) {
|
||||||
return valueInMap;
|
return valueInMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1050,8 +1151,7 @@ public final class MoonriseRegionFileIO {
|
|||||||
|
|
||||||
this.finishRead(compoundTag, throwable);
|
this.finishRead(compoundTag, throwable);
|
||||||
if (!this.tryAbortWrite()) {
|
if (!this.tryAbortWrite()) {
|
||||||
// we are already on the compression executor, don't bother scheduling
|
this.scheduleWriteCompress();
|
||||||
this.performWriteCompress();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1060,17 +1160,24 @@ public final class MoonriseRegionFileIO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void scheduleWriteCompress() {
|
public void scheduleWriteCompress() {
|
||||||
|
final InProgressWrite inProgressWrite = this.inProgressWrite;
|
||||||
|
|
||||||
final PrioritisedExecutor.PrioritisedTask task;
|
final PrioritisedExecutor.PrioritisedTask task;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
task = this.regionDataController.compressionExecutor.createTask(this::performWriteCompress, this.priority);
|
task = this.regionDataController.compressionExecutor.createTask(() -> {
|
||||||
|
ChunkIOTask.this.performWriteCompress(inProgressWrite);
|
||||||
|
}, this.priority);
|
||||||
this.currentTask = task;
|
this.currentTask = task;
|
||||||
}
|
}
|
||||||
task.queue();
|
|
||||||
|
inProgressWrite.addToWaiters(this, (final CompoundTag data, final Throwable throwable) -> {
|
||||||
|
task.queue();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryAbortWrite() {
|
private boolean tryAbortWrite() {
|
||||||
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
||||||
if (this.inProgressWrite == NOTHING_TO_WRITE) {
|
if (this.inProgressWrite == null) {
|
||||||
final ChunkIOTask inMap = this.regionDataController.chunkTasks.compute(chunkKey, (final long keyInMap, final ChunkIOTask valueInMap) -> {
|
final ChunkIOTask inMap = this.regionDataController.chunkTasks.compute(chunkKey, (final long keyInMap, final ChunkIOTask valueInMap) -> {
|
||||||
if (valueInMap == null) {
|
if (valueInMap == null) {
|
||||||
throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkIOTask.this.toString() + ", report this!");
|
throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkIOTask.this.toString() + ", report this!");
|
||||||
@@ -1079,7 +1186,7 @@ public final class MoonriseRegionFileIO {
|
|||||||
throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkIOTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkIOTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueInMap.inProgressWrite != NOTHING_TO_WRITE) {
|
if (valueInMap.inProgressWrite != null) {
|
||||||
return valueInMap;
|
return valueInMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1095,61 +1202,62 @@ public final class MoonriseRegionFileIO {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performWriteCompress() {
|
private void performWriteCompress(final InProgressWrite inProgressWrite) {
|
||||||
for (;;) {
|
final CompoundTag write = inProgressWrite.value;
|
||||||
final CompoundTag write = this.inProgressWrite;
|
if (!inProgressWrite.isComplete()) {
|
||||||
if (write == NOTHING_TO_WRITE) {
|
throw new IllegalStateException("Should be writable");
|
||||||
throw new IllegalStateException("Should be writable");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RegionDataController.WriteData writeData = null;
|
RegionDataController.WriteData writeData = null;
|
||||||
boolean failedWrite = false;
|
boolean failedWrite = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writeData = this.regionDataController.startWrite(this.chunkX, this.chunkZ, write);
|
writeData = this.regionDataController.startWrite(this.chunkX, this.chunkZ, write);
|
||||||
} catch (final Throwable thr) {
|
} catch (final Throwable thr) {
|
||||||
// TODO implement this?
|
// TODO implement this?
|
||||||
/*if (thr instanceof RegionFileStorage.RegionFileSizeException) {
|
/*if (thr instanceof RegionFileStorage.RegionFileSizeException) {
|
||||||
final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
|
final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
|
||||||
LOGGER.error("Chunk at (" + this.chunkX + "," + this.chunkZ + ") in '" + WorldUtil.getWorldName(this.world) + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
|
LOGGER.error("Chunk at (" + this.chunkX + "," + this.chunkZ + ") in '" + WorldUtil.getWorldName(this.world) + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
|
||||||
} else */
|
} else */
|
||||||
{
|
{
|
||||||
failedWrite = thr instanceof IOException;
|
failedWrite = thr instanceof IOException;
|
||||||
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
|
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (writeData == null) {
|
if (writeData == null) {
|
||||||
// null if a throwable was encountered
|
// null if a throwable was encountered
|
||||||
|
|
||||||
// we cannot continue to the I/O stage here, so try to complete
|
// we cannot continue to the I/O stage here, so try to complete
|
||||||
|
|
||||||
if (this.tryCompleteWrite(write, failedWrite)) {
|
if (this.tryCompleteWrite(inProgressWrite, failedWrite)) {
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
// fetch new data and try again
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// writeData != null && !failedWrite
|
// fetch new data and try again
|
||||||
// we can continue to I/O stage
|
this.scheduleWriteCompress();
|
||||||
this.writeData = writeData;
|
|
||||||
this.scheduleWriteIO();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// writeData != null && !failedWrite
|
||||||
|
// we can continue to I/O stage
|
||||||
|
this.writeData = writeData;
|
||||||
|
this.scheduleWriteIO(inProgressWrite);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleWriteIO() {
|
private void scheduleWriteIO(final InProgressWrite inProgressWrite) {
|
||||||
final PrioritisedExecutor.PrioritisedTask task;
|
final PrioritisedExecutor.PrioritisedTask task;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, this::runWriteIO, this.priority);
|
task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, () -> {
|
||||||
|
ChunkIOTask.this.runWriteIO(inProgressWrite);
|
||||||
|
}, this.priority);
|
||||||
this.currentTask = task;
|
this.currentTask = task;
|
||||||
}
|
}
|
||||||
task.queue();
|
task.queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runWriteIO() {
|
private void runWriteIO(final InProgressWrite inProgressWrite) {
|
||||||
RegionDataController.WriteData writeData = this.writeData;
|
RegionDataController.WriteData writeData = this.writeData;
|
||||||
this.writeData = null;
|
this.writeData = null;
|
||||||
|
|
||||||
@@ -1162,14 +1270,14 @@ public final class MoonriseRegionFileIO {
|
|||||||
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
|
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.tryCompleteWrite(writeData.input(), failedWrite)) {
|
if (!this.tryCompleteWrite(inProgressWrite, failedWrite)) {
|
||||||
// fetch new data and try again
|
// fetch new data and try again
|
||||||
this.scheduleWriteCompress();
|
this.scheduleWriteCompress();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryCompleteWrite(final CompoundTag written, final boolean failedWrite) {
|
private boolean tryCompleteWrite(final InProgressWrite written, final boolean failedWrite) {
|
||||||
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
||||||
|
|
||||||
final boolean[] done = new boolean[] { false };
|
final boolean[] done = new boolean[] { false };
|
||||||
@@ -1216,14 +1324,6 @@ public final class MoonriseRegionFileIO {
|
|||||||
return this.callbacks.isEmpty();
|
return this.callbacks.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundTag getValue() {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Throwable getThrowable() {
|
|
||||||
return this.throwable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
|
public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
|
||||||
return this.callbacks.add(callback);
|
return this.callbacks.add(callback);
|
||||||
}
|
}
|
||||||
@@ -1241,11 +1341,72 @@ public final class MoonriseRegionFileIO {
|
|||||||
try {
|
try {
|
||||||
consumer.accept(value == null ? null : value.copy(), throwable);
|
consumer.accept(value == null ? null : value.copy(), throwable);
|
||||||
} catch (final Throwable thr) {
|
} catch (final Throwable thr) {
|
||||||
LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data for task " + task.toString(), thr);
|
LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data (read) for task " + task.toString(), thr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class InProgressWrite {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(InProgressWrite.class);
|
||||||
|
|
||||||
|
private CompoundTag value;
|
||||||
|
private Throwable throwable;
|
||||||
|
private volatile boolean complete;
|
||||||
|
private final MultiThreadedQueue<BiConsumer<CompoundTag, Throwable>> callbacks = new MultiThreadedQueue<>();
|
||||||
|
|
||||||
|
private final PrioritisedExecutor.PrioritisedTask writeTask;
|
||||||
|
|
||||||
|
public InProgressWrite(final PrioritisedExecutor.PrioritisedTask writeTask) {
|
||||||
|
this.writeTask = writeTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isComplete() {
|
||||||
|
return this.complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void schedule(final ChunkIOTask task, final Consumer<BiConsumer<CompoundTag, Throwable>> scheduler) {
|
||||||
|
scheduler.accept((final CompoundTag data, final Throwable throwable) -> {
|
||||||
|
InProgressWrite.this.complete(task, data, throwable);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
|
||||||
|
return this.callbacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToWaiters(final ChunkIOTask task, final BiConsumer<CompoundTag, Throwable> consumer) {
|
||||||
|
if (!this.callbacks.add(consumer)) {
|
||||||
|
this.syncAccept(task, consumer, this.value, this.throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncAccept(final ChunkIOTask task, final BiConsumer<CompoundTag, Throwable> consumer, final CompoundTag value, final Throwable throwable) {
|
||||||
|
try {
|
||||||
|
consumer.accept(value == null ? null : value.copy(), throwable);
|
||||||
|
} catch (final Throwable thr) {
|
||||||
|
LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data (write) for task " + task.toString(), thr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void complete(final ChunkIOTask task, final CompoundTag value, final Throwable throwable) {
|
||||||
|
this.value = value;
|
||||||
|
this.throwable = throwable;
|
||||||
|
this.complete = true;
|
||||||
|
|
||||||
|
task.pendingWriteComplete(this);
|
||||||
|
|
||||||
|
BiConsumer<CompoundTag, Throwable> consumer;
|
||||||
|
while ((consumer = this.callbacks.pollOrBlockAdds()) != null) {
|
||||||
|
this.syncAccept(task, consumer, value, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean cancel(final BiConsumer<CompoundTag, Throwable> callback) {
|
||||||
|
return this.callbacks.remove(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class RegionDataController {
|
public static abstract class RegionDataController {
|
||||||
|
|||||||
@@ -61,12 +61,4 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
|
|||||||
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
|
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
|
||||||
|
|
||||||
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
|
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
|
||||||
|
|
||||||
public ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks();
|
|
||||||
|
|
||||||
public void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder);
|
|
||||||
|
|
||||||
public void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder);
|
|
||||||
|
|
||||||
public void moonrise$clearUnsyncedChunks();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,4 @@ public interface ChunkSystemChunkHolder {
|
|||||||
|
|
||||||
public LevelChunk moonrise$getFullChunk();
|
public LevelChunk moonrise$getFullChunk();
|
||||||
|
|
||||||
public boolean moonrise$isMarkedDirtyForPlayers();
|
|
||||||
|
|
||||||
public void moonrise$markDirtyForPlayers(final boolean value);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity;
|
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
import ca.spottedleaf.moonrise.common.list.EntityList;
|
import ca.spottedleaf.moonrise.common.list.EntityList;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
||||||
@@ -14,6 +15,7 @@ import net.minecraft.server.level.FullChunkStatus;
|
|||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
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.EntitySpawnReason;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.entity.boss.EnderDragonPart;
|
import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||||
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||||
@@ -74,7 +76,7 @@ public final class ChunkEntitySlices {
|
|||||||
|
|
||||||
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) {
|
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) {
|
||||||
// TODO check this and below on update for format changes
|
// TODO check this and below on update for format changes
|
||||||
return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world).collect(ImmutableList.toImmutableList());
|
return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paper start - rewrite chunk system
|
// Paper start - rewrite chunk system
|
||||||
@@ -102,7 +104,7 @@ public final class ChunkEntitySlices {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final ListTag entitiesTag = new ListTag();
|
final ListTag entitiesTag = new ListTag();
|
||||||
for (final Entity entity : entities) {
|
for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) {
|
||||||
CompoundTag compoundTag = new CompoundTag();
|
CompoundTag compoundTag = new CompoundTag();
|
||||||
if (entity.save(compoundTag)) {
|
if (entity.save(compoundTag)) {
|
||||||
entitiesTag.add(compoundTag);
|
entitiesTag.add(compoundTag);
|
||||||
@@ -149,12 +151,12 @@ public final class ChunkEntitySlices {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (entity.shouldBeSaved()) {
|
if (entity.shouldBeSaved()) {
|
||||||
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
PlatformHooks.get().unloadEntity(entity);
|
||||||
if (entity.isVehicle()) {
|
if (entity.isVehicle()) {
|
||||||
// we cannot assume that these entities are contained within this chunk, because entities can
|
// we cannot assume that these entities are contained within this chunk, because entities can
|
||||||
// desync - so we need to remove them all
|
// desync - so we need to remove them all
|
||||||
for (final Entity passenger : entity.getIndirectPassengers()) {
|
for (final Entity passenger : entity.getIndirectPassengers()) {
|
||||||
passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
PlatformHooks.get().unloadEntity(passenger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +165,7 @@ public final class ChunkEntitySlices {
|
|||||||
return this.entities.size() != 0;
|
return this.entities.size() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Entity> getAllEntities() {
|
public List<Entity> getAllEntities() {
|
||||||
final int len = this.entities.size();
|
final int len = this.entities.size();
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|||||||
|
|
||||||
protected final SWMRLong2ObjectHashTable<ChunkSlicesRegion> regions = new SWMRLong2ObjectHashTable<>(128, 0.5f);
|
protected final SWMRLong2ObjectHashTable<ChunkSlicesRegion> regions = new SWMRLong2ObjectHashTable<>(128, 0.5f);
|
||||||
|
|
||||||
protected final int minSection; // inclusive
|
|
||||||
protected final int maxSection; // inclusive
|
|
||||||
protected final LevelCallback<Entity> worldCallback;
|
protected final LevelCallback<Entity> worldCallback;
|
||||||
|
|
||||||
protected final ConcurrentLong2ReferenceChainedHashTable<Entity> entityById = new ConcurrentLong2ReferenceChainedHashTable<>();
|
protected final ConcurrentLong2ReferenceChainedHashTable<Entity> entityById = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||||
@@ -56,8 +54,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|||||||
|
|
||||||
public EntityLookup(final Level world, final LevelCallback<Entity> worldCallback) {
|
public EntityLookup(final Level world, final LevelCallback<Entity> worldCallback) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.minSection = WorldUtil.getMinSection(world);
|
|
||||||
this.maxSection = WorldUtil.getMaxSection(world);
|
|
||||||
this.worldCallback = worldCallback;
|
this.worldCallback = worldCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,7 +400,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|||||||
protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) {
|
protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) {
|
||||||
final BlockPos pos = entity.blockPosition();
|
final BlockPos pos = entity.blockPosition();
|
||||||
final int sectionX = pos.getX() >> 4;
|
final int sectionX = pos.getX() >> 4;
|
||||||
final int sectionY = Mth.clamp(pos.getY() >> 4, this.minSection, this.maxSection);
|
final int sectionY = Mth.clamp(pos.getY() >> 4, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world));
|
||||||
final int sectionZ = pos.getZ() >> 4;
|
final int sectionZ = pos.getZ() >> 4;
|
||||||
this.checkThread(sectionX, sectionZ, "Cannot add entity off-main thread");
|
this.checkThread(sectionX, sectionZ, "Cannot add entity off-main thread");
|
||||||
|
|
||||||
@@ -523,7 +519,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|||||||
final int sectionZ = ((ChunkSystemEntity)entity).moonrise$getSectionZ();
|
final int sectionZ = ((ChunkSystemEntity)entity).moonrise$getSectionZ();
|
||||||
final BlockPos newPos = entity.blockPosition();
|
final BlockPos newPos = entity.blockPosition();
|
||||||
final int newSectionX = newPos.getX() >> 4;
|
final int newSectionX = newPos.getX() >> 4;
|
||||||
final int newSectionY = Mth.clamp(newPos.getY() >> 4, this.minSection, this.maxSection);
|
final int newSectionY = Mth.clamp(newPos.getY() >> 4, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world));
|
||||||
final int newSectionZ = newPos.getZ() >> 4;
|
final int newSectionZ = newPos.getZ() >> 4;
|
||||||
|
|
||||||
if (newSectionX == sectionX && newSectionY == sectionY && newSectionZ == sectionZ) {
|
if (newSectionX == sectionX && newSectionY == sectionY && newSectionZ == sectionZ) {
|
||||||
@@ -963,7 +959,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|||||||
|
|
||||||
public ChunkEntitySlices getOrCreateChunk(final int chunkX, final int chunkZ) {
|
public ChunkEntitySlices getOrCreateChunk(final int chunkX, final int chunkZ) {
|
||||||
final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
|
final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
|
||||||
ChunkEntitySlices ret;
|
final ChunkEntitySlices ret;
|
||||||
if (region == null || (ret = region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT))) == null) {
|
if (region == null || (ret = region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT))) == null) {
|
||||||
return this.createEntityChunk(chunkX, chunkZ, true);
|
return this.createEntityChunk(chunkX, chunkZ, true);
|
||||||
}
|
}
|
||||||
@@ -1061,7 +1057,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|||||||
@Override
|
@Override
|
||||||
public void onRemove(final Entity.RemovalReason reason) {
|
public void onRemove(final Entity.RemovalReason reason) {
|
||||||
final Entity entity = this.entity;
|
final Entity entity = this.entity;
|
||||||
EntityLookup.this.checkThread(entity, "Cannot remove entity off-main"); // Paper - rewrite chunk system
|
EntityLookup.this.checkThread(entity, "Cannot remove entity off-main");
|
||||||
final Visibility tickingState = EntityLookup.getEntityStatus(entity);
|
final Visibility tickingState = EntityLookup.getEntityStatus(entity);
|
||||||
|
|
||||||
EntityLookup.this.removeEntity(entity);
|
EntityLookup.this.removeEntity(entity);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package ca.spottedleaf.moonrise.patches.chunk_system.level.poi;
|
|||||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.mojang.serialization.DataResult;
|
import com.mojang.serialization.DataResult;
|
||||||
import net.minecraft.SharedConstants;
|
import net.minecraft.SharedConstants;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
@@ -123,7 +122,6 @@ public final class PoiChunk {
|
|||||||
ret.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
|
ret.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
|
||||||
|
|
||||||
final ServerLevel world = this.world;
|
final ServerLevel world = this.world;
|
||||||
final PoiManager poiManager = world.getPoiManager();
|
|
||||||
final int chunkX = this.chunkX;
|
final int chunkX = this.chunkX;
|
||||||
final int chunkZ = this.chunkZ;
|
final int chunkZ = this.chunkZ;
|
||||||
|
|
||||||
@@ -133,13 +131,8 @@ public final class PoiChunk {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final long key = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
|
// I do not believe asynchronously converting to CompoundTag is worth the scheduling.
|
||||||
// codecs are honestly such a fucking disaster. What the fuck is this trash?
|
final DataResult<Tag> serializedResult = PoiSection.Packed.CODEC.encodeStart(registryOps, section.pack());
|
||||||
final Codec<PoiSection> codec = PoiSection.codec(() -> {
|
|
||||||
poiManager.setDirty(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
final DataResult<Tag> serializedResult = codec.encodeStart(registryOps, section);
|
|
||||||
final int finalSectionY = sectionY;
|
final int finalSectionY = sectionY;
|
||||||
final Tag serialized = serializedResult.resultOrPartial((final String description) -> {
|
final Tag serialized = serializedResult.resultOrPartial((final String description) -> {
|
||||||
LOGGER.error("Failed to serialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
|
LOGGER.error("Failed to serialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
|
||||||
@@ -183,19 +176,18 @@ public final class PoiChunk {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final long coordinateKey = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
|
|
||||||
// codecs are honestly such a fucking disaster. What the fuck is this trash?
|
|
||||||
final Codec<PoiSection> codec = PoiSection.codec(() -> {
|
|
||||||
poiManager.setDirty(coordinateKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
final CompoundTag section = sections.getCompound(key);
|
final CompoundTag section = sections.getCompound(key);
|
||||||
final DataResult<PoiSection> deserializeResult = codec.parse(registryOps, section);
|
final DataResult<PoiSection.Packed> deserializeResult = PoiSection.Packed.CODEC.parse(registryOps, section);
|
||||||
final int finalSectionY = sectionY;
|
final int finalSectionY = sectionY;
|
||||||
final PoiSection deserialized = deserializeResult.resultOrPartial((final String description) -> {
|
final PoiSection.Packed packed = deserializeResult.resultOrPartial((final String description) -> {
|
||||||
LOGGER.error("Failed to deserialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
|
LOGGER.error("Failed to deserialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
|
||||||
}).orElse(null);
|
}).orElse(null);
|
||||||
|
|
||||||
|
final long coordinateKey = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
|
||||||
|
final PoiSection deserialized = packed == null ? null : packed.unpack(() -> {
|
||||||
|
poiManager.setDirty(coordinateKey);
|
||||||
|
});
|
||||||
|
|
||||||
if (deserialized == null || ((ChunkSystemPoiSection)deserialized).moonrise$isEmpty()) {
|
if (deserialized == null || ((ChunkSystemPoiSection)deserialized).moonrise$isEmpty()) {
|
||||||
// completely empty, no point in storing this
|
// completely empty, no point in storing this
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.chunk_system.level.storage;
|
package ca.spottedleaf.moonrise.patches.chunk_system.level.storage;
|
||||||
|
|
||||||
import com.mojang.serialization.Dynamic;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public interface ChunkSystemSectionStorage {
|
public interface ChunkSystemSectionStorage {
|
||||||
|
|
||||||
public CompoundTag moonrise$read(final int chunkX, final int chunkZ) throws IOException;
|
|
||||||
|
|
||||||
public void moonrise$write(final int chunkX, final int chunkZ, final CompoundTag data) throws IOException;
|
|
||||||
|
|
||||||
public RegionFileStorage moonrise$getRegionStorage();
|
public RegionFileStorage moonrise$getRegionStorage();
|
||||||
|
|
||||||
public void moonrise$close() throws IOException;
|
public void moonrise$close() throws IOException;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|||||||
import ca.spottedleaf.moonrise.common.misc.AllocatingRateLimiter;
|
import ca.spottedleaf.moonrise.common.misc.AllocatingRateLimiter;
|
||||||
import ca.spottedleaf.moonrise.common.misc.SingleUserAreaMap;
|
import ca.spottedleaf.moonrise.common.misc.SingleUserAreaMap;
|
||||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||||
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
|
||||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||||
@@ -16,13 +15,10 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManage
|
|||||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
|
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import it.unimi.dsi.fastutil.HashCommon;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import it.unimi.dsi.fastutil.longs.LongComparator;
|
import it.unimi.dsi.fastutil.longs.LongComparator;
|
||||||
import it.unimi.dsi.fastutil.longs.LongHeapPriorityQueue;
|
import it.unimi.dsi.fastutil.longs.LongHeapPriorityQueue;
|
||||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
import net.minecraft.network.protocol.Packet;
|
import net.minecraft.network.protocol.Packet;
|
||||||
import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket;
|
import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket;
|
||||||
@@ -42,8 +38,6 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|||||||
import net.minecraft.world.level.levelgen.BelowZeroRetrogen;
|
import net.minecraft.world.level.levelgen.BelowZeroRetrogen;
|
||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -433,11 +427,11 @@ public final class RegionizedPlayerChunkLoader {
|
|||||||
if (!this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
|
if (!this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PlatformHooks.get().onChunkUnWatch(this.world, new ChunkPos(chunkX, chunkZ), this.player);
|
|
||||||
this.sendUnloadChunkRaw(chunkX, chunkZ);
|
this.sendUnloadChunkRaw(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendUnloadChunkRaw(final int chunkX, final int chunkZ) {
|
private void sendUnloadChunkRaw(final int chunkX, final int chunkZ) {
|
||||||
|
PlatformHooks.get().onChunkUnWatch(this.world, new ChunkPos(chunkX, chunkZ), this.player);
|
||||||
// Note: Check PlayerChunkSender#dropChunk for other logic
|
// Note: Check PlayerChunkSender#dropChunk for other logic
|
||||||
// Note: drop isAlive() check so that chunks properly unload client-side when the player dies
|
// Note: drop isAlive() check so that chunks properly unload client-side when the player dies
|
||||||
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
||||||
@@ -831,7 +825,7 @@ public final class RegionizedPlayerChunkLoader {
|
|||||||
}
|
}
|
||||||
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
||||||
// not yet post-processed, need to do this so that tile entities can properly be sent to clients
|
// not yet post-processed, need to do this so that tile entities can properly be sent to clients
|
||||||
chunk.postProcessGeneration();
|
chunk.postProcessGeneration(this.world);
|
||||||
// check if there was any recursive action
|
// check if there was any recursive action
|
||||||
if (this.removed || this.sendQueue.isEmpty() || this.sendQueue.firstLong() != pendingSend) {
|
if (this.removed || this.sendQueue.isEmpty() || this.sendQueue.firstLong() != pendingSend) {
|
||||||
return;
|
return;
|
||||||
@@ -1083,5 +1077,9 @@ public final class RegionizedPlayerChunkLoader {
|
|||||||
|
|
||||||
// now all tickets should be removed, which is all of our external state
|
// now all tickets should be removed, which is all of our external state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LongOpenHashSet getSentChunksRaw() {
|
||||||
|
return this.sentChunks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,8 +231,8 @@ public final class ChunkHolderManager {
|
|||||||
public void autoSave() {
|
public void autoSave() {
|
||||||
final List<NewChunkHolder> reschedule = new ArrayList<>();
|
final List<NewChunkHolder> reschedule = new ArrayList<>();
|
||||||
final long currentTick = this.currentTick;
|
final long currentTick = this.currentTick;
|
||||||
final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval());
|
final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval(this.world));
|
||||||
final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick();
|
final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick(this.world);
|
||||||
for (int autoSaved = 0; autoSaved < maxToSave && !this.autoSaveQueue.isEmpty();) {
|
for (int autoSaved = 0; autoSaved < maxToSave && !this.autoSaveQueue.isEmpty();) {
|
||||||
final NewChunkHolder holder = this.autoSaveQueue.first();
|
final NewChunkHolder holder = this.autoSaveQueue.first();
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import ca.spottedleaf.concurrentutil.executor.thread.PrioritisedThreadPool;
|
|||||||
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
||||||
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
||||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||||
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
|
|
||||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||||
import ca.spottedleaf.moonrise.common.util.JsonUtil;
|
import ca.spottedleaf.moonrise.common.util.JsonUtil;
|
||||||
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
||||||
@@ -121,6 +120,7 @@ public final class ChunkTaskScheduler {
|
|||||||
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor loadExecutor;
|
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor loadExecutor;
|
||||||
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor ioExecutor;
|
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor ioExecutor;
|
||||||
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor compressionExecutor;
|
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor compressionExecutor;
|
||||||
|
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor saveExecutor;
|
||||||
|
|
||||||
private final PrioritisedTaskQueue mainThreadExecutor = new PrioritisedTaskQueue();
|
private final PrioritisedTaskQueue mainThreadExecutor = new PrioritisedTaskQueue();
|
||||||
|
|
||||||
@@ -288,6 +288,7 @@ public final class ChunkTaskScheduler {
|
|||||||
this.ioExecutor = MoonriseCommon.SERVER_REGION_IO_GROUP.createExecutor(-1, MoonriseCommon.IO_QUEUE_HOLD_TIME, 0);
|
this.ioExecutor = MoonriseCommon.SERVER_REGION_IO_GROUP.createExecutor(-1, MoonriseCommon.IO_QUEUE_HOLD_TIME, 0);
|
||||||
// we need a separate executor here so that on shutdown we can continue to process I/O tasks
|
// we need a separate executor here so that on shutdown we can continue to process I/O tasks
|
||||||
this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
||||||
|
this.saveExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
||||||
this.chunkHolderManager = new ChunkHolderManager(world, this);
|
this.chunkHolderManager = new ChunkHolderManager(world, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -855,12 +856,14 @@ public final class ChunkTaskScheduler {
|
|||||||
|
|
||||||
public boolean haltIO(final boolean sync, final long maxWaitNS) {
|
public boolean haltIO(final boolean sync, final long maxWaitNS) {
|
||||||
this.ioExecutor.halt();
|
this.ioExecutor.halt();
|
||||||
|
this.saveExecutor.halt();
|
||||||
this.compressionExecutor.halt();
|
this.compressionExecutor.halt();
|
||||||
if (sync) {
|
if (sync) {
|
||||||
final long time = System.nanoTime();
|
final long time = System.nanoTime();
|
||||||
for (long failures = 9L;; failures = ConcurrentUtil.linearLongBackoff(failures, 500_000L, 50_000_000L)) {
|
for (long failures = 9L;; failures = ConcurrentUtil.linearLongBackoff(failures, 500_000L, 50_000_000L)) {
|
||||||
if (
|
if (
|
||||||
!this.ioExecutor.isActive() &&
|
!this.ioExecutor.isActive() &&
|
||||||
|
!this.saveExecutor.isActive() &&
|
||||||
!this.compressionExecutor.isActive()
|
!this.compressionExecutor.isActive()
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|||||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemFeatures;
|
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.async_save.AsyncChunkSaveData;
|
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||||
@@ -47,7 +45,7 @@ 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.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
@@ -760,7 +758,6 @@ public final class NewChunkHolder {
|
|||||||
|
|
||||||
void onUnload() {
|
void onUnload() {
|
||||||
this.unloaded = true;
|
this.unloaded = true;
|
||||||
((ChunkSystemServerLevel)this.world).moonrise$removeUnsyncedChunk(this.vanillaChunkHolder);
|
|
||||||
((ChunkSystemLevel)this.world).moonrise$releaseChunkData(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ));
|
((ChunkSystemLevel)this.world).moonrise$releaseChunkData(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,7 +860,7 @@ public final class NewChunkHolder {
|
|||||||
|
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
final LazyRunnable toRun = new LazyRunnable();
|
final LazyRunnable toRun = new LazyRunnable();
|
||||||
this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.loadExecutor.createTask(toRun), toRun);
|
this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.saveExecutor.createTask(toRun), toRun);
|
||||||
}
|
}
|
||||||
if (poiChunk != null) {
|
if (poiChunk != null) {
|
||||||
this.poiDataUnload = new UnloadTask(new CallbackCompletable<>(), null, null);
|
this.poiDataUnload = new UnloadTask(new CallbackCompletable<>(), null, null);
|
||||||
@@ -898,7 +895,7 @@ public final class NewChunkHolder {
|
|||||||
final ChunkEntitySlices entityChunk = state.entityChunk();
|
final ChunkEntitySlices entityChunk = state.entityChunk();
|
||||||
final PoiChunk poiChunk = state.poiChunk();
|
final PoiChunk poiChunk = state.poiChunk();
|
||||||
|
|
||||||
final boolean shouldLevelChunkNotSave = ChunkSystemFeatures.forceNoSave(chunk);
|
final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk);
|
||||||
|
|
||||||
// unload chunk data
|
// unload chunk data
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
@@ -1081,7 +1078,7 @@ public final class NewChunkHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't really have a choice but to place this hook here
|
// Don't really have a choice but to place this hook here
|
||||||
PlatformHooks.get().onChunkHolderTicketChange(this.world, this, oldLevel, newLevel);
|
PlatformHooks.get().onChunkHolderTicketChange(this.world, this.vanillaChunkHolder, oldLevel, newLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final int NEIGHBOUR_RADIUS = 2;
|
static final int NEIGHBOUR_RADIUS = 2;
|
||||||
@@ -1713,7 +1710,7 @@ public final class NewChunkHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean forceNoSaveChunk = ChunkSystemFeatures.forceNoSave(chunk);
|
final boolean forceNoSaveChunk = PlatformHooks.get().forceNoSave(chunk);
|
||||||
|
|
||||||
// can only synchronously save worldgen chunks during shutdown
|
// can only synchronously save worldgen chunks during shutdown
|
||||||
boolean canSaveChunk = !forceNoSaveChunk && (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved()));
|
boolean canSaveChunk = !forceNoSaveChunk && (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved()));
|
||||||
@@ -1743,56 +1740,6 @@ public final class NewChunkHolder {
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class AsyncChunkSerializeTask implements Runnable {
|
|
||||||
|
|
||||||
private final ServerLevel world;
|
|
||||||
private final ChunkAccess chunk;
|
|
||||||
private final AsyncChunkSaveData asyncSaveData;
|
|
||||||
private final NewChunkHolder toComplete;
|
|
||||||
|
|
||||||
public AsyncChunkSerializeTask(final ServerLevel world, final ChunkAccess chunk, final AsyncChunkSaveData asyncSaveData,
|
|
||||||
final NewChunkHolder toComplete) {
|
|
||||||
this.world = world;
|
|
||||||
this.chunk = chunk;
|
|
||||||
this.asyncSaveData = asyncSaveData;
|
|
||||||
this.toComplete = toComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
final CompoundTag toSerialize;
|
|
||||||
try {
|
|
||||||
toSerialize = ChunkSystemFeatures.saveChunkAsync(this.world, this.chunk, this.asyncSaveData);
|
|
||||||
} catch (final Throwable throwable) {
|
|
||||||
LOGGER.error("Failed to asynchronously save chunk " + this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(this.world) + "', falling back to synchronous save", throwable);
|
|
||||||
final ChunkPos pos = this.chunk.getPos();
|
|
||||||
((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().scheduleChunkTask(pos.x, pos.z, () -> {
|
|
||||||
final CompoundTag synchronousSave;
|
|
||||||
try {
|
|
||||||
synchronousSave = ChunkSystemFeatures.saveChunkAsync(AsyncChunkSerializeTask.this.world, AsyncChunkSerializeTask.this.chunk, AsyncChunkSerializeTask.this.asyncSaveData);
|
|
||||||
} catch (final Throwable throwable2) {
|
|
||||||
LOGGER.error("Failed to synchronously save chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(AsyncChunkSerializeTask.this.world) + "', chunk data will be lost", throwable2);
|
|
||||||
AsyncChunkSerializeTask.this.toComplete.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncChunkSerializeTask.this.toComplete.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, synchronousSave);
|
|
||||||
LOGGER.info("Successfully serialized chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(AsyncChunkSerializeTask.this.world) + "' synchronously");
|
|
||||||
|
|
||||||
}, Priority.HIGHEST);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.toComplete.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, toSerialize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "AsyncChunkSerializeTask{" +
|
|
||||||
"chunk={pos=" + this.chunk.getPos() + ",world=\"" + WorldUtil.getWorldName(this.world) + "\"}" +
|
|
||||||
"}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean saveChunk(final ChunkAccess chunk, final boolean unloading) {
|
private boolean saveChunk(final ChunkAccess chunk, final boolean unloading) {
|
||||||
if (!chunk.isUnsaved()) {
|
if (!chunk.isUnsaved()) {
|
||||||
if (unloading) {
|
if (unloading) {
|
||||||
@@ -1800,45 +1747,39 @@ public final class NewChunkHolder {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean completing = false;
|
|
||||||
boolean failedAsyncPrepare = false;
|
|
||||||
try {
|
try {
|
||||||
if (unloading && ChunkSystemFeatures.supportsAsyncChunkSave()) {
|
final SerializableChunkData chunkData = SerializableChunkData.copyOf(this.world, chunk);
|
||||||
try {
|
PlatformHooks.get().chunkSyncSave(this.world, chunk, chunkData);
|
||||||
final AsyncChunkSaveData asyncSaveData = ChunkSystemFeatures.getAsyncSaveData(this.world, chunk);
|
|
||||||
|
|
||||||
this.chunkDataUnload.toRun().setRunnable(new AsyncChunkSerializeTask(this.world, chunk, asyncSaveData, this));
|
chunk.tryMarkSaved();
|
||||||
|
|
||||||
chunk.setUnsaved(false);
|
final CallbackCompletable<CompoundTag> completable = new CallbackCompletable<>();
|
||||||
|
|
||||||
this.chunkDataUnload.task().queue();
|
final Runnable run = () -> {
|
||||||
|
final CompoundTag data = chunkData.write();
|
||||||
|
|
||||||
return true;
|
completable.complete(data);
|
||||||
} catch (final Throwable thr) {
|
|
||||||
LOGGER.error("Failed to prepare async chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "', falling back to synchronous save", thr);
|
if (unloading) {
|
||||||
failedAsyncPrepare = true;
|
NewChunkHolder.this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, data);
|
||||||
// fall through to synchronous save
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
final CompoundTag save = ChunkSerializer.write(this.world, chunk);
|
|
||||||
PlatformHooks.get().chunkSyncSave(this.world, chunk, save);
|
|
||||||
|
|
||||||
|
final PrioritisedExecutor.PrioritisedTask task;
|
||||||
if (unloading) {
|
if (unloading) {
|
||||||
completing = true;
|
this.chunkDataUnload.toRun().setRunnable(run);
|
||||||
this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, save);
|
task = this.chunkDataUnload.task();
|
||||||
if (failedAsyncPrepare) {
|
|
||||||
LOGGER.info("Successfully serialized chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "' synchronously");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, save, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
|
task = this.scheduler.saveExecutor.createTask(run);
|
||||||
}
|
}
|
||||||
chunk.setUnsaved(false);
|
|
||||||
|
task.queue();
|
||||||
|
|
||||||
|
MoonriseRegionFileIO.scheduleSave(
|
||||||
|
this.world, this.chunkX, this.chunkZ, completable, task, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, Priority.NORMAL
|
||||||
|
);
|
||||||
} catch (final Throwable thr) {
|
} catch (final Throwable thr) {
|
||||||
LOGGER.error("Failed to save chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "'", thr);
|
LOGGER.error("Failed to save chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "'", thr);
|
||||||
if (unloading && !completing) {
|
|
||||||
this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
|||||||
final ServerLevel world = this.world;
|
final ServerLevel world = this.world;
|
||||||
final ProtoChunk protoChunk = (ProtoChunk)this.fromChunk;
|
final ProtoChunk protoChunk = (ProtoChunk)this.fromChunk;
|
||||||
chunk = new LevelChunk(this.world, protoChunk, (final LevelChunk unused) -> {
|
chunk = new LevelChunk(this.world, protoChunk, (final LevelChunk unused) -> {
|
||||||
ChunkStatusTasks.postLoadProtoChunk(world, protoChunk.getEntities());
|
PlatformHooks.get().postLoadProtoChunk(world, protoChunk);
|
||||||
});
|
});
|
||||||
this.chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false));
|
this.chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false));
|
||||||
}
|
}
|
||||||
@@ -82,16 +82,10 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
|||||||
// This brings entity addition back in line with older versions of the game
|
// This brings entity addition back in line with older versions of the game
|
||||||
// Since we load the NBT in the empty status, this will never block for I/O
|
// Since we load the NBT in the empty status, this will never block for I/O
|
||||||
((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getOrCreateEntityChunk(this.chunkX, this.chunkZ, false);
|
((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getOrCreateEntityChunk(this.chunkX, this.chunkZ, false);
|
||||||
} finally {
|
chunk.setLoaded(true);
|
||||||
platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we don't need the entitiesInLevel, not sure why it's there
|
|
||||||
chunk.setLoaded(true);
|
|
||||||
try {
|
|
||||||
platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, chunk);
|
|
||||||
chunk.registerAllBlockEntitiesAfterLevelLoad();
|
chunk.registerAllBlockEntitiesAfterLevelLoad();
|
||||||
chunk.registerTickContainerInLevel(this.world);
|
chunk.registerTickContainerInLevel(this.world);
|
||||||
|
chunk.setUnsavedListener(this.world.getChunkSource().chunkMap.worldGenContext.unsavedListener());
|
||||||
platformHooks.chunkFullStatusComplete(chunk, (ProtoChunk)this.fromChunk);
|
platformHooks.chunkFullStatusComplete(chunk, (ProtoChunk)this.fromChunk);
|
||||||
} finally {
|
} finally {
|
||||||
platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, null);
|
platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, null);
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|||||||
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
||||||
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
||||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||||
|
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemConverters;
|
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemConverters;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemFeatures;
|
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk;
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
||||||
@@ -19,7 +19,7 @@ import net.minecraft.world.level.chunk.ChunkAccess;
|
|||||||
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.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -273,9 +273,12 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ChunkDataLoadTask extends CallbackDataLoadTask<CompoundTag, ChunkAccess> {
|
|
||||||
|
private static record ReadChunk(ProtoChunk protoChunk, SerializableChunkData chunkData) {}
|
||||||
|
|
||||||
|
private static final class ChunkDataLoadTask extends CallbackDataLoadTask<ReadChunk, ChunkAccess> {
|
||||||
private ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
private ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
||||||
final int chunkZ, final Priority priority) {
|
final int chunkZ, final Priority priority) {
|
||||||
super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, priority);
|
super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,30 +303,32 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final CompoundTag data, final Throwable throwable) {
|
protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final ReadChunk data, final Throwable throwable) {
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
return new TaskResult<>(null, throwable);
|
return new TaskResult<>(null, throwable);
|
||||||
}
|
}
|
||||||
if (data == null) {
|
|
||||||
|
if (data == null || data.protoChunk() == null) {
|
||||||
return new TaskResult<>(this.getEmptyChunk(), null);
|
return new TaskResult<>(this.getEmptyChunk(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ChunkSystemFeatures.supportsAsyncChunkDeserialization()) {
|
if (!PlatformHooks.get().hasMainChunkLoadHook()) {
|
||||||
return this.deserialize(data);
|
return new TaskResult<>(data.protoChunk(), null);
|
||||||
}
|
}
|
||||||
// need to deserialize on main thread
|
|
||||||
|
// need to invoke the callback for loading on the main thread
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProtoChunk getEmptyChunk() {
|
private ProtoChunk getEmptyChunk() {
|
||||||
return new ProtoChunk(
|
return new ProtoChunk(
|
||||||
new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
|
new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
|
||||||
this.world.registryAccess().registryOrThrow(Registries.BIOME), (BlendingData)null
|
this.world.registryAccess().lookupOrThrow(Registries.BIOME), (BlendingData)null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TaskResult<CompoundTag, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
|
protected TaskResult<ReadChunk, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
LOGGER.error("Failed to load chunk data for task: " + this.toString() + ", chunk data will be lost", throwable);
|
LOGGER.error("Failed to load chunk data for task: " + this.toString() + ", chunk data will be lost", throwable);
|
||||||
return new TaskResult<>(null, null);
|
return new TaskResult<>(null, null);
|
||||||
@@ -337,32 +342,33 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|||||||
// run converters
|
// run converters
|
||||||
final CompoundTag converted = this.world.getChunkSource().chunkMap.upgradeChunkTag(data);
|
final CompoundTag converted = this.world.getChunkSource().chunkMap.upgradeChunkTag(data);
|
||||||
|
|
||||||
return new TaskResult<>(converted, null);
|
// unpack the data
|
||||||
|
final SerializableChunkData chunkData = SerializableChunkData.parse(
|
||||||
|
this.world, this.world.registryAccess(), converted
|
||||||
|
);
|
||||||
|
|
||||||
|
if (chunkData == null) {
|
||||||
|
LOGGER.error("Deserialized chunk for task: " + this.toString() + " produced null, chunk data will be lost?");
|
||||||
|
}
|
||||||
|
|
||||||
|
// read into ProtoChunk
|
||||||
|
final ProtoChunk chunk = chunkData == null ? null : chunkData.read(
|
||||||
|
this.world, this.world.getPoiManager(), this.world.getChunkSource().chunkMap.storageInfo(),
|
||||||
|
new ChunkPos(this.chunkX, this.chunkZ)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new TaskResult<>(new ReadChunk(chunk, chunkData), null);
|
||||||
} catch (final Throwable thr2) {
|
} catch (final Throwable thr2) {
|
||||||
LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
|
LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
|
||||||
return new TaskResult<>(null, null);
|
return new TaskResult<>(null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TaskResult<ChunkAccess, Throwable> deserialize(final CompoundTag data) {
|
|
||||||
try {
|
|
||||||
final ChunkAccess deserialized = ChunkSerializer.read(
|
|
||||||
this.world, this.world.getPoiManager(), this.world.getChunkSource().chunkMap.storageInfo(), new ChunkPos(this.chunkX, this.chunkZ), data
|
|
||||||
);
|
|
||||||
return new TaskResult<>(deserialized, null);
|
|
||||||
} catch (final Throwable thr2) {
|
|
||||||
LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
|
|
||||||
return new TaskResult<>(this.getEmptyChunk(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TaskResult<ChunkAccess, Throwable> runOnMain(final CompoundTag data, final Throwable throwable) {
|
protected TaskResult<ChunkAccess, Throwable> runOnMain(final ReadChunk data, final Throwable throwable) {
|
||||||
// data != null && throwable == null
|
PlatformHooks.get().mainChunkLoad(data.protoChunk(), data.chunkData());
|
||||||
if (ChunkSystemFeatures.supportsAsyncChunkDeserialization()) {
|
|
||||||
throw new UnsupportedOperationException();
|
return new TaskResult<>(data.protoChunk(), null);
|
||||||
}
|
|
||||||
return this.deserialize(data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.collisions;
|
package ca.spottedleaf.moonrise.patches.collisions;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||||
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData;
|
import ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData;
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionDiscreteVoxelShape;
|
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionDiscreteVoxelShape;
|
||||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
|
||||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||||
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||||
import it.unimi.dsi.fastutil.doubles.DoubleList;
|
import it.unimi.dsi.fastutil.doubles.DoubleList;
|
||||||
@@ -1231,7 +1231,7 @@ public final class CollisionUtil {
|
|||||||
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
|
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
|
||||||
ft, tf
|
ft, tf
|
||||||
);
|
);
|
||||||
if (mergedX == MergedVoxelCoordinateList.EMPTY) {
|
if (mergedX == null) {
|
||||||
return Shapes.empty();
|
return Shapes.empty();
|
||||||
}
|
}
|
||||||
final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
|
final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
|
||||||
@@ -1239,7 +1239,7 @@ public final class CollisionUtil {
|
|||||||
((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
|
((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
|
||||||
ft, tf
|
ft, tf
|
||||||
);
|
);
|
||||||
if (mergedY == MergedVoxelCoordinateList.EMPTY) {
|
if (mergedY == null) {
|
||||||
return Shapes.empty();
|
return Shapes.empty();
|
||||||
}
|
}
|
||||||
final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
|
final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
|
||||||
@@ -1247,7 +1247,7 @@ public final class CollisionUtil {
|
|||||||
((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
|
((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
|
||||||
ft, tf
|
ft, tf
|
||||||
);
|
);
|
||||||
if (mergedZ == MergedVoxelCoordinateList.EMPTY) {
|
if (mergedZ == null) {
|
||||||
return Shapes.empty();
|
return Shapes.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1329,7 +1329,7 @@ public final class CollisionUtil {
|
|||||||
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
|
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
|
||||||
ft, tf
|
ft, tf
|
||||||
);
|
);
|
||||||
if (mergedX == MergedVoxelCoordinateList.EMPTY) {
|
if (mergedX == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
|
final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
|
||||||
@@ -1337,7 +1337,7 @@ public final class CollisionUtil {
|
|||||||
((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
|
((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
|
||||||
ft, tf
|
ft, tf
|
||||||
);
|
);
|
||||||
if (mergedY == MergedVoxelCoordinateList.EMPTY) {
|
if (mergedY == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
|
final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
|
||||||
@@ -1345,7 +1345,7 @@ public final class CollisionUtil {
|
|||||||
((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
|
((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
|
||||||
ft, tf
|
ft, tf
|
||||||
);
|
);
|
||||||
if (mergedZ == MergedVoxelCoordinateList.EMPTY) {
|
if (mergedZ == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1368,10 +1368,6 @@ public final class CollisionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final MergedVoxelCoordinateList EMPTY = new MergedVoxelCoordinateList(
|
|
||||||
new double[] { 0.0 }, 0.0, new int[0], new int[0], 0
|
|
||||||
);
|
|
||||||
|
|
||||||
private static int[] getIndices(final int length) {
|
private static int[] getIndices(final int length) {
|
||||||
final int[] ret = new int[length];
|
final int[] ret = new int[length];
|
||||||
|
|
||||||
@@ -1506,7 +1502,7 @@ public final class CollisionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultSize <= 1 ? EMPTY : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
|
return resultSize <= 1 ? null : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1934,13 +1930,13 @@ public final class CollisionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
final int minSection = WorldUtil.getMinSection(world);
|
||||||
|
|
||||||
final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
|
final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
|
||||||
final int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
|
final int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
|
||||||
|
|
||||||
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - COLLISION_EPSILON) - 1);
|
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - COLLISION_EPSILON) - 1);
|
||||||
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
||||||
|
|
||||||
final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
|
final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
|
||||||
final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
|
final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
|
||||||
|
|||||||
@@ -1,246 +0,0 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.collisions.util;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Spliterator;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.BinaryOperator;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.IntFunction;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.function.ToDoubleFunction;
|
|
||||||
import java.util.function.ToIntFunction;
|
|
||||||
import java.util.function.ToLongFunction;
|
|
||||||
import java.util.stream.Collector;
|
|
||||||
import java.util.stream.DoubleStream;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import java.util.stream.LongStream;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public final class NoneMatchStream<T> implements Stream<T> {
|
|
||||||
|
|
||||||
private final boolean value;
|
|
||||||
|
|
||||||
public NoneMatchStream(final boolean value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean noneMatch(Predicate<? super T> predicate) {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<T> filter(Predicate<? super T> predicate) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IntStream mapToInt(ToIntFunction<? super T> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LongStream mapToLong(ToLongFunction<? super T> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<T> distinct() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<T> sorted() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<T> sorted(Comparator<? super T> comparator) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<T> peek(Consumer<? super T> action) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<T> limit(long maxSize) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<T> skip(long n) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forEach(Consumer<? super T> action) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forEachOrdered(Consumer<? super T> action) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Object[] toArray() {
|
|
||||||
return new Object[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public <A> A[] toArray(IntFunction<A[]> generator) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T reduce(T identity, BinaryOperator<T> accumulator) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Optional<T> reduce(BinaryOperator<T> accumulator) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <R, A> R collect(Collector<? super T, A, R> collector) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Optional<T> min(Comparator<? super T> comparator) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Optional<T> max(Comparator<? super T> comparator) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long count() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean anyMatch(Predicate<? super T> predicate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean allMatch(Predicate<? super T> predicate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Optional<T> findFirst() {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Optional<T> findAny() {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterator<T> iterator() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Spliterator<T> spliterator() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isParallel() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Stream<T> sequential() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Stream<T> parallel() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Stream<T> unordered() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Stream<T> onClose(Runnable closeHandler) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.getblock;
|
|
||||||
|
|
||||||
public interface GetBlockLevel {
|
|
||||||
|
|
||||||
public int moonrise$getMinSection();
|
|
||||||
|
|
||||||
public int moonrise$getMaxSection();
|
|
||||||
|
|
||||||
public int moonrise$getMinBuildHeight();
|
|
||||||
|
|
||||||
public int moonrise$getMaxBuildHeight();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -93,7 +93,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|||||||
|
|
||||||
final int currentLevel = this.getLightLevel(worldX, worldY, worldZ);
|
final int currentLevel = this.getLightLevel(worldX, worldY, worldZ);
|
||||||
final BlockState blockState = this.getBlockState(worldX, worldY, worldZ);
|
final BlockState blockState = this.getBlockState(worldX, worldY, worldZ);
|
||||||
final int emittedLevel = (PlatformHooks.get().getLightEmission(blockState, lightAccess.getLevel(), this.mutablePos1.set(worldX, worldY, worldZ))) & emittedMask;
|
final int emittedLevel = (PlatformHooks.get().getLightEmission(blockState, lightAccess.getLevel(), this.lightEmissionPos.set(worldX, worldY, worldZ))) & emittedMask;
|
||||||
|
|
||||||
this.setLightLevel(worldX, worldY, worldZ, emittedLevel);
|
this.setLightLevel(worldX, worldY, worldZ, emittedLevel);
|
||||||
// this accounts for change in emitted light that would cause an increase
|
// this accounts for change in emitted light that would cause an increase
|
||||||
@@ -121,7 +121,6 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos();
|
protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos();
|
||||||
protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
|
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
|
||||||
@@ -136,7 +135,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int opacity = Math.max(1, centerState.getLightBlock(world, this.recalcCenterPos));
|
final int opacity = Math.max(1, centerState.getLightBlock());
|
||||||
if (opacity >= 15) {
|
if (opacity >= 15) {
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
@@ -167,9 +166,8 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|||||||
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
|
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
|
||||||
// we don't read the blockstate because most of the time this is false, so using the faster
|
// we don't read the blockstate because most of the time this is false, so using the faster
|
||||||
// known transparency lookup results in a net win
|
// known transparency lookup results in a net win
|
||||||
this.recalcNeighbourPos.set(offX, offY, offZ);
|
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
|
||||||
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms);
|
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
|
||||||
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms);
|
|
||||||
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
|
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
|
||||||
// not allowed to propagate
|
// not allowed to propagate
|
||||||
continue;
|
continue;
|
||||||
@@ -220,7 +218,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|||||||
final PalettedContainer<BlockState> states = section.states;
|
final PalettedContainer<BlockState> states = section.states;
|
||||||
final int offY = sectionY << 4;
|
final int offY = sectionY << 4;
|
||||||
|
|
||||||
final BlockPos.MutableBlockPos mutablePos = this.mutablePos1;
|
final BlockPos.MutableBlockPos mutablePos = this.lightEmissionPos;
|
||||||
for (int index = 0; index < (16 * 16 * 16); ++index) {
|
for (int index = 0; index < (16 * 16 * 16); ++index) {
|
||||||
final BlockState state = states.get(index);
|
final BlockState state = states.get(index);
|
||||||
mutablePos.set(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15));
|
mutablePos.set(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15));
|
||||||
|
|||||||
@@ -290,9 +290,6 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos();
|
|
||||||
protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
|
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
|
||||||
final int expect) {
|
final int expect) {
|
||||||
@@ -304,8 +301,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|||||||
final BlockState centerState = this.getBlockState(worldX, worldY, worldZ);
|
final BlockState centerState = this.getBlockState(worldX, worldY, worldZ);
|
||||||
|
|
||||||
final BlockState conditionallyOpaqueState;
|
final BlockState conditionallyOpaqueState;
|
||||||
this.recalcCenterPos.set(worldX, worldY, worldZ);
|
final int opacity = Math.max(1, centerState.getLightBlock());
|
||||||
final int opacity = Math.max(1, centerState.getLightBlock(lightAccess.getLevel(), this.recalcCenterPos));
|
|
||||||
if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
|
if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
|
||||||
conditionallyOpaqueState = centerState;
|
conditionallyOpaqueState = centerState;
|
||||||
} else {
|
} else {
|
||||||
@@ -334,9 +330,8 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|||||||
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
|
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
|
||||||
// we don't read the blockstate because most of the time this is false, so using the faster
|
// we don't read the blockstate because most of the time this is false, so using the faster
|
||||||
// known transparency lookup results in a net win
|
// known transparency lookup results in a net win
|
||||||
this.recalcNeighbourPos.set(offX, offY, offZ);
|
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
|
||||||
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms);
|
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
|
||||||
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms);
|
|
||||||
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
|
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
|
||||||
// not allowed to propagate
|
// not allowed to propagate
|
||||||
continue;
|
continue;
|
||||||
@@ -604,7 +599,6 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|||||||
// clobbering the light values will result in broken propagation)
|
// clobbering the light values will result in broken propagation)
|
||||||
protected final int tryPropagateSkylight(final BlockGetter world, final int worldX, int startY, final int worldZ,
|
protected final int tryPropagateSkylight(final BlockGetter world, final int worldX, int startY, final int worldZ,
|
||||||
final boolean extrudeInitialised, final boolean delayLightSet) {
|
final boolean extrudeInitialised, final boolean delayLightSet) {
|
||||||
final BlockPos.MutableBlockPos mutablePos = this.mutablePos3;
|
|
||||||
final int encodeOffset = this.coordinateOffset;
|
final int encodeOffset = this.coordinateOffset;
|
||||||
final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; // just don't check upwards.
|
final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; // just don't check upwards.
|
||||||
|
|
||||||
@@ -626,8 +620,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|||||||
|
|
||||||
final VoxelShape fromShape;
|
final VoxelShape fromShape;
|
||||||
if (((StarlightAbstractBlockState)above).starlight$isConditionallyFullOpaque()) {
|
if (((StarlightAbstractBlockState)above).starlight$isConditionallyFullOpaque()) {
|
||||||
this.mutablePos2.set(worldX, startY + 1, worldZ);
|
fromShape = above.getFaceOcclusionShape(AxisDirection.NEGATIVE_Y.nms);
|
||||||
fromShape = above.getFaceOcclusionShape(world, this.mutablePos2, AxisDirection.NEGATIVE_Y.nms);
|
|
||||||
if (Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
if (Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
||||||
// above wont let us propagate
|
// above wont let us propagate
|
||||||
break;
|
break;
|
||||||
@@ -637,10 +630,9 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// does light propagate from the top down?
|
// does light propagate from the top down?
|
||||||
mutablePos.set(worldX, startY, worldZ);
|
|
||||||
long flags = 0L;
|
long flags = 0L;
|
||||||
if (((StarlightAbstractBlockState)current).starlight$isConditionallyFullOpaque()) {
|
if (((StarlightAbstractBlockState)current).starlight$isConditionallyFullOpaque()) {
|
||||||
final VoxelShape cullingFace = current.getFaceOcclusionShape(world, mutablePos, AxisDirection.POSITIVE_Y.nms);
|
final VoxelShape cullingFace = current.getFaceOcclusionShape(AxisDirection.POSITIVE_Y.nms);
|
||||||
|
|
||||||
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
||||||
// can't propagate here, we're done on this column.
|
// can't propagate here, we're done on this column.
|
||||||
@@ -649,7 +641,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|||||||
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int opacity = current.getLightBlock(world, mutablePos);
|
final int opacity = current.getLightBlock();
|
||||||
if (opacity > 0) {
|
if (opacity > 0) {
|
||||||
// let the queued value (if any) handle it from here.
|
// let the queued value (if any) handle it from here.
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ public abstract class StarLightEngine {
|
|||||||
protected static enum AxisDirection {
|
protected static enum AxisDirection {
|
||||||
|
|
||||||
// Declaration order is important and relied upon. Do not change without modifying propagation code.
|
// Declaration order is important and relied upon. Do not change without modifying propagation code.
|
||||||
POSITIVE_X(1, 0, 0), NEGATIVE_X(-1, 0, 0),
|
POSITIVE_X(1, 0, 0, Direction.EAST) , NEGATIVE_X(-1, 0, 0, Direction.WEST),
|
||||||
POSITIVE_Z(0, 0, 1), NEGATIVE_Z(0, 0, -1),
|
POSITIVE_Z(0, 0, 1, Direction.SOUTH), NEGATIVE_Z(0, 0, -1, Direction.NORTH),
|
||||||
POSITIVE_Y(0, 1, 0), NEGATIVE_Y(0, -1, 0);
|
POSITIVE_Y(0, 1, 0, Direction.UP) , NEGATIVE_Y(0, -1, 0, Direction.DOWN);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
POSITIVE_X.opposite = NEGATIVE_X; NEGATIVE_X.opposite = POSITIVE_X;
|
POSITIVE_X.opposite = NEGATIVE_X; NEGATIVE_X.opposite = POSITIVE_X;
|
||||||
@@ -63,11 +63,11 @@ public abstract class StarLightEngine {
|
|||||||
public final long everythingButThisDirection;
|
public final long everythingButThisDirection;
|
||||||
public final long everythingButTheOppositeDirection;
|
public final long everythingButTheOppositeDirection;
|
||||||
|
|
||||||
AxisDirection(final int x, final int y, final int z) {
|
AxisDirection(final int x, final int y, final int z, final Direction nms) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
this.nms = Direction.fromDelta(x, y, z);
|
this.nms = nms;
|
||||||
this.everythingButThisDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << this.ordinal()));
|
this.everythingButThisDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << this.ordinal()));
|
||||||
// positive is always even, negative is always odd. Flip the 1 bit to get the negative direction.
|
// positive is always even, negative is always odd. Flip the 1 bit to get the negative direction.
|
||||||
this.everythingButTheOppositeDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << (this.ordinal() ^ 1)));
|
this.everythingButTheOppositeDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << (this.ordinal() ^ 1)));
|
||||||
@@ -107,9 +107,7 @@ public abstract class StarLightEngine {
|
|||||||
// index = x + (z * 5)
|
// index = x + (z * 5)
|
||||||
protected final boolean[][] emptinessMapCache = new boolean[5 * 5][];
|
protected final boolean[][] emptinessMapCache = new boolean[5 * 5][];
|
||||||
|
|
||||||
protected final BlockPos.MutableBlockPos mutablePos1 = new BlockPos.MutableBlockPos();
|
protected final BlockPos.MutableBlockPos lightEmissionPos = new BlockPos.MutableBlockPos();
|
||||||
protected final BlockPos.MutableBlockPos mutablePos2 = new BlockPos.MutableBlockPos();
|
|
||||||
protected final BlockPos.MutableBlockPos mutablePos3 = new BlockPos.MutableBlockPos();
|
|
||||||
|
|
||||||
protected int encodeOffsetX;
|
protected int encodeOffsetX;
|
||||||
protected int encodeOffsetY;
|
protected int encodeOffsetY;
|
||||||
@@ -1151,10 +1149,9 @@ public abstract class StarLightEngine {
|
|||||||
if (blockState == null) {
|
if (blockState == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.mutablePos1.set(offX, offY, offZ);
|
|
||||||
long flags = 0;
|
long flags = 0;
|
||||||
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
||||||
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
||||||
|
|
||||||
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1162,7 +1159,7 @@ public abstract class StarLightEngine {
|
|||||||
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
final int opacity = blockState.getLightBlock();
|
||||||
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
||||||
if (targetLevel <= currentLevel) {
|
if (targetLevel <= currentLevel) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1186,13 +1183,12 @@ public abstract class StarLightEngine {
|
|||||||
} else {
|
} else {
|
||||||
// we actually need to worry about our state here
|
// we actually need to worry about our state here
|
||||||
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
|
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
|
||||||
this.mutablePos2.set(posX, posY, posZ);
|
|
||||||
for (final AxisDirection propagate : checkDirections) {
|
for (final AxisDirection propagate : checkDirections) {
|
||||||
final int offX = posX + propagate.x;
|
final int offX = posX + propagate.x;
|
||||||
final int offY = posY + propagate.y;
|
final int offY = posY + propagate.y;
|
||||||
final int offZ = posZ + propagate.z;
|
final int offZ = posZ + propagate.z;
|
||||||
|
|
||||||
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty();
|
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
|
||||||
|
|
||||||
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1212,10 +1208,9 @@ public abstract class StarLightEngine {
|
|||||||
if (blockState == null) {
|
if (blockState == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.mutablePos1.set(offX, offY, offZ);
|
|
||||||
long flags = 0;
|
long flags = 0;
|
||||||
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
||||||
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
||||||
|
|
||||||
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1223,7 +1218,7 @@ public abstract class StarLightEngine {
|
|||||||
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
final int opacity = blockState.getLightBlock();
|
||||||
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
||||||
if (targetLevel <= currentLevel) {
|
if (targetLevel <= currentLevel) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1296,10 +1291,10 @@ public abstract class StarLightEngine {
|
|||||||
if (blockState == null) {
|
if (blockState == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.mutablePos1.set(offX, offY, offZ);
|
this.lightEmissionPos.set(offX, offY, offZ);
|
||||||
long flags = 0;
|
long flags = 0;
|
||||||
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
||||||
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
||||||
|
|
||||||
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1307,7 +1302,7 @@ public abstract class StarLightEngine {
|
|||||||
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
final int opacity = blockState.getLightBlock();
|
||||||
final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
||||||
if (lightLevel > targetLevel) {
|
if (lightLevel > targetLevel) {
|
||||||
// it looks like another source propagated here, so re-propagate it
|
// it looks like another source propagated here, so re-propagate it
|
||||||
@@ -1321,7 +1316,7 @@ public abstract class StarLightEngine {
|
|||||||
| (FLAG_RECHECK_LEVEL | flags);
|
| (FLAG_RECHECK_LEVEL | flags);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.mutablePos1)) & emittedMask;
|
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
|
||||||
if (emittedLight != 0) {
|
if (emittedLight != 0) {
|
||||||
// re-propagate source
|
// re-propagate source
|
||||||
// note: do not set recheck level, or else the propagation will fail
|
// note: do not set recheck level, or else the propagation will fail
|
||||||
@@ -1353,7 +1348,6 @@ public abstract class StarLightEngine {
|
|||||||
} else {
|
} else {
|
||||||
// we actually need to worry about our state here
|
// we actually need to worry about our state here
|
||||||
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
|
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
|
||||||
this.mutablePos2.set(posX, posY, posZ);
|
|
||||||
for (final AxisDirection propagate : checkDirections) {
|
for (final AxisDirection propagate : checkDirections) {
|
||||||
final int offX = posX + propagate.x;
|
final int offX = posX + propagate.x;
|
||||||
final int offY = posY + propagate.y;
|
final int offY = posY + propagate.y;
|
||||||
@@ -1362,7 +1356,7 @@ public abstract class StarLightEngine {
|
|||||||
final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset;
|
final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset;
|
||||||
final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8);
|
final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8);
|
||||||
|
|
||||||
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty();
|
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
|
||||||
|
|
||||||
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1380,10 +1374,10 @@ public abstract class StarLightEngine {
|
|||||||
if (blockState == null) {
|
if (blockState == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.mutablePos1.set(offX, offY, offZ);
|
this.lightEmissionPos.set(offX, offY, offZ);
|
||||||
long flags = 0;
|
long flags = 0;
|
||||||
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
||||||
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
||||||
|
|
||||||
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1391,7 +1385,7 @@ public abstract class StarLightEngine {
|
|||||||
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
final int opacity = blockState.getLightBlock();
|
||||||
final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
||||||
if (lightLevel > targetLevel) {
|
if (lightLevel > targetLevel) {
|
||||||
// it looks like another source propagated here, so re-propagate it
|
// it looks like another source propagated here, so re-propagate it
|
||||||
@@ -1405,7 +1399,7 @@ public abstract class StarLightEngine {
|
|||||||
| (FLAG_RECHECK_LEVEL | flags);
|
| (FLAG_RECHECK_LEVEL | flags);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.mutablePos1)) & emittedMask;
|
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
|
||||||
if (emittedLight != 0) {
|
if (emittedLight != 0) {
|
||||||
// re-propagate source
|
// re-propagate source
|
||||||
// note: do not set recheck level, or else the propagation will fail
|
// note: do not set recheck level, or else the propagation will fail
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package ca.spottedleaf.moonrise.patches.starlight.storage;
|
||||||
|
|
||||||
|
public interface StarlightSectionData {
|
||||||
|
|
||||||
|
public int starlight$getBlockLightState();
|
||||||
|
|
||||||
|
public void starlight$setBlockLightState(final int state);
|
||||||
|
|
||||||
|
public int starlight$getSkyLightState();
|
||||||
|
|
||||||
|
public void starlight$setSkyLightState(final int state);
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user