Compare commits
99 Commits
v0.1.0-bet
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7510eda16 | ||
|
|
d9442c1492 | ||
|
|
93eb2786f2 | ||
|
|
1bef6823c5 | ||
|
|
01152eec95 | ||
|
|
1e39f5370a | ||
|
|
ea50ba38ea | ||
|
|
c00b9fcd7b | ||
|
|
bad5cae4d8 | ||
|
|
e3b1502bb6 | ||
|
|
54fc964987 | ||
|
|
0cbc9aa1a1 | ||
|
|
19e2136ae1 | ||
|
|
126cc03747 | ||
|
|
ceb4936d9d | ||
|
|
3cb888e894 | ||
|
|
7bedc1a7de | ||
|
|
718f6e1369 | ||
|
|
da9ab708a6 | ||
|
|
f22335f0b6 | ||
|
|
a3f2328000 | ||
|
|
529b9a44bb | ||
|
|
1e9a6504a1 | ||
|
|
9c46dcbb94 | ||
|
|
9a1e04389a | ||
|
|
29084d8e3f | ||
|
|
41790ecf1a | ||
|
|
8af7bccdfd | ||
|
|
5f9b3571f8 | ||
|
|
9adfb2514d | ||
|
|
bf2cd1c571 | ||
|
|
dad9a5c2eb | ||
|
|
a3acd46ee1 | ||
|
|
3e8cb80336 | ||
|
|
5c3e713be7 | ||
|
|
284631c321 | ||
|
|
666c4cb1a3 | ||
|
|
19b523eecd | ||
|
|
4a748778dc | ||
|
|
a70073ae3e | ||
|
|
6724814c02 | ||
|
|
f32a08738e | ||
|
|
56e48ed069 | ||
|
|
9da99576a6 | ||
|
|
dfbe1bcf8b | ||
|
|
ae29196221 | ||
|
|
f1eb61cc51 | ||
|
|
58c933938f | ||
|
|
1dc3cb5f14 | ||
|
|
2acfc6a68e | ||
|
|
c22538c364 | ||
|
|
e244c60375 | ||
|
|
65253d5199 | ||
|
|
6e82b034b7 | ||
|
|
a7cd78e63b | ||
|
|
05ba7066d9 | ||
|
|
340ac4e8f5 | ||
|
|
a4770aca2b | ||
|
|
a8d4ce526b | ||
|
|
2acd5cc213 | ||
|
|
07dce0ffe6 | ||
|
|
c2e2f0b9f2 | ||
|
|
1374ea34ee | ||
|
|
fef4872a3a | ||
|
|
a9678c76eb | ||
|
|
bafa195546 | ||
|
|
04c6e6c01a | ||
|
|
7351872730 | ||
|
|
77b977ac17 | ||
|
|
f0d122f358 | ||
|
|
aa240eb16d | ||
|
|
f9c99d1e32 | ||
|
|
3fa0ff67a7 | ||
|
|
f7d9e5b422 | ||
|
|
5d1975154b | ||
|
|
d21fa9f48c | ||
|
|
1c150afe83 | ||
|
|
d9988c86f4 | ||
|
|
0958439e54 | ||
|
|
8d9d6488e8 | ||
|
|
8ba83bc9c2 | ||
|
|
0c60a6ac08 | ||
|
|
d404dbc6c5 | ||
|
|
8d456890f1 | ||
|
|
23eddfe918 | ||
|
|
55ff406372 | ||
|
|
3e25a2f4aa | ||
|
|
cfa80c4488 | ||
|
|
453b635ef2 | ||
|
|
e07e4fdcc4 | ||
|
|
81bb9701da | ||
|
|
d0a7f9af62 | ||
|
|
fe7bcfc56a | ||
|
|
44f8058b09 | ||
|
|
d1f8e81913 | ||
|
|
dcef1320b2 | ||
|
|
179b45cac5 | ||
|
|
89e9cd52e4 | ||
|
|
38c3f5fd80 |
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
|
||||
13
README.md
13
README.md
@@ -1,5 +1,10 @@
|
||||
Moonrise
|
||||
==
|
||||
[](https://modrinth.com/mod/moonrise-opt)
|
||||
[](https://www.curseforge.com/minecraft/mc-mods/moonrise)
|
||||
[](https://github.com/Tuinity/Moonrise/releases)
|
||||
[](LICENSE.md)
|
||||
|
||||
Fabric/NeoForge mod for optimising performance of the integrated (singleplayer/LAN) and dedicated server.
|
||||
|
||||
|
||||
@@ -7,20 +12,20 @@ Fabric/NeoForge mod for optimising performance of the integrated (singleplayer/L
|
||||
Moonrise aims to optimise the game *without changing Vanilla behavior*. If you find that there are changes to Vanilla behavior,
|
||||
please open an issue.
|
||||
|
||||
Moonrise ports several important [Paper](https://github.com/PaperMC/Paper/)
|
||||
Moonrise is an official port of several important [Paper](https://github.com/PaperMC/Paper/)
|
||||
patches. Listed below are notable patches:
|
||||
- [Starlight](https://github.com/PaperMC/Starlight/)
|
||||
- Chunk system rewrite
|
||||
- Collision optimisations
|
||||
- Entity tracker optimisations
|
||||
- Random ticking optimisations
|
||||
- [Starlight](https://github.com/PaperMC/Starlight/)
|
||||
|
||||
## Known Compatibility Issues
|
||||
| Mod | Status |
|
||||
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Lithium | <details><summary>✅ compatible</summary>Lithium optimises many of the same parts of the game as Moonrise, for example the chunk system. Moonrise will automatically disable conflicting parts of Lithium. This mechanism needs to be manually validated for each Moonrise and Lithium release.</details> |
|
||||
| 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> |
|
||||
| FerriteCore | <details><summary>✅ compatible</summary>FerriteCore optimises some of the same parts of the game as Moonrise. Moonrise will automatically disable conflicting parts of FerriteCore. This mechanism needs to be manually validated for each Moonrise and FerriteCore release.</details> |
|
||||
| C2ME | <details><summary>❌ incompatible</summary>C2ME is based around modifications to the chunk system, which Moonrise replaces wholesale. This makes them fundamentally incompatible.</details> |
|
||||
|
||||
## Configuration
|
||||
Moonrise provides documented configuration options for tuning the chunk system and enabling bugfixes in the config file `$mcdir$/config/moonrise.yml`.
|
||||
|
||||
34
build.gradle
34
build.gradle
@@ -98,7 +98,8 @@ subprojects {
|
||||
}
|
||||
loom.runs.all {
|
||||
ideConfigGenerated true
|
||||
// property "mixin.debug", "true"
|
||||
property "mixin.debug", "true"
|
||||
property "Moonrise.MaxViewDistance", "128"
|
||||
}
|
||||
|
||||
plugins.apply("me.modmuss50.mod-publish-plugin")
|
||||
@@ -112,18 +113,45 @@ subprojects {
|
||||
}
|
||||
changelog = providers.environmentVariable("RELEASE_NOTES")
|
||||
|
||||
List<String> supportedMcVersions = rootProject.supported_minecraft_versions.split(',')
|
||||
|
||||
modrinth {
|
||||
projectId = "KOHu7RCS"
|
||||
accessToken = providers.environmentVariable("MODRINTH_TOKEN")
|
||||
minecraftVersions = [rootProject.minecraft_version]
|
||||
minecraftVersions = supportedMcVersions
|
||||
}
|
||||
|
||||
curseforge {
|
||||
projectId = "1096335"
|
||||
accessToken = providers.environmentVariable("CURSEFORGE_TOKEN")
|
||||
minecraftVersions = [rootProject.minecraft_version]
|
||||
minecraftVersions = supportedMcVersions
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a run with lithium for compatibility testing
|
||||
sourceSets.create("lithium")
|
||||
configurations.create("lithium")
|
||||
loom {
|
||||
createRemapConfigurations(sourceSets.lithium)
|
||||
runs {
|
||||
register("lithiumClient") {
|
||||
client()
|
||||
property "mixin.debug", "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||
}
|
||||
dependencies {
|
||||
String coordinates = "maven.modrinth:lithium:"
|
||||
if (getProject().name == "Moonrise-NeoForge") {
|
||||
coordinates += rootProject.neo_lithium_version
|
||||
} else {
|
||||
coordinates += rootProject.fabric_lithium_version
|
||||
}
|
||||
modLithiumRuntimeOnly coordinates
|
||||
}
|
||||
}
|
||||
|
||||
loom.runs.all {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id("xyz.jpenilla.quiet-architectury-loom")
|
||||
id 'maven-publish'
|
||||
id 'io.github.goooler.shadow'
|
||||
id 'com.gradleup.shadow'
|
||||
}
|
||||
|
||||
configurations.create("libs")
|
||||
@@ -22,9 +22,10 @@ dependencies {
|
||||
|
||||
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||
modImplementation "com.terraformersmc:modmenu:11.0.1"
|
||||
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
|
||||
|
||||
modImplementation fabricApiLibs.command.api.v2
|
||||
modImplementation fabricApiLibs.lifecycle.events.v1
|
||||
include fabricApiLibs.command.api.v2
|
||||
include fabricApiLibs.base
|
||||
}
|
||||
@@ -59,26 +60,7 @@ publishMods {
|
||||
incompatible(
|
||||
"not-enough-crashes",
|
||||
"starlight",
|
||||
"c2me-fabric"
|
||||
"c2me"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a run with lithium for compatibility testing
|
||||
sourceSets.create("lithium")
|
||||
configurations.create("lithium")
|
||||
loom {
|
||||
createRemapConfigurations(sourceSets.lithium)
|
||||
runs {
|
||||
register("lithiumClient") {
|
||||
client()
|
||||
property "mixin.debug", "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||
}
|
||||
dependencies {
|
||||
modLithiumRuntimeOnly "maven.modrinth:lithium:${rootProject.lithium_version}"
|
||||
}
|
||||
|
||||
@@ -2,29 +2,38 @@ package ca.spottedleaf.moonrise.fabric;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import com.mojang.datafixers.DSL;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
|
||||
|
||||
@Override
|
||||
public String getBrand() {
|
||||
return "Moonrise";
|
||||
@@ -42,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) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCurrentlyLoadingChunk() {
|
||||
return false;
|
||||
@@ -69,7 +68,12 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
|
||||
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel)newChunk.getLevel(), newChunk);
|
||||
if (!(original instanceof ImposterProtoChunk)) {
|
||||
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,17 +82,19 @@ public final class FabricHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk) {
|
||||
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel)chunk.getLevel(), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
|
||||
|
||||
}
|
||||
|
||||
@@ -160,12 +166,12 @@ public final class FabricHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long configAutoSaveInterval() {
|
||||
public long configAutoSaveInterval(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int configMaxAutoSavePerTick() {
|
||||
public int configMaxAutoSavePerTick(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
||||
}
|
||||
|
||||
@@ -173,4 +179,47 @@ public final class FabricHooks implements PlatformHooks {
|
||||
public boolean configFixMC159283() {
|
||||
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceNoSave(final ChunkAccess chunk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||
final int fromVersion, final int toVersion) {
|
||||
return (CompoundTag)dataFixer.update(
|
||||
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
|
||||
).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainChunkLoadHook() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadEntity(final Entity entity) {
|
||||
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
|
||||
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
|
||||
return currentRange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package ca.spottedleaf.moonrise.fabric.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
abstract class EntityMixin {
|
||||
|
||||
@Shadow
|
||||
public abstract boolean touchingUnloadedChunk();
|
||||
|
||||
@Shadow
|
||||
public abstract AABB getBoundingBox();
|
||||
|
||||
@Shadow
|
||||
@Deprecated
|
||||
public abstract boolean isPushedByFluid();
|
||||
|
||||
@Shadow
|
||||
private Level level;
|
||||
|
||||
@Shadow
|
||||
@Deprecated
|
||||
protected Object2DoubleMap<TagKey<Fluid>> fluidHeight;
|
||||
|
||||
@Shadow
|
||||
public abstract Vec3 getDeltaMovement();
|
||||
|
||||
@Shadow
|
||||
public abstract void setDeltaMovement(final Vec3 arg);
|
||||
|
||||
/**
|
||||
* @reason Optimise the block reading in this function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, final double flowScale) {
|
||||
if (this.touchingUnloadedChunk()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||
|
||||
final Level world = this.level;
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
|
||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||
|
||||
final boolean isPushable = this.isPushedByFluid();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
Vec3 pushVector = Vec3.ZERO;
|
||||
double totalPushes = 0.0;
|
||||
double maxHeightDiff = 0.0;
|
||||
boolean inFluid = false;
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
|
||||
|
||||
// bound y
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
|
||||
|
||||
if (fluidState.isEmpty() || !fluidState.is(fluid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
|
||||
|
||||
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
|
||||
final double diff = height - boundingBox.minY;
|
||||
|
||||
if (diff < 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inFluid = true;
|
||||
maxHeightDiff = Math.max(maxHeightDiff, diff);
|
||||
|
||||
if (!isPushable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++totalPushes;
|
||||
|
||||
final Vec3 flow = fluidState.getFlow(world, mutablePos);
|
||||
|
||||
if (diff < 0.4) {
|
||||
pushVector = pushVector.add(flow.scale(diff));
|
||||
} else {
|
||||
pushVector = pushVector.add(flow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.fluidHeight.put(fluid, maxHeightDiff);
|
||||
|
||||
if (pushVector.lengthSqr() == 0.0) {
|
||||
return inFluid;
|
||||
}
|
||||
|
||||
// note: totalPushes != 0 as pushVector != 0
|
||||
pushVector = pushVector.scale(1.0 / totalPushes);
|
||||
final Vec3 currMovement = this.getDeltaMovement();
|
||||
|
||||
if (!((Entity)(Object)this instanceof Player)) {
|
||||
pushVector = pushVector.normalize();
|
||||
}
|
||||
|
||||
pushVector = pushVector.scale(flowScale);
|
||||
if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
|
||||
pushVector = pushVector.normalize().scale(0.0045000000000000005);
|
||||
}
|
||||
|
||||
this.setDeltaMovement(currMovement.add(pushVector));
|
||||
|
||||
// note: inFluid = true here as pushVector != 0
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,76 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "moonrise",
|
||||
"version": "${version}",
|
||||
"name": "Moonrise",
|
||||
"description": "Optimisation mod for the dedicated and integrated server.",
|
||||
"authors": [
|
||||
"Spottedleaf"
|
||||
],
|
||||
"contact": {
|
||||
"issues": "https://github.com/Tuinity/Moonrise/issues",
|
||||
"sources": "https://github.com/Tuinity/Moonrise",
|
||||
"discord": "https://discord.gg/tuinity",
|
||||
"homepage": "https://www.curseforge.com/minecraft/mc-mods/moonrise"
|
||||
},
|
||||
"breaks": {
|
||||
"notenoughcrashes": "*",
|
||||
"starlight": "*",
|
||||
"c2me": "*"
|
||||
},
|
||||
"license": "GPL-3.0-only",
|
||||
"icon": "assets/moonrise/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"modmenu": [
|
||||
"ca.spottedleaf.moonrise.fabric.MoonriseModMenuHook"
|
||||
"schemaVersion": 1,
|
||||
"id": "moonrise",
|
||||
"version": "${version}",
|
||||
"name": "Moonrise",
|
||||
"description": "Optimisation mod for the dedicated and integrated server.",
|
||||
"authors": [
|
||||
"Spottedleaf"
|
||||
],
|
||||
"client": [
|
||||
"ca.spottedleaf.moonrise.fabric.MoonriseFabricClient"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"moonrise.mixins.json",
|
||||
"moonrise-fabric.mixins.json"
|
||||
],
|
||||
"accessWidener": "moonrise.accesswidener",
|
||||
"depends": {
|
||||
"fabricloader": ">=${loader_version}",
|
||||
"minecraft": ">=1.21 <=1.21.1",
|
||||
"fabric-command-api-v2": "*"
|
||||
},
|
||||
"custom": {
|
||||
"lithium:options": {
|
||||
"mixin.collections.chunk_tickets": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.collections.entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.ai.poi": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.world.tick_scheduler": false,
|
||||
"mixin.alloc.chunk_ticking": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.entity.collisions.intersection": false,
|
||||
"mixin.entity.collisions.movement": false,
|
||||
"mixin.entity.collisions.unpushable_cramming": false,
|
||||
"mixin.chunk.entity_class_groups": false
|
||||
"contact": {
|
||||
"issues": "https://github.com/Tuinity/Moonrise/issues",
|
||||
"sources": "https://github.com/Tuinity/Moonrise",
|
||||
"discord": "https://discord.gg/tuinity",
|
||||
"homepage": "https://www.curseforge.com/minecraft/mc-mods/moonrise"
|
||||
},
|
||||
"breaks": {
|
||||
"notenoughcrashes": "*",
|
||||
"starlight": "*",
|
||||
"c2me": "*"
|
||||
},
|
||||
"license": "GPL-3.0-only",
|
||||
"icon": "assets/moonrise/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"modmenu": [
|
||||
"ca.spottedleaf.moonrise.fabric.MoonriseModMenuHook"
|
||||
],
|
||||
"client": [
|
||||
"ca.spottedleaf.moonrise.fabric.MoonriseFabricClient"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"moonrise.mixins.json",
|
||||
"moonrise-fabric.mixins.json"
|
||||
],
|
||||
"accessWidener": "moonrise.accesswidener",
|
||||
"depends": {
|
||||
"fabricloader": ">=${loader_version}",
|
||||
"minecraft": ">1.21.1 <1.21.4",
|
||||
"fabric-command-api-v2": "*"
|
||||
},
|
||||
"custom": {
|
||||
"lithium:options": {
|
||||
"mixin.ai.poi": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.alloc.entity_tracker": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.chunk.entity_class_groups": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.collections.chunk_tickets": false,
|
||||
"mixin.collections.entity_ticking": false,
|
||||
"mixin.entity.collisions.intersection": false,
|
||||
"mixin.entity.collisions.movement": false,
|
||||
"mixin.entity.collisions.unpushable_cramming": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.util.entity_movement_tracking": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.world.explosions.block_raycast": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.world.tick_scheduler": false
|
||||
},
|
||||
"ferritecore:disabled_options": [
|
||||
"replaceNeighborLookup",
|
||||
"replacePropertyMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"mixins": [
|
||||
"chunk_system.FabricDistanceManagerMixin",
|
||||
"chunk_system.FabricMinecraftServerMixin",
|
||||
"chunk_system.FabricServerLevelMixin"
|
||||
"chunk_system.FabricServerLevelMixin",
|
||||
"collisions.EntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"command.ClientSuggestionProviderMixin"
|
||||
|
||||
@@ -3,14 +3,19 @@ org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.daemon=false
|
||||
# Fabric Properties
|
||||
# check these on https://modmuss50.me/fabric.html
|
||||
minecraft_version=1.21.1
|
||||
loader_version=0.15.11
|
||||
neoforge_version=21.1.20
|
||||
minecraft_version=1.21.3
|
||||
loader_version=0.16.7
|
||||
supported_minecraft_versions=1.21.3
|
||||
neoforge_version=21.3.31-beta
|
||||
fabric_api_version=0.107.0+1.21.3
|
||||
snakeyaml_version=2.2
|
||||
concurrentutil_version=0.0.2-SNAPSHOT
|
||||
cloth_version=15.0.128
|
||||
lithium_version=mc1.21.1-0.13.0
|
||||
cloth_version=16.0.141
|
||||
modmenu_version=12.0.0-beta.1
|
||||
# version ids from modrinth
|
||||
fabric_lithium_version=QhCwdt4l
|
||||
neo_lithium_version=wDD955sb
|
||||
# Mod Properties
|
||||
mod_version=0.1.0-beta.1
|
||||
mod_version=0.2.0-beta.4
|
||||
maven_group=ca.spottedleaf.moonrise
|
||||
archives_base_name=moonrise
|
||||
|
||||
@@ -3,7 +3,7 @@ import net.fabricmc.loom.util.aw2at.Aw2At
|
||||
plugins {
|
||||
id("xyz.jpenilla.quiet-architectury-loom")
|
||||
id 'maven-publish'
|
||||
id 'io.github.goooler.shadow'
|
||||
id 'com.gradleup.shadow'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -53,17 +53,15 @@ publishMods {
|
||||
modrinth {
|
||||
incompatible(
|
||||
"notenoughcrashes",
|
||||
"starlight-forge",
|
||||
"canary",
|
||||
"radium"
|
||||
"starlight-neoforge",
|
||||
"canary"
|
||||
)
|
||||
}
|
||||
curseforge {
|
||||
incompatible(
|
||||
"not-enough-crashes-forge",
|
||||
"starlight-forge",
|
||||
"canary",
|
||||
"radium-reforged"
|
||||
"starlight-neoforge",
|
||||
"canary"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,13 @@ package ca.spottedleaf.moonrise.neoforge;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import com.mojang.datafixers.DSL;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -13,16 +17,16 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.neoforged.neoforge.common.CommonHooks;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
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
|
||||
public boolean hasCurrentlyLoadingChunk() {
|
||||
return true;
|
||||
@@ -88,10 +82,12 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
|
||||
final ChunkPos pos = holder.getPos();
|
||||
|
||||
EventHooks.fireChunkTicketLevelUpdated(
|
||||
world, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ),
|
||||
oldLevel, newLevel, holder.vanillaChunkHolder
|
||||
world, CoordinateUtils.getChunkKey(pos.x, pos.z),
|
||||
oldLevel, newLevel, holder
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,7 +97,7 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
|
||||
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, world, data));
|
||||
}
|
||||
|
||||
@@ -193,12 +189,12 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long configAutoSaveInterval() {
|
||||
public long configAutoSaveInterval(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int configMaxAutoSavePerTick() {
|
||||
public int configMaxAutoSavePerTick(final ServerLevel world) {
|
||||
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
|
||||
}
|
||||
|
||||
@@ -206,4 +202,47 @@ public final class NeoForgeHooks implements PlatformHooks {
|
||||
public boolean configFixMC159283() {
|
||||
return ConfigHolder.getConfig().bugFixes.fixMC159283;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceNoSave(final ChunkAccess chunk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||
final int fromVersion, final int toVersion) {
|
||||
return (CompoundTag)dataFixer.update(
|
||||
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
|
||||
).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainChunkLoadHook() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
|
||||
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, chunkData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadEntity(final Entity entity) {
|
||||
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
|
||||
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
|
||||
return currentRange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package ca.spottedleaf.moonrise.neoforge.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.server.level.ChunkLevel;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
@@ -15,13 +15,13 @@ import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(DistanceManager.class)
|
||||
abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
|
||||
@Unique
|
||||
private final ConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket<?>>> mtSafeForcedTickets = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
@Shadow
|
||||
@Final
|
||||
private Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> forcedTickets;
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system
|
||||
@@ -34,7 +34,7 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
if (forceTicks) {
|
||||
final Ticket<T> forceTicket = new Ticket<>(type, level, identifier, forceTicks);
|
||||
|
||||
this.mtSafeForcedTickets.compute(pos.toLong(), (final long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
this.forcedTickets.compute(pos.toLong(), (final Long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
final SortedArraySet<Ticket<?>> ret;
|
||||
if (valueInMap != null) {
|
||||
ret = valueInMap;
|
||||
@@ -42,7 +42,11 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
ret = SortedArraySet.create(4);
|
||||
}
|
||||
|
||||
ret.add(forceTicket);
|
||||
if (ret.add(forceTicket)) {
|
||||
((ChunkTickServerLevel)NeoForgeDistanceManagerMixin.this.moonrise$getChunkMap().level).moonrise$addPlayerTickingRequest(
|
||||
CoordinateUtils.getChunkX(keyInMap.longValue()), CoordinateUtils.getChunkZ(keyInMap.longValue())
|
||||
);
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
@@ -60,8 +64,12 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
if (forceTicks) {
|
||||
final Ticket<T> forceTicket = new Ticket<>(type, level, identifier, forceTicks);
|
||||
|
||||
this.mtSafeForcedTickets.computeIfPresent(pos.toLong(), (final long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
valueInMap.remove(forceTicket);
|
||||
this.forcedTickets.computeIfPresent(pos.toLong(), (final Long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
|
||||
if (valueInMap.remove(forceTicket)) {
|
||||
((ChunkTickServerLevel)NeoForgeDistanceManagerMixin.this.moonrise$getChunkMap().level).moonrise$removePlayerTickingRequest(
|
||||
CoordinateUtils.getChunkX(keyInMap.longValue()), CoordinateUtils.getChunkZ(keyInMap.longValue())
|
||||
);
|
||||
}
|
||||
|
||||
return valueInMap.isEmpty() ? null : valueInMap;
|
||||
});
|
||||
@@ -69,11 +77,11 @@ abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManage
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Make this API thread-safe
|
||||
* @reason Only use containsKey, as we fix the leak with this impl
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean shouldForceTicks(final long chunkPos) {
|
||||
return this.mtSafeForcedTickets.containsKey(chunkPos);
|
||||
return this.forcedTickets.containsKey(chunkPos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
package ca.spottedleaf.moonrise.neoforge.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.neoforge.patches.collisions.FluidPushCalculation;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.neoforged.neoforge.common.extensions.IEntityExtension;
|
||||
import net.neoforged.neoforge.fluids.FluidType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import java.util.Iterator;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
abstract class EntityMixin implements IEntityExtension {
|
||||
|
||||
@Shadow
|
||||
public abstract boolean touchingUnloadedChunk();
|
||||
|
||||
@Shadow
|
||||
public abstract AABB getBoundingBox();
|
||||
|
||||
@Shadow
|
||||
private Level level;
|
||||
|
||||
@Shadow
|
||||
protected abstract void setFluidTypeHeight(final FluidType type, final double height);
|
||||
|
||||
@Shadow
|
||||
public abstract Vec3 getDeltaMovement();
|
||||
|
||||
@Shadow
|
||||
public abstract void setDeltaMovement(final Vec3 arg);
|
||||
|
||||
/**
|
||||
* @reason Optimise the block reading in this function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void updateFluidHeightAndDoFluidPushing() {
|
||||
if (this.touchingUnloadedChunk()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||
|
||||
final Level world = this.level;
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
// note: bounds are exclusive in Vanilla, so we subtract 1
|
||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
final Reference2ReferenceArrayMap<FluidType, FluidPushCalculation> calculations = new Reference2ReferenceArrayMap<>();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
|
||||
|
||||
// bound y
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
|
||||
|
||||
if (fluidState.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
|
||||
|
||||
final FluidType type = fluidState.getFluidType();
|
||||
|
||||
// note: assume fluidState.isEmpty() == type.isAir()
|
||||
|
||||
final FluidPushCalculation calculation = calculations.computeIfAbsent(type, (final FluidType key) -> {
|
||||
return new FluidPushCalculation();
|
||||
});
|
||||
|
||||
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
|
||||
final double diff = height - boundingBox.minY;
|
||||
|
||||
if (diff < 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
calculation.maxHeightDiff = Math.max(calculation.maxHeightDiff, diff);
|
||||
|
||||
if (calculation.isPushed == Boolean.FALSE) {
|
||||
continue;
|
||||
} else if (calculation.isPushed == null) {
|
||||
final boolean isPushed = this.isPushedByFluid(type);
|
||||
calculation.isPushed = Boolean.valueOf(isPushed);
|
||||
if (!isPushed) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++calculation.totalPushes;
|
||||
|
||||
final Vec3 flow = fluidState.getFlow(world, mutablePos);
|
||||
|
||||
if (diff < 0.4) {
|
||||
calculation.pushVector = calculation.pushVector.add(flow.scale(diff));
|
||||
} else {
|
||||
calculation.pushVector = calculation.pushVector.add(flow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (calculations.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Iterator<Reference2ReferenceMap.Entry<FluidType, FluidPushCalculation>> iterator = calculations.reference2ReferenceEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Reference2ReferenceMap.Entry<FluidType, FluidPushCalculation> entry = iterator.next();
|
||||
final FluidType type = entry.getKey();
|
||||
final FluidPushCalculation calculation = entry.getValue();
|
||||
|
||||
this.setFluidTypeHeight(type, calculation.maxHeightDiff);
|
||||
|
||||
Vec3 pushVector = calculation.pushVector;
|
||||
|
||||
if (pushVector.lengthSqr() == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// note: totalPushes != 0 as pushVector != 0
|
||||
pushVector = pushVector.scale(1.0 / calculation.totalPushes);
|
||||
final Vec3 currMovement = this.getDeltaMovement();
|
||||
|
||||
if (!((Entity)(Object)this instanceof Player)) {
|
||||
pushVector = pushVector.normalize();
|
||||
}
|
||||
|
||||
pushVector = pushVector.scale(this.getFluidMotionScale(type));
|
||||
if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
|
||||
pushVector = pushVector.normalize().scale(0.0045000000000000005);
|
||||
}
|
||||
|
||||
this.setDeltaMovement(currMovement.add(pushVector));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ca.spottedleaf.moonrise.neoforge.patches.collisions;
|
||||
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class FluidPushCalculation {
|
||||
|
||||
public Vec3 pushVector = Vec3.ZERO;
|
||||
public double totalPushes = 0.0;
|
||||
public double maxHeightDiff = 0.0;
|
||||
public Boolean isPushed = null;
|
||||
|
||||
}
|
||||
@@ -13,6 +13,10 @@ displayURL = "https://github.com/Tuinity/Moonrise"
|
||||
authors = "Spottedleaf"
|
||||
description = "Optimisation mod for the dedicated and integrated server."
|
||||
displayTest = "IGNORE_ALL_VERSION"
|
||||
"ferritecore:disabled_options" = [
|
||||
"replaceNeighborLookup",
|
||||
"replacePropertyMap"
|
||||
]
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "neoforge"
|
||||
@@ -24,7 +28,7 @@ side = "BOTH"
|
||||
[[dependencies.moonrise]]
|
||||
modId = "minecraft"
|
||||
type = "required"
|
||||
versionRange = "[1.21,1.21.2)"
|
||||
versionRange = "(1.21.1,1.21.4)"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
||||
|
||||
@@ -36,20 +40,11 @@ type = "incompatible"
|
||||
modId = "starlight"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "lithium"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
# unofficial lithium port
|
||||
modId = "canary"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
# unofficial lithium port
|
||||
modId = "radium"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "c2me"
|
||||
type = "incompatible"
|
||||
@@ -59,3 +54,31 @@ config = "moonrise.mixins.json"
|
||||
|
||||
[[mixins]]
|
||||
config = "moonrise-neoforge.mixins.json"
|
||||
|
||||
["lithium:options"]
|
||||
"mixin.ai.poi" = false
|
||||
"mixin.alloc.deep_passengers" = false
|
||||
"mixin.alloc.entity_tracker" = false
|
||||
"mixin.block.flatten_states" = false
|
||||
"mixin.chunk.entity_class_groups" = false
|
||||
"mixin.chunk.no_validation" = false
|
||||
"mixin.collections.chunk_tickets" = false
|
||||
"mixin.collections.entity_ticking" = false
|
||||
"mixin.entity.collisions.intersection" = false
|
||||
"mixin.entity.collisions.movement" = false
|
||||
"mixin.entity.collisions.unpushable_cramming" = false
|
||||
"mixin.entity.replace_entitytype_predicates" = false
|
||||
"mixin.math.fast_blockpos" = false
|
||||
"mixin.math.fast_util" = false
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space" = false
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets" = false
|
||||
"mixin.shapes.blockstate_cache" = false
|
||||
"mixin.shapes.optimized_matching" = false
|
||||
"mixin.shapes.specialized_shapes" = false
|
||||
"mixin.util.block_tracking" = false
|
||||
"mixin.util.entity_movement_tracking" = false
|
||||
"mixin.world.block_entity_ticking" = false
|
||||
"mixin.world.chunk_access" = false
|
||||
"mixin.world.explosions.block_raycast" = false
|
||||
"mixin.world.temperature_cache" = false
|
||||
"mixin.world.tick_scheduler" = false
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"mixins": [
|
||||
"chunk_system.NeoForgeDistanceManagerMixin",
|
||||
"chunk_system.NeoForgeMinecraftServerMixin",
|
||||
"chunk_system.NeoForgeServerLevelMixin"
|
||||
"chunk_system.NeoForgeServerLevelMixin",
|
||||
"collisions.EntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"command.ClientCommandSourceStackMixin"
|
||||
|
||||
@@ -24,8 +24,8 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.294" apply false
|
||||
id 'io.github.goooler.shadow' version '8.1.8' apply false
|
||||
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.300" apply false
|
||||
id 'com.gradleup.shadow' version '8.3.0' apply false
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
@@ -37,7 +37,7 @@ dependencyResolutionManagement {
|
||||
}
|
||||
versionCatalogs {
|
||||
create("fabricApiLibs") {
|
||||
from("net.fabricmc.fabric-api:fabric-api-catalog:0.102.0+1.21.1")
|
||||
from("net.fabricmc.fabric-api:fabric-api-catalog:${fabric_api_version}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
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.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.datafix.DataFixTypes;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Predicate;
|
||||
@@ -33,10 +35,6 @@ public interface PlatformHooks {
|
||||
|
||||
public Predicate<BlockState> maybeHasLightEmission();
|
||||
|
||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
|
||||
|
||||
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original);
|
||||
|
||||
public boolean hasCurrentlyLoadingChunk();
|
||||
|
||||
public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
|
||||
@@ -47,11 +45,11 @@ public interface PlatformHooks {
|
||||
|
||||
public boolean allowAsyncTicketUpdates();
|
||||
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
|
||||
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel);
|
||||
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk);
|
||||
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data);
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
|
||||
|
||||
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
|
||||
|
||||
@@ -82,12 +80,30 @@ public interface PlatformHooks {
|
||||
|
||||
public int configPlayerMaxConcurrentGens();
|
||||
|
||||
public long configAutoSaveInterval();
|
||||
public long configAutoSaveInterval(final ServerLevel world);
|
||||
|
||||
public int configMaxAutoSavePerTick();
|
||||
public int configMaxAutoSavePerTick(final ServerLevel world);
|
||||
|
||||
public boolean configFixMC159283();
|
||||
|
||||
// support for CB chunk mustNotSave
|
||||
public boolean forceNoSave(final ChunkAccess chunk);
|
||||
|
||||
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||
final int fromVersion, final int toVersion);
|
||||
|
||||
public boolean hasMainChunkLoadHook();
|
||||
|
||||
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData);
|
||||
|
||||
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities);
|
||||
|
||||
public void unloadEntity(final Entity entity);
|
||||
|
||||
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk);
|
||||
|
||||
public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
|
||||
|
||||
public static final class Holder {
|
||||
private Holder() {
|
||||
}
|
||||
|
||||
@@ -19,6 +19,13 @@ public final class IntList {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
public void setMinCapacity(final int len) {
|
||||
final int[] byIndex = this.byIndex;
|
||||
if (byIndex.length < len) {
|
||||
this.byIndex = Arrays.copyOf(byIndex, len);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRaw(final int index) {
|
||||
return this.byIndex[index];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package ca.spottedleaf.moonrise.common.list;
|
||||
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class ShortList {
|
||||
|
||||
private final Short2ShortOpenHashMap map = new Short2ShortOpenHashMap();
|
||||
{
|
||||
this.map.defaultReturnValue(Short.MIN_VALUE);
|
||||
}
|
||||
|
||||
private static final short[] EMPTY_LIST = new short[0];
|
||||
|
||||
private short[] byIndex = EMPTY_LIST;
|
||||
private short count;
|
||||
|
||||
public int size() {
|
||||
return (int)this.count;
|
||||
}
|
||||
|
||||
public short getRaw(final int index) {
|
||||
return this.byIndex[index];
|
||||
}
|
||||
|
||||
public void setMinCapacity(final int len) {
|
||||
final short[] byIndex = this.byIndex;
|
||||
if (byIndex.length < len) {
|
||||
this.byIndex = Arrays.copyOf(byIndex, len);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(final short value) {
|
||||
final int count = (int)this.count;
|
||||
final short currIndex = this.map.putIfAbsent(value, (short)count);
|
||||
|
||||
if (currIndex != Short.MIN_VALUE) {
|
||||
return false; // already in this list
|
||||
}
|
||||
|
||||
short[] list = this.byIndex;
|
||||
|
||||
if (list.length == count) {
|
||||
// resize required
|
||||
list = this.byIndex = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
|
||||
}
|
||||
|
||||
list[count] = value;
|
||||
this.count = (short)(count + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean remove(final short value) {
|
||||
final short index = this.map.remove(value);
|
||||
if (index == Short.MIN_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// move the entry at the end to this index
|
||||
final short endIndex = --this.count;
|
||||
final short end = this.byIndex[endIndex];
|
||||
if (index != endIndex) {
|
||||
// not empty after this call
|
||||
this.map.put(end, index);
|
||||
}
|
||||
this.byIndex[(int)index] = end;
|
||||
this.byIndex[(int)endIndex] = (short)0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.count = (short)0;
|
||||
this.map.clear();
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,17 @@ import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
|
||||
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class NearbyPlayers {
|
||||
|
||||
@@ -20,7 +24,27 @@ public final class NearbyPlayers {
|
||||
GENERAL_REALLY_SMALL,
|
||||
TICK_VIEW_DISTANCE,
|
||||
VIEW_DISTANCE,
|
||||
SPAWN_RANGE, // Moonrise - chunk tick iteration
|
||||
// Moonrise start - chunk tick iteration
|
||||
SPAWN_RANGE {
|
||||
@Override
|
||||
void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
((ChunkTickServerLevel)world).moonrise$addPlayerTickingRequest(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
((ChunkTickServerLevel)world).moonrise$removePlayerTickingRequest(chunkX, chunkZ);
|
||||
}
|
||||
};
|
||||
// Moonrise end - chunk tick iteration
|
||||
|
||||
void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
|
||||
}
|
||||
|
||||
void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static final NearbyMapType[] MAP_TYPES = NearbyMapType.values();
|
||||
@@ -37,6 +61,12 @@ public final class NearbyPlayers {
|
||||
private final ServerLevel world;
|
||||
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
|
||||
private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
|
||||
private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
|
||||
{
|
||||
for (int i = 0; i < this.directByChunk.length; ++i) {
|
||||
this.directByChunk[i] = new Long2ReferenceOpenHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public NearbyPlayers(final ServerLevel world) {
|
||||
this.world = world;
|
||||
@@ -70,6 +100,16 @@ public final class NearbyPlayers {
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (this.players.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final ServerPlayer player : new ArrayList<>(this.players.keySet())) {
|
||||
this.removePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void tickPlayer(final ServerPlayer player) {
|
||||
final TrackedPlayer[] players = this.players.get(player);
|
||||
if (players == null) {
|
||||
@@ -94,38 +134,41 @@ public final class NearbyPlayers {
|
||||
return this.byChunk.get(CoordinateUtils.getChunkKey(pos));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
|
||||
public TrackedChunk getChunk(final int chunkX, final int chunkZ) {
|
||||
return this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayers(final ChunkPos pos, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
public ReferenceList<ServerPlayer> getPlayersByBlock(final int blockX, final int blockZ, final NearbyMapType type) {
|
||||
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
|
||||
|
||||
return chunk == null ? null : chunk.players[type.ordinal()];
|
||||
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
|
||||
}
|
||||
|
||||
public static final class TrackedChunk {
|
||||
|
||||
private static final ServerPlayer[] EMPTY_PLAYERS_ARRAY = new ServerPlayer[0];
|
||||
|
||||
private final long chunkKey;
|
||||
private final NearbyPlayers nearbyPlayers;
|
||||
private final ReferenceList<ServerPlayer>[] players = new ReferenceList[TOTAL_MAP_TYPES];
|
||||
private int nonEmptyLists;
|
||||
private long updateCount;
|
||||
|
||||
public TrackedChunk(final long chunkKey, final NearbyPlayers nearbyPlayers) {
|
||||
this.chunkKey = chunkKey;
|
||||
this.nearbyPlayers = nearbyPlayers;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.nonEmptyLists == 0;
|
||||
}
|
||||
@@ -145,7 +188,10 @@ public final class NearbyPlayers {
|
||||
final ReferenceList<ServerPlayer> list = this.players[idx];
|
||||
if (list == null) {
|
||||
++this.nonEmptyLists;
|
||||
(this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY)).add(player);
|
||||
final ReferenceList<ServerPlayer> players = (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY));
|
||||
this.nearbyPlayers.directByChunk[idx].put(this.chunkKey, players);
|
||||
players.add(player);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,6 +215,7 @@ public final class NearbyPlayers {
|
||||
|
||||
if (list.size() == 0) {
|
||||
this.players[idx] = null;
|
||||
this.nearbyPlayers.directByChunk[idx].remove(this.chunkKey);
|
||||
--this.nonEmptyLists;
|
||||
}
|
||||
}
|
||||
@@ -187,9 +234,19 @@ public final class NearbyPlayers {
|
||||
protected void addCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) {
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
|
||||
NearbyPlayers.this.byChunk.computeIfAbsent(chunkKey, (final long keyInMap) -> {
|
||||
return new TrackedChunk();
|
||||
}).addPlayer(parameter, this.type);
|
||||
final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey);
|
||||
final NearbyMapType type = this.type;
|
||||
if (chunk != null) {
|
||||
chunk.addPlayer(parameter, type);
|
||||
type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
|
||||
} else {
|
||||
final TrackedChunk created = new TrackedChunk(chunkKey, NearbyPlayers.this);
|
||||
NearbyPlayers.this.byChunk.put(chunkKey, created);
|
||||
created.addPlayer(parameter, type);
|
||||
type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
|
||||
|
||||
((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$requestChunkData(chunkKey).nearbyPlayers = created;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,10 +258,16 @@ public final class NearbyPlayers {
|
||||
throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey));
|
||||
}
|
||||
|
||||
chunk.removePlayer(parameter, this.type);
|
||||
final NearbyMapType type = this.type;
|
||||
chunk.removePlayer(parameter, type);
|
||||
type.removeFrom(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
|
||||
|
||||
if (chunk.isEmpty()) {
|
||||
NearbyPlayers.this.byChunk.remove(chunkKey);
|
||||
final ChunkData chunkData = ((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$releaseChunkData(chunkKey);
|
||||
if (chunkData != null) {
|
||||
chunkData.nearbyPlayers = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.common.misc;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.util.IntPairUtil;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
||||
|
||||
@@ -14,6 +15,10 @@ public final class PositionCountingAreaMap<T> {
|
||||
return this.counters.keySet();
|
||||
}
|
||||
|
||||
public LongSet getPositions() {
|
||||
return this.positions.keySet();
|
||||
}
|
||||
|
||||
public int getTotalPositions() {
|
||||
return this.positions.size();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
@@ -117,16 +118,18 @@ public final class ChunkSystem {
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
||||
chunk.postProcessGeneration();
|
||||
chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
|
||||
}
|
||||
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
|
||||
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
|
||||
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$markChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$removeChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
|
||||
import ca.spottedleaf.moonrise.common.config.config.YamlConfig;
|
||||
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
|
||||
@@ -13,7 +14,7 @@ public final class ConfigHolder {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigHolder.class);
|
||||
|
||||
private static final File CONFIG_FILE = new File(System.getProperty("Moonrise.ConfigFile", "config/moonrise.yml"));
|
||||
private static final File CONFIG_FILE = new File(System.getProperty(PlatformHooks.get().getBrand() + ".ConfigFile", "config/moonrise.yml"));
|
||||
private static final TypeAdapterRegistry CONFIG_ADAPTERS = new TypeAdapterRegistry();
|
||||
private static final YamlConfig<MoonriseConfig> CONFIG;
|
||||
static {
|
||||
@@ -25,7 +26,7 @@ public final class ConfigHolder {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
private static final String CONFIG_HEADER = """
|
||||
private static final String CONFIG_HEADER = String.format("""
|
||||
This is the configuration file for Moonrise.
|
||||
|
||||
Each configuration option is prefixed with a comment to explain what it does. Additional changes to this file
|
||||
@@ -33,9 +34,10 @@ public final class ConfigHolder {
|
||||
|
||||
Below are the Moonrise startup flags. Note that startup flags must be placed in the JVM arguments, not
|
||||
program arguments.
|
||||
-DMoonrise.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
|
||||
-DMoonrise.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
|
||||
""";
|
||||
-D%1$s.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
|
||||
-D%1$s.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
|
||||
-D%1$s.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
|
||||
""", PlatformHooks.get().getBrand());
|
||||
|
||||
static {
|
||||
reloadConfig();
|
||||
|
||||
@@ -3,8 +3,12 @@ package ca.spottedleaf.moonrise.common.util;
|
||||
public final class MixinWorkarounds {
|
||||
|
||||
// mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
|
||||
// https://github.com/FabricMC/Mixin/pull/147
|
||||
public static long[] clone(final long[] values) {
|
||||
return values.clone();
|
||||
}
|
||||
|
||||
public static byte[] clone(final byte[] values) {
|
||||
return values.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public final class MoonriseCommon {
|
||||
@Override
|
||||
public void accept(Thread thread) {
|
||||
thread.setDaemon(true);
|
||||
thread.setName("Moonrise Common Worker #" + this.idGenerator.getAndIncrement());
|
||||
thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
|
||||
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(final Thread thread, final Throwable throwable) {
|
||||
@@ -44,7 +44,7 @@ public final class MoonriseCommon {
|
||||
} else {
|
||||
defaultWorkerThreads = defaultWorkerThreads / 2;
|
||||
}
|
||||
defaultWorkerThreads = Integer.getInteger("Moonrise.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||
|
||||
int workerThreads = configWorkerThreads;
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
|
||||
public final class MoonriseConstants {
|
||||
|
||||
public static final int MAX_VIEW_DISTANCE = 32;
|
||||
public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
|
||||
|
||||
private MoonriseConstants() {}
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ public final class SimpleRandom extends LegacyRandomSource {
|
||||
return (int)(seed >>> (BITS - Integer.SIZE));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int nextInt(final int bound) {
|
||||
if (bound <= 0) {
|
||||
|
||||
@@ -8,11 +8,19 @@ public final class WorldUtil {
|
||||
// min, max are inclusive
|
||||
|
||||
public static int getMaxSection(final LevelHeightAccessor world) {
|
||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
||||
return world.getMaxSectionY();
|
||||
}
|
||||
|
||||
public static int getMaxSection(final Level world) {
|
||||
return world.getMaxSectionY();
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -18,15 +19,15 @@ interface BitStorageMixin extends BlockCountingBitStorage {
|
||||
|
||||
// provide default impl in case mods implement this...
|
||||
@Override
|
||||
public default Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>();
|
||||
public default Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
final int size = this.getSize();
|
||||
for (int index = 0; index < size; ++index) {
|
||||
final int paletteIdx = this.get(index);
|
||||
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
||||
return new IntArrayList();
|
||||
}).add(index);
|
||||
return new ShortArrayList(64);
|
||||
}).add((short)index);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
@@ -48,26 +48,32 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
public abstract boolean maybeHas(Predicate<BlockState> predicate);
|
||||
|
||||
@Unique
|
||||
private static final IntArrayList FULL_LIST = new IntArrayList(16*16*16);
|
||||
private static final ShortArrayList FULL_LIST = new ShortArrayList(16*16*16);
|
||||
static {
|
||||
for (int i = 0; i < (16*16*16); ++i) {
|
||||
for (short i = 0; i < (16*16*16); ++i) {
|
||||
FULL_LIST.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private int specialCollidingBlocks;
|
||||
private boolean isClient;
|
||||
|
||||
@Unique
|
||||
private final IntList tickingBlocks = new IntList();
|
||||
private static final short CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS = (short)9999;
|
||||
|
||||
@Unique
|
||||
private short specialCollidingBlocks;
|
||||
|
||||
@Unique
|
||||
private final ShortList tickingBlocks = new ShortList();
|
||||
|
||||
@Override
|
||||
public final int moonrise$getSpecialCollidingBlocks() {
|
||||
return this.specialCollidingBlocks;
|
||||
public final boolean moonrise$hasSpecialCollidingBlocks() {
|
||||
return this.specialCollidingBlocks != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IntList moonrise$getTickingBlockList() {
|
||||
public final ShortList moonrise$getTickingBlockList() {
|
||||
return this.tickingBlocks;
|
||||
}
|
||||
|
||||
@@ -86,20 +92,35 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
if (oldState == newState) {
|
||||
return;
|
||||
}
|
||||
if (CollisionUtil.isSpecialCollidingBlock(oldState)) {
|
||||
--this.specialCollidingBlocks;
|
||||
}
|
||||
if (CollisionUtil.isSpecialCollidingBlock(newState)) {
|
||||
++this.specialCollidingBlocks;
|
||||
|
||||
if (this.isClient) {
|
||||
if (CollisionUtil.isSpecialCollidingBlock(newState)) {
|
||||
this.specialCollidingBlocks = CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int position = x | (z << 4) | (y << (4+4));
|
||||
|
||||
if (oldState.isRandomlyTicking()) {
|
||||
this.tickingBlocks.remove(position);
|
||||
final boolean isSpecialOld = CollisionUtil.isSpecialCollidingBlock(oldState);
|
||||
final boolean isSpecialNew = CollisionUtil.isSpecialCollidingBlock(newState);
|
||||
if (isSpecialOld != isSpecialNew) {
|
||||
if (isSpecialOld) {
|
||||
--this.specialCollidingBlocks;
|
||||
} else {
|
||||
++this.specialCollidingBlocks;
|
||||
}
|
||||
}
|
||||
if (newState.isRandomlyTicking()) {
|
||||
this.tickingBlocks.add(position);
|
||||
|
||||
final boolean oldTicking = oldState.isRandomlyTicking();
|
||||
final boolean newTicking = newState.isRandomlyTicking();
|
||||
if (oldTicking != newTicking) {
|
||||
final ShortList tickingBlocks = this.tickingBlocks;
|
||||
final short position = (short)(x | (z << 4) | (y << (4+4)));
|
||||
|
||||
if (oldTicking) {
|
||||
tickingBlocks.remove(position);
|
||||
} else {
|
||||
tickingBlocks.add(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,11 +154,11 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
|
||||
if (this.maybeHas((final BlockState state) -> !state.isAir())) {
|
||||
final PalettedContainer.Data<BlockState> data = this.states.data;
|
||||
final Palette<BlockState> palette = data.palette;
|
||||
final Palette<BlockState> palette = data.palette();
|
||||
final int paletteSize = palette.getSize();
|
||||
final BitStorage storage = data.storage;
|
||||
final BitStorage storage = data.storage();
|
||||
|
||||
final Int2ObjectOpenHashMap<IntArrayList> counts;
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> counts;
|
||||
if (paletteSize == 1) {
|
||||
counts = new Int2ObjectOpenHashMap<>(1);
|
||||
counts.put(0, FULL_LIST);
|
||||
@@ -145,10 +166,10 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
counts = ((BlockCountingBitStorage)storage).moonrise$countEntries();
|
||||
}
|
||||
|
||||
for (final Iterator<Int2ObjectMap.Entry<IntArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Int2ObjectMap.Entry<IntArrayList> entry = iterator.next();
|
||||
for (final Iterator<Int2ObjectMap.Entry<ShortArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Int2ObjectMap.Entry<ShortArrayList> entry = iterator.next();
|
||||
final int paletteIdx = entry.getIntKey();
|
||||
final IntArrayList coordinates = entry.getValue();
|
||||
final ShortArrayList coordinates = entry.getValue();
|
||||
final int paletteCount = coordinates.size();
|
||||
|
||||
final BlockState state = palette.valueFor(paletteIdx);
|
||||
@@ -158,16 +179,21 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
}
|
||||
|
||||
if (CollisionUtil.isSpecialCollidingBlock(state)) {
|
||||
this.specialCollidingBlocks += paletteCount;
|
||||
this.specialCollidingBlocks += (short)paletteCount;
|
||||
}
|
||||
this.nonEmptyBlockCount += paletteCount;
|
||||
this.nonEmptyBlockCount += (short)paletteCount;
|
||||
if (state.isRandomlyTicking()) {
|
||||
this.tickingBlockCount += paletteCount;
|
||||
final int[] raw = coordinates.elements();
|
||||
this.tickingBlockCount += (short)paletteCount;
|
||||
final short[] raw = coordinates.elements();
|
||||
final int rawLen = raw.length;
|
||||
|
||||
Objects.checkFromToIndex(0, paletteCount, raw.length);
|
||||
final ShortList tickingBlocks = this.tickingBlocks;
|
||||
|
||||
tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16));
|
||||
|
||||
Objects.checkFromToIndex(0, paletteCount, rawLen);
|
||||
for (int i = 0; i < paletteCount; ++i) {
|
||||
this.tickingBlocks.add(raw[i]);
|
||||
tickingBlocks.add(raw[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +202,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
if (!fluid.isEmpty()) {
|
||||
//this.nonEmptyBlockCount += count; // fix vanilla bug: make non-empty block count correct
|
||||
if (fluid.isRandomlyTicking()) {
|
||||
this.tickingFluidCount += paletteCount;
|
||||
this.tickingFluidCount += (short)paletteCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +210,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Call recalcBlockCounts on the client, as the client does not invoke it when deserializing chunk sections.
|
||||
* @reason Set up special colliding blocks on the client, as it is too expensive to perform a full calculation
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
@@ -194,6 +220,8 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
)
|
||||
)
|
||||
private void callRecalcBlocksClient(final CallbackInfo ci) {
|
||||
this.recalcBlockCounts();
|
||||
this.isClient = true;
|
||||
// force has special colliding blocks to be true
|
||||
this.specialCollidingBlocks = this.nonEmptyBlockCount != (short)0 && this.maybeHas(CollisionUtil::isSpecialCollidingBlock) ? CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS : (short)0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -24,43 +25,66 @@ abstract class SimpleBitStorageMixin implements BitStorage, BlockCountingBitStor
|
||||
@Final
|
||||
private int bits;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private long mask;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private int size;
|
||||
|
||||
@Override
|
||||
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final int valuesPerLong = this.valuesPerLong;
|
||||
final int bits = this.bits;
|
||||
final long mask = this.mask;
|
||||
final long mask = (1L << bits) - 1L;
|
||||
final int size = this.size;
|
||||
|
||||
// we may be backed by global palette, so limit bits for init capacity
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(
|
||||
1 << Math.min(6, bits)
|
||||
);
|
||||
if (bits <= 6) {
|
||||
final ShortArrayList[] byId = new ShortArrayList[1 << bits];
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1 << bits);
|
||||
|
||||
int index = 0;
|
||||
int index = 0;
|
||||
|
||||
for (long value : this.data) {
|
||||
int li = 0;
|
||||
do {
|
||||
final int paletteIdx = (int)(value & mask);
|
||||
value >>= bits;
|
||||
for (long value : this.data) {
|
||||
int li = 0;
|
||||
do {
|
||||
final int paletteIdx = (int)(value & mask);
|
||||
value >>= bits;
|
||||
++li;
|
||||
|
||||
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
||||
return new IntArrayList();
|
||||
}).add(index);
|
||||
final ShortArrayList coords = byId[paletteIdx];
|
||||
if (coords != null) {
|
||||
coords.add((short)index++);
|
||||
continue;
|
||||
} else {
|
||||
final ShortArrayList newCoords = new ShortArrayList(64);
|
||||
byId[paletteIdx] = newCoords;
|
||||
newCoords.add((short)index++);
|
||||
ret.put(paletteIdx, newCoords);
|
||||
continue;
|
||||
}
|
||||
} while (li < valuesPerLong && index < size);
|
||||
}
|
||||
|
||||
++li;
|
||||
++index;
|
||||
} while (li < valuesPerLong && index < size);
|
||||
return ret;
|
||||
} else {
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(
|
||||
1 << 6
|
||||
);
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (long value : this.data) {
|
||||
int li = 0;
|
||||
do {
|
||||
final int paletteIdx = (int)(value & mask);
|
||||
value >>= bits;
|
||||
++li;
|
||||
|
||||
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
||||
return new ShortArrayList(64);
|
||||
}).add((short)index++);
|
||||
} while (li < valuesPerLong && index < size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.ZeroBitStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -17,17 +18,17 @@ abstract class ZeroBitStorageMixin implements BitStorage, BlockCountingBitStorag
|
||||
private int size;
|
||||
|
||||
@Override
|
||||
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final int size = this.size;
|
||||
|
||||
final int[] raw = new int[size];
|
||||
final short[] raw = new short[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
raw[i] = i;
|
||||
raw[i] = (short)i;
|
||||
}
|
||||
|
||||
final IntArrayList coordinates = IntArrayList.wrap(raw, size);
|
||||
final ShortArrayList coordinates = ShortArrayList.wrap(raw, size);
|
||||
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(1);
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1);
|
||||
ret.put(0, coordinates);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -17,6 +18,9 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
|
||||
super(string, class_);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static final Boolean[] BY_ID = new Boolean[]{ Boolean.FALSE, Boolean.TRUE };
|
||||
|
||||
@Override
|
||||
public final int moonrise$getIdFor(final Boolean value) {
|
||||
return value.booleanValue() ? 1 : 0;
|
||||
@@ -33,23 +37,6 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
this.moonrise$setById(new Boolean[]{ Boolean.FALSE, Boolean.TRUE });
|
||||
}
|
||||
|
||||
/**
|
||||
* This skips all ops after the identity comparison in the original code.
|
||||
*
|
||||
* @reason Properties are identity comparable
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "equals",
|
||||
constant = @Constant(
|
||||
classValue = BooleanProperty.class,
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private boolean skipFurtherComparison(final Object obj, final Operation<Boolean> orig) {
|
||||
return false;
|
||||
this.moonrise$setById(BY_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ import java.util.Collection;
|
||||
@Mixin(EnumProperty.class)
|
||||
abstract class EnumPropertyMixin<T extends Enum<T> & StringRepresentable> extends Property<T> implements PropertyAccess<T> {
|
||||
|
||||
@Shadow
|
||||
public abstract Collection<T> getPossibleValues();
|
||||
|
||||
@Unique
|
||||
private int[] idLookupTable;
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
|
||||
}
|
||||
|
||||
// remove values arrays
|
||||
this.values = null;
|
||||
for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
|
||||
final S value = entry.getValue();
|
||||
((StateHolderMixin<O, S>)(Object)(StateHolder<O, S>)value).values = null;
|
||||
@@ -126,8 +125,8 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public <T extends Comparable<T>> Optional<T> getOptionalValue(final Property<T> property) {
|
||||
return property == null ? Optional.empty() : Optional.ofNullable(this.optimisedTable.get(this.tableIndex, property));
|
||||
public <T extends Comparable<T>> T getNullableValue(Property<T> property) {
|
||||
return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -302,6 +302,10 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
)
|
||||
)
|
||||
private LevelChunk redirectLightUpdate(final ChunkHolder instance) {
|
||||
if (this.playersSentChunkTo.size() == 0) {
|
||||
// no players to sent to, so don't need to update anything
|
||||
return null;
|
||||
}
|
||||
return this.getChunkToSend();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,17 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.Supplier;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.StreamTagVisitor;
|
||||
import net.minecraft.server.level.ChunkGenerationTask;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkResult;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
|
||||
import net.minecraft.server.level.ChunkTaskDispatcher;
|
||||
import net.minecraft.server.level.ChunkTrackingView;
|
||||
import net.minecraft.server.level.GeneratingChunkMap;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
@@ -28,7 +31,6 @@ import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.StaticCache2D;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
@@ -78,13 +80,10 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
|
||||
|
||||
@Shadow
|
||||
private ChunkTaskPriorityQueueSorter queueSorter;
|
||||
private ChunkTaskDispatcher worldgenTaskDispatcher;
|
||||
|
||||
@Shadow
|
||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
|
||||
|
||||
@Shadow
|
||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
|
||||
private ChunkTaskDispatcher lightTaskDispatcher;
|
||||
|
||||
@Shadow
|
||||
private int serverViewDistance;
|
||||
@@ -98,6 +97,12 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
@Shadow
|
||||
private Queue<Runnable> unloadQueue;
|
||||
|
||||
@Shadow
|
||||
private LongSet chunksToEagerlySave;
|
||||
|
||||
@Shadow
|
||||
private AtomicInteger activeChunkWrites;
|
||||
|
||||
public ChunkMapMixin(RegionStorageInfo regionStorageInfo, Path path, DataFixer dataFixer, boolean bl) {
|
||||
super(regionStorageInfo, path, dataFixer, bl);
|
||||
}
|
||||
@@ -128,11 +133,12 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
this.updatingChunkMap = null;
|
||||
this.visibleChunkMap = null;
|
||||
this.pendingUnloads = null;
|
||||
this.queueSorter = null;
|
||||
this.worldgenMailbox = null;
|
||||
this.mainThreadMailbox = null;
|
||||
this.worldgenTaskDispatcher = null;
|
||||
this.lightTaskDispatcher = null;
|
||||
this.pendingGenerationTasks = null;
|
||||
this.unloadQueue = null;
|
||||
this.chunksToEagerlySave = null;
|
||||
this.activeChunkWrites = null;
|
||||
|
||||
// Dummy impl for mods that try to loadAsync directly
|
||||
this.worker = new IOWorker(
|
||||
@@ -152,7 +158,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
@Override
|
||||
public CompletableFuture<Optional<CompoundTag>> loadAsync(final ChunkPos chunkPos) {
|
||||
final CompletableFuture<Optional<CompoundTag>> future = new CompletableFuture<>();
|
||||
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (tag, throwable) -> {
|
||||
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (final CompoundTag tag, final Throwable throwable) -> {
|
||||
if (throwable != null) {
|
||||
future.completeExceptionally(throwable);
|
||||
} else {
|
||||
@@ -184,6 +190,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason This map is not needed, we maintain our own ordered set of chunks to autosave.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void setChunkUnsaved(final ChunkPos pos) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system hooks
|
||||
* @author Spottedleaf
|
||||
@@ -261,6 +276,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void onLevelChange(final ChunkPos chunkPos, final IntSupplier intSupplier, final int i, final IntConsumer intConsumer) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
@@ -309,6 +333,15 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.autoSave();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void saveChunksEagerly(final BooleanSupplier hasTime) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
@@ -403,7 +436,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void onChunkReadyToSend(final LevelChunk chunk) {
|
||||
public void onChunkReadyToSend(final ChunkHolder holder, final LevelChunk chunk) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -422,7 +455,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
* @see NewChunkHolder#save(boolean)
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder) {
|
||||
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder, final long time) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -505,6 +538,21 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "forEachSpawnCandidateChunk",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;"
|
||||
)
|
||||
)
|
||||
private <V> V redirectChunkHolderGet(final Long2ObjectLinkedOpenHashMap<V> instance, final long key) {
|
||||
return (V)this.getVisibleChunkIfPresent(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<CompoundTag>> read(final ChunkPos pos) {
|
||||
final CompletableFuture<Optional<CompoundTag>> ret = new CompletableFuture<>();
|
||||
@@ -524,10 +572,11 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> write(final ChunkPos pos, final CompoundTag tag) {
|
||||
public CompletableFuture<Void> write(final ChunkPos pos, final Supplier<CompoundTag> tag) {
|
||||
MoonriseRegionFileIO.scheduleSave(
|
||||
this.level, pos.x, pos.z, tag,
|
||||
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
|
||||
this.level, pos.x, pos.z, tag.get(),
|
||||
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(ChunkStorage.class)
|
||||
abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
private IOWorker worker;
|
||||
public IOWorker worker;
|
||||
|
||||
@Unique
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -118,13 +119,13 @@ abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseab
|
||||
method = "write",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
|
||||
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;"
|
||||
)
|
||||
)
|
||||
private CompletableFuture<Void> redirectWrite(final IOWorker instance, final ChunkPos chunkPos,
|
||||
final CompoundTag compoundTag) {
|
||||
final Supplier<CompoundTag> compoundTag) {
|
||||
try {
|
||||
this.storage.write(chunkPos, compoundTag);
|
||||
this.storage.write(chunkPos, compoundTag.get());
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} catch (final Throwable throwable) {
|
||||
return CompletableFuture.failedFuture(throwable);
|
||||
|
||||
@@ -41,8 +41,8 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
||||
@Final
|
||||
private ClientChunkCache chunkSource;
|
||||
|
||||
protected ClientLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
||||
protected ClientLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,14 +55,28 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData,
|
||||
ResourceKey<Level> resourceKey, Holder<DimensionType> holder, int i, int j, Supplier<ProfilerFiller> supplier,
|
||||
LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
|
||||
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey<Level> resourceKey,
|
||||
Holder<DimensionType> holder, int i, int j, LevelRenderer levelRenderer, boolean bl, long l, int k, CallbackInfo ci) {
|
||||
this.entityStorage = null;
|
||||
|
||||
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ClientChunkCache chunkSource = this.chunkSource;
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Redirect to new entity manager
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -8,13 +8,12 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ThrottlingChunkTaskDispatcher;
|
||||
import net.minecraft.server.level.Ticket;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.server.level.TickingTracker;
|
||||
import net.minecraft.util.SortedArraySet;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
@@ -45,13 +44,7 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
Set<ChunkHolder> chunksToUpdateFutures;
|
||||
|
||||
@Shadow
|
||||
ChunkTaskPriorityQueueSorter ticketThrottler;
|
||||
|
||||
@Shadow
|
||||
ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> ticketThrottlerInput;
|
||||
|
||||
@Shadow
|
||||
ProcessorHandle<ChunkTaskPriorityQueueSorter.Release> ticketThrottlerReleaser;
|
||||
ThrottlingChunkTaskDispatcher ticketDispatcher;
|
||||
|
||||
@Shadow
|
||||
LongSet ticketsToRelease;
|
||||
@@ -59,12 +52,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
@Shadow
|
||||
Executor mainThreadExecutor;
|
||||
|
||||
@Shadow
|
||||
private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
|
||||
|
||||
@Shadow
|
||||
private int simulationDistance;
|
||||
|
||||
|
||||
@Override
|
||||
public ChunkMap moonrise$getChunkMap() {
|
||||
throw new AbstractMethodError();
|
||||
@@ -86,16 +77,14 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
this.tickingTicketsTracker = null;
|
||||
this.playerTicketManager = null;
|
||||
this.chunksToUpdateFutures = null;
|
||||
this.ticketThrottler = null;
|
||||
this.ticketThrottlerInput = null;
|
||||
this.ticketThrottlerReleaser = null;
|
||||
this.ticketDispatcher = null;
|
||||
this.ticketsToRelease = null;
|
||||
this.mainThreadExecutor = null;
|
||||
this.simulationDistance = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkHolderManager moonrise$getChunkHolderManager() {
|
||||
public final ChunkHolderManager moonrise$getChunkHolderManager() {
|
||||
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
||||
}
|
||||
|
||||
@@ -326,6 +315,15 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Remove old chunk system hooks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public LongSet getTickingChunks() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason This hack is not required anymore, see {@link MinecraftServerMixin}
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -49,6 +50,9 @@ abstract class EntityMixin implements ChunkSystemEntity {
|
||||
@Unique
|
||||
private FullChunkStatus chunkStatus;
|
||||
|
||||
@Unique
|
||||
private ChunkData chunkData;
|
||||
|
||||
@Unique
|
||||
private int sectionX = Integer.MIN_VALUE;
|
||||
|
||||
@@ -76,6 +80,16 @@ abstract class EntityMixin implements ChunkSystemEntity {
|
||||
this.chunkStatus = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData() {
|
||||
return this.chunkData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$setChunkData(final ChunkData chunkData) {
|
||||
this.chunkData = chunkData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getSectionX() {
|
||||
return this.sectionX;
|
||||
|
||||
@@ -99,8 +99,18 @@ abstract class EntityTickListMixin {
|
||||
* @reason Route to new entity list
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void forEach(final Consumer<Entity> action) {
|
||||
@Inject(
|
||||
method = "forEach",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void injectForEach(final Consumer<Entity> consumer, final CallbackInfo ci) {
|
||||
this.forEach(consumer);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void forEach(final Consumer<Entity> action) {
|
||||
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
|
||||
// (by dfl iterator() is configured to not iterate over new entries)
|
||||
final IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = this.entities.iterator();
|
||||
|
||||
@@ -195,15 +195,6 @@ abstract class GenerationChunkHolderMixin {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Chunk system is not built on futures anymore
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public int getGenerationRefCount() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk holder
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -93,11 +93,15 @@ abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystemLevelCh
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnsaved(final boolean needsSaving) {
|
||||
if (!needsSaving) {
|
||||
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
|
||||
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
|
||||
public boolean tryMarkSaved() {
|
||||
if (!this.isUnsaved()) {
|
||||
return false;
|
||||
}
|
||||
super.setUnsaved(needsSaving);
|
||||
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
|
||||
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
|
||||
|
||||
super.tryMarkSaved();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.util.profiling.Profiler;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
@@ -33,9 +38,6 @@ import java.util.function.Predicate;
|
||||
@Mixin(Level.class)
|
||||
abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter, LevelAccessor, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
public abstract ProfilerFiller getProfiler();
|
||||
|
||||
@Shadow
|
||||
public abstract LevelChunk getChunk(int i, int j);
|
||||
|
||||
@@ -46,13 +48,16 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
@Unique
|
||||
private EntityLookup entityLookup;
|
||||
|
||||
@Unique
|
||||
private final ConcurrentLong2ReferenceChainedHashTable<ChunkData> chunkData = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
|
||||
@Override
|
||||
public final EntityLookup moonrise$getEntityLookup() {
|
||||
return this.entityLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moonrise$setEntityLookup(final EntityLookup entityLookup) {
|
||||
public final void moonrise$setEntityLookup(final EntityLookup entityLookup) {
|
||||
if (this.entityLookup != null && !(this.entityLookup instanceof DefaultEntityLookup)) {
|
||||
throw new IllegalStateException("Entity lookup already initialised");
|
||||
}
|
||||
@@ -80,7 +85,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
@Overwrite
|
||||
@Override
|
||||
public List<Entity> getEntities(final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
final List<Entity> ret = new ArrayList<>();
|
||||
|
||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entity, boundingBox, ret, predicate);
|
||||
@@ -98,7 +103,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
public <T extends Entity> void getEntities(final EntityTypeTest<Entity, T> entityTypeTest,
|
||||
final AABB boundingBox, final Predicate<? super T> predicate,
|
||||
final List<? super T> into, final int maxCount) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
|
||||
if (entityTypeTest instanceof EntityType<T> byType) {
|
||||
if (maxCount != Integer.MAX_VALUE) {
|
||||
@@ -171,7 +176,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
*/
|
||||
@Override
|
||||
public final <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
final List<T> ret = new ArrayList<>();
|
||||
|
||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate);
|
||||
@@ -189,7 +194,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
*/
|
||||
@Override
|
||||
public final List<Entity> moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
|
||||
this.getProfiler().incrementCounter("getEntities");
|
||||
Profiler.get().incrementCounter("getEntities");
|
||||
final List<Entity> ret = new ArrayList<>();
|
||||
|
||||
((ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate);
|
||||
@@ -217,6 +222,52 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
// no-op on ClientLevel
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData(final long chunkKey) {
|
||||
return this.chunkData.get(chunkKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData(final int chunkX, final int chunkZ) {
|
||||
return this.chunkData.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$requestChunkData(final long chunkKey) {
|
||||
return this.chunkData.compute(chunkKey, (final long keyInMap, final ChunkData valueInMap) -> {
|
||||
if (valueInMap == null) {
|
||||
final ChunkData ret = new ChunkData();
|
||||
ret.increaseRef();
|
||||
return ret;
|
||||
}
|
||||
|
||||
valueInMap.increaseRef();
|
||||
return valueInMap;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$releaseChunkData(final long chunkKey) {
|
||||
return this.chunkData.compute(chunkKey, (final long keyInMap, final ChunkData chunkData) -> {
|
||||
return chunkData.decreaseRef() == 0 ? null : chunkData;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ChunkSource chunkSource = this.getChunkSource();
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Declare method in this class so that any invocations are virtual, and not interface.
|
||||
* @author Spottedleaf
|
||||
@@ -226,6 +277,13 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
return this.getChunkSource().hasChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChunksAt(final int minBlockX, final int minBlockZ, final int maxBlockX, final int maxBlockZ) {
|
||||
return this.moonrise$areChunksLoaded(
|
||||
minBlockX >> 4, minBlockZ >> 4, maxBlockX >> 4, maxBlockZ >> 4
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Turn all getChunk(x, z, status) calls into virtual invokes, instead of interface invokes:
|
||||
* 1. The interface invoke is expensive
|
||||
|
||||
@@ -240,6 +240,7 @@ abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop<TickTask
|
||||
private void closeIOThreads(final CallbackInfo ci) {
|
||||
LOGGER.info("Waiting for I/O tasks to complete...");
|
||||
MoonriseRegionFileIO.flush((MinecraftServer)(Object)this);
|
||||
LOGGER.info("All I/O tasks to complete");
|
||||
if ((Object)this instanceof DedicatedServer) {
|
||||
MoonriseCommon.haltExecutors();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
@@ -44,7 +45,14 @@ import java.util.stream.Stream;
|
||||
@Mixin(PoiManager.class)
|
||||
// Declare the generic type as Object so that our Overrides match the method signature of the superclass
|
||||
// Specifically, getOrCreate must return Object so that existing invokes do not route to the superclass
|
||||
public abstract class PoiManagerMixin extends SectionStorage<Object> implements ChunkSystemPoiManager {
|
||||
public abstract class PoiManagerMixin extends SectionStorage<Object, Object> implements ChunkSystemPoiManager {
|
||||
|
||||
public PoiManagerMixin(final SimpleRegionStorage simpleRegionStorage, final Codec<Object> codec, final Function<Object, Object> function,
|
||||
final BiFunction<Object, Runnable, Object> biFunction, final Function<Runnable, Object> function2,
|
||||
final RegistryAccess registryAccess, final ChunkIOErrorReporter chunkIOErrorReporter,
|
||||
final LevelHeightAccessor levelHeightAccessor) {
|
||||
super(simpleRegionStorage, codec, function, biFunction, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
abstract boolean isVillageCenter(long l);
|
||||
@@ -52,10 +60,6 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
@Shadow
|
||||
public abstract void checkConsistencyWithBlocks(SectionPos sectionPos, LevelChunkSection levelChunkSection);
|
||||
|
||||
public PoiManagerMixin(SimpleRegionStorage simpleRegionStorage, Function<Runnable, Codec<Object>> function, Function<Runnable, Object> function2, RegistryAccess registryAccess, ChunkIOErrorReporter chunkIOErrorReporter, LevelHeightAccessor levelHeightAccessor) {
|
||||
super(simpleRegionStorage, function, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private ServerLevel world;
|
||||
|
||||
@@ -151,8 +155,8 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
|
||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
|
||||
|
||||
final ChunkHolderManager manager = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
||||
final PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
||||
final PoiChunk ret = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
||||
.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
||||
|
||||
return ret == null ? Optional.empty() : (Optional)ret.getSectionForVanilla(chunkY);
|
||||
}
|
||||
@@ -206,9 +210,13 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
public final void moonrise$onUnload(final long coordinate) { // Paper - rewrite chunk system
|
||||
final int chunkX = CoordinateUtils.getChunkX(coordinate);
|
||||
final int chunkZ = CoordinateUtils.getChunkZ(coordinate);
|
||||
|
||||
final int minY = WorldUtil.getMinSection(this.world);
|
||||
final int maxY = WorldUtil.getMaxSection(this.world);
|
||||
|
||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
|
||||
for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
|
||||
final long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
|
||||
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
||||
final long sectionPos = SectionPos.asLong(chunkX, sectionY, chunkZ);
|
||||
this.updateDistanceTracking(sectionPos);
|
||||
}
|
||||
}
|
||||
@@ -217,8 +225,12 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
public final void moonrise$loadInPoiChunk(final PoiChunk poiChunk) {
|
||||
final int chunkX = poiChunk.chunkX;
|
||||
final int chunkZ = poiChunk.chunkZ;
|
||||
|
||||
final int minY = WorldUtil.getMinSection(this.world);
|
||||
final int maxY = WorldUtil.getMaxSection(this.world);
|
||||
|
||||
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
|
||||
for (int sectionY = this.levelHeightAccessor.getMinSection(); sectionY < this.levelHeightAccessor.getMaxSection(); ++sectionY) {
|
||||
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
||||
final PoiSection section = poiChunk.getSection(sectionY);
|
||||
if (section != null && !((ChunkSystemPoiSection)section).moonrise$isEmpty()) {
|
||||
this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
|
||||
@@ -254,20 +266,4 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
|
||||
private <T> Stream<T> skipLoadedSet(final Stream<T> instance, final Predicate<? super T> predicate) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$close() throws IOException {}
|
||||
|
||||
@Override
|
||||
public final CompoundTag moonrise$read(final int chunkX, final int chunkZ) throws IOException {
|
||||
return MoonriseRegionFileIO.loadData(
|
||||
this.world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.POI_DATA,
|
||||
MoonriseRegionFileIO.getIOBlockingPriorityForCurrentThread()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$write(final int chunkX, final int chunkZ, final CompoundTag data) throws IOException {
|
||||
MoonriseRegionFileIO.scheduleSave(this.world, chunkX, chunkZ, data, MoonriseRegionFileIO.RegionFileType.POI_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ abstract class PoiSectionMixin implements ChunkSystemPoiSection {
|
||||
|
||||
|
||||
@Unique
|
||||
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);;
|
||||
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$isEmpty() {
|
||||
|
||||
@@ -2,8 +2,6 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.storage.ChunkSystemSectionStorage;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
||||
import net.minecraft.world.level.chunk.storage.SectionStorage;
|
||||
@@ -23,15 +21,11 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(SectionStorage.class)
|
||||
abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoCloseable {
|
||||
abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
private SimpleRegionStorage simpleRegionStorage;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static Logger LOGGER;
|
||||
|
||||
|
||||
@Unique
|
||||
private RegionFileStorage storage;
|
||||
@@ -41,6 +35,9 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
||||
return this.storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moonrise$close() throws IOException {}
|
||||
|
||||
/**
|
||||
* @reason Retrieve storage from IOWorker, and then nuke it
|
||||
* @author Spottedleaf
|
||||
@@ -61,12 +58,8 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public final CompletableFuture<Optional<CompoundTag>> tryRead(final ChunkPos pos) {
|
||||
try {
|
||||
return CompletableFuture.completedFuture(Optional.ofNullable(this.moonrise$read(pos.x, pos.z)));
|
||||
} catch (final Throwable thr) {
|
||||
return CompletableFuture.failedFuture(thr);
|
||||
}
|
||||
public final CompletableFuture<Optional<SectionStorage.PackedChunk<P>>> tryRead(final ChunkPos pos) {
|
||||
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,29 +67,17 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void readColumn(final ChunkPos pos, final RegistryOps<Tag> ops, final CompoundTag data) {
|
||||
public void unpackChunk(final ChunkPos chunkPos, final SectionStorage.PackedChunk<P> packedChunk) {
|
||||
throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Route to new chunk system hook
|
||||
* @reason Destroy old chunk system hook
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "writeColumn(Lnet/minecraft/world/level/ChunkPos;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/chunk/storage/SimpleRegionStorage;write(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
|
||||
)
|
||||
)
|
||||
private CompletableFuture<Void> redirectWrite(final SimpleRegionStorage instance, final ChunkPos pos,
|
||||
final CompoundTag tag) {
|
||||
try {
|
||||
this.moonrise$write(pos.x, pos.z, tag);
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.error("Error writing poi chunk data to disk for chunk " + pos, ex);
|
||||
}
|
||||
return null;
|
||||
@Overwrite
|
||||
private void writeChunk(final ChunkPos chunkPos) {
|
||||
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,4 +94,4 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
|
||||
private void redirectClose(final SimpleRegionStorage instance) throws IOException {
|
||||
this.moonrise$close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ChunkSerializer.class)
|
||||
abstract class ChunkSerializerMixin {
|
||||
@Mixin(SerializableChunkData.class)
|
||||
abstract class SerializableChunkDataMixin {
|
||||
|
||||
/**
|
||||
* @reason Chunk system handles this during full transition
|
||||
@@ -17,12 +17,12 @@ abstract class ChunkSerializerMixin {
|
||||
* @see ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask
|
||||
*/
|
||||
@Redirect(
|
||||
method = "read",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
|
||||
)
|
||||
method = "read",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
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) {}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
@@ -23,6 +24,7 @@ import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LightChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
@@ -33,6 +35,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -48,6 +52,10 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
@Final
|
||||
public ServerLevel level;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private DimensionDataStorage dataStorage;
|
||||
|
||||
@Unique
|
||||
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
|
||||
@@ -258,6 +266,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
@Override
|
||||
@Overwrite
|
||||
public void close() throws IOException {
|
||||
this.dataStorage.close();
|
||||
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(true, true);
|
||||
}
|
||||
|
||||
@@ -282,7 +291,8 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
*/
|
||||
@Overwrite
|
||||
public void getFullChunk(final long pos, final Consumer<LevelChunk> consumer) {
|
||||
final LevelChunk fullChunk = this.getChunkNow(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
|
||||
// note: bypass currentlyLoaded from getChunkNow
|
||||
final LevelChunk fullChunk = this.fullChunks.get(pos);
|
||||
if (fullChunk != null) {
|
||||
consumer.accept(fullChunk);
|
||||
}
|
||||
@@ -310,7 +320,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "tickChunks",
|
||||
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
shift = At.Shift.AFTER,
|
||||
@@ -331,27 +341,11 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ServerLevel;isNaturalSpawningAllowed(Lnet/minecraft/world/level/ChunkPos;)Z"
|
||||
)
|
||||
)
|
||||
private boolean shortNaturalSpawning(final ServerLevel instance, final ChunkPos chunkPos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason In the chunk system, ticking chunks always have loaded entities. Of course, they are also always
|
||||
* marked to be as ticking as well.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
|
||||
)
|
||||
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
|
||||
)
|
||||
)
|
||||
private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) {
|
||||
return true;
|
||||
|
||||
@@ -10,6 +10,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityData
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.PoiDataController;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
||||
@@ -26,6 +27,7 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
@@ -62,6 +64,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -83,8 +86,8 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
@Final
|
||||
private ServerChunkCache chunkSource;
|
||||
|
||||
protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
||||
protected ServerLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -345,6 +348,21 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
return this.entityTickingChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ServerChunkCache chunkSource = this.chunkSource;
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Declare method in this class so that any invocations are virtual, and not interface.
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -33,7 +33,7 @@ abstract class ChunkMapMixin {
|
||||
public ServerLevel level;
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean playerIsCloseEnoughForSpawning(ServerPlayer serverPlayer, ChunkPos chunkPos);
|
||||
public abstract boolean playerIsCloseEnoughForSpawning(ServerPlayer serverPlayer, ChunkPos chunkPos);
|
||||
|
||||
/**
|
||||
* @reason Hook for updating the spawn tracker in distance manager. We add our own hook instead of using the
|
||||
@@ -85,11 +85,20 @@ abstract class ChunkMapMixin {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use nearby players to avoid iterating over all online players
|
||||
* @reason Avoid checking first if there are nearby players, as we make internal perform this implicitly.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
|
||||
return this.anyPlayerCloseEnoughForSpawningInternal(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use nearby players to avoid iterating over all online players
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos) {
|
||||
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
|
||||
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
|
||||
);
|
||||
@@ -116,15 +125,15 @@ abstract class ChunkMapMixin {
|
||||
*/
|
||||
@Overwrite
|
||||
public List<ServerPlayer> getPlayersCloseForSpawning(final ChunkPos pos) {
|
||||
final List<ServerPlayer> ret = new ArrayList<>();
|
||||
|
||||
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
|
||||
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
|
||||
);
|
||||
if (players == null) {
|
||||
return ret;
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<ServerPlayer> ret = null;
|
||||
|
||||
final ServerPlayer[] raw = players.getRawDataUnchecked();
|
||||
final int len = players.size();
|
||||
|
||||
@@ -132,10 +141,15 @@ abstract class ChunkMapMixin {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
final ServerPlayer player = raw[i];
|
||||
if (this.playerIsCloseEnoughForSpawning(player, pos)) {
|
||||
ret.add(player);
|
||||
if (ret == null) {
|
||||
ret = new ArrayList<>(len - i);
|
||||
ret.add(player);
|
||||
} else {
|
||||
ret.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret == null ? new ArrayList<>() : ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -106,4 +107,13 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
|
||||
public boolean hasPlayersNearby(final long pos) {
|
||||
return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use spawnChunkTracker instead
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public LongIterator getSpawnCandidateChunks() {
|
||||
return this.spawnChunkTracker.getPositions().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(ServerChunkCache.class)
|
||||
abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
@@ -27,94 +33,84 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
@Final
|
||||
public ServerLevel level;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
public ChunkMap chunkMap;
|
||||
|
||||
|
||||
@Unique
|
||||
private ServerChunkCache.ChunkAndHolder[] iterationCopy;
|
||||
private final SimpleRandom shuffleRandom = new SimpleRandom(0L);
|
||||
|
||||
/**
|
||||
* @reason Avoid creating the list, which is sized at the chunkholder count. The actual number of ticking
|
||||
* chunks is always lower. The mixin below will initialise the list to non-null.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Lists;newArrayListWithCapacity(I)Ljava/util/ArrayList;"
|
||||
)
|
||||
)
|
||||
private <T> ArrayList<T> avoidListCreation(final int initialArraySize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Initialise the list to contain only the ticking chunks.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@ModifyVariable(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "STORE",
|
||||
opcode = Opcodes.ASTORE,
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private List<ServerChunkCache.ChunkAndHolder> initTickChunks(final List<ServerChunkCache.ChunkAndHolder> shouldBeNull) {
|
||||
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||
((ChunkSystemServerLevel)this.level).moonrise$getTickingChunks();
|
||||
|
||||
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];
|
||||
@Unique
|
||||
private boolean isChunkNearPlayer(final ChunkMap chunkMap, final ChunkPos chunkPos, final LevelChunk levelChunk) {
|
||||
final ChunkData chunkData = ((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
|
||||
.moonrise$getRealChunkHolder().holderData;
|
||||
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
||||
if (nearbyPlayers == null) {
|
||||
return false;
|
||||
}
|
||||
System.arraycopy(raw, 0, this.iterationCopy, 0, size);
|
||||
|
||||
return ObjectArrayList.wrap(
|
||||
this.iterationCopy, size
|
||||
);
|
||||
}
|
||||
final ReferenceList<ServerPlayer> players = nearbyPlayers.getPlayers(NearbyPlayers.NearbyMapType.SPAWN_RANGE);
|
||||
|
||||
if (players == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerPlayer[] raw = players.getRawDataUnchecked();
|
||||
final int len = players.size();
|
||||
|
||||
Objects.checkFromIndexSize(0, len, raw.length);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (chunkMap.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 Clear the iteration array, and at the same time broadcast chunk changes.
|
||||
* @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
|
||||
*/
|
||||
@Overwrite
|
||||
private void collectTickingChunks(final List<LevelChunk> list) {
|
||||
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
||||
|
||||
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
||||
final int size = tickingChunks.size();
|
||||
|
||||
final ChunkMap chunkMap = this.chunkMap;
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i];
|
||||
final LevelChunk levelChunk = chunkAndHolder.chunk();
|
||||
|
||||
if (!this.isChunkNearPlayer(chunkMap, levelChunk.getPos(), levelChunk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list.add(levelChunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
|
||||
* function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
||||
ordinal = 0
|
||||
)
|
||||
method = "tickChunks()V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
|
||||
)
|
||||
)
|
||||
private void broadcastChanges(final List<ServerChunkCache.ChunkAndHolder> instance,
|
||||
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
|
||||
final ObjectArrayList<ServerChunkCache.ChunkAndHolder> chunks = (ObjectArrayList<ServerChunkCache.ChunkAndHolder>)instance;
|
||||
final ServerChunkCache.ChunkAndHolder[] raw = chunks.elements();
|
||||
final int size = chunks.size();
|
||||
|
||||
Objects.checkFromToIndex(0, size, raw.length);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final ServerChunkCache.ChunkAndHolder holder = raw[i];
|
||||
raw[i] = null;
|
||||
|
||||
holder.holder().broadcastChanges(holder.chunk());
|
||||
}
|
||||
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
|
||||
this.shuffleRandom.setSeed(randomSource.nextLong());
|
||||
Util.shuffle(list, this.shuffleRandom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
abstract class ServerLevelMixin implements ChunkTickServerLevel {
|
||||
|
||||
@Unique
|
||||
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_PLAYER_CHUNK_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
|
||||
|
||||
@Unique
|
||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> playerTickingChunks = new ReferenceList<>(EMPTY_PLAYER_CHUNK_HOLDERS);
|
||||
|
||||
@Unique
|
||||
private final Long2IntOpenHashMap playerTickingRequests = new Long2IntOpenHashMap();
|
||||
|
||||
@Override
|
||||
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks() {
|
||||
return this.playerTickingChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$markChunkForPlayerTicking(final LevelChunk chunk) {
|
||||
final ChunkPos pos = chunk.getPos();
|
||||
if (!this.playerTickingRequests.containsKey(CoordinateUtils.getChunkKey(pos))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.add(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removeChunkForPlayerTicking(final LevelChunk chunk) {
|
||||
this.playerTickingChunks.remove(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$addPlayerTickingRequest(final int chunkX, final int chunkZ) {
|
||||
TickThread.ensureTickThread((ServerLevel)(Object)this, chunkX, chunkZ, "Cannot add ticking request async");
|
||||
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
|
||||
if (this.playerTickingRequests.addTo(chunkKey, 1) != 0) {
|
||||
// already added
|
||||
return;
|
||||
}
|
||||
|
||||
final NewChunkHolder chunkHolder = ((ChunkSystemServerLevel)(ServerLevel)(Object)this).moonrise$getChunkTaskScheduler()
|
||||
.chunkHolderManager.getChunkHolder(chunkKey);
|
||||
|
||||
if (chunkHolder == null || !chunkHolder.isTickingReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.add(
|
||||
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removePlayerTickingRequest(final int chunkX, final int chunkZ) {
|
||||
TickThread.ensureTickThread((ServerLevel)(Object)this, chunkX, chunkZ, "Cannot remove ticking request async");
|
||||
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
final int val = this.playerTickingRequests.addTo(chunkKey, -1);
|
||||
|
||||
if (val <= 0) {
|
||||
throw new IllegalStateException("Negative counter");
|
||||
}
|
||||
|
||||
if (val != 1) {
|
||||
// still has at least one request
|
||||
return;
|
||||
}
|
||||
|
||||
final NewChunkHolder chunkHolder = ((ChunkSystemServerLevel)(ServerLevel)(Object)this).moonrise$getChunkTaskScheduler()
|
||||
.chunkHolderManager.getChunkHolder(chunkKey);
|
||||
|
||||
if (chunkHolder == null || !chunkHolder.isTickingReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.remove(
|
||||
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,37 +6,32 @@ import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.entity.vehicle.AbstractMinecart;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(ArmorStand.class)
|
||||
abstract class ArmorStandMixin extends LivingEntity {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static Predicate<Entity> RIDABLE_MINECARTS;
|
||||
|
||||
protected ArmorStandMixin(EntityType<? extends LivingEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Optimise this method by making it use the class lookup
|
||||
* @reason Optimise by making it use the class lookup
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void pushEntities() {
|
||||
final List<AbstractMinecart> nearby = this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), RIDABLE_MINECARTS);
|
||||
|
||||
for (int i = 0, len = nearby.size(); i < len; ++i) {
|
||||
final AbstractMinecart minecart = nearby.get(i);
|
||||
if (this.distanceToSqr(minecart) <= 0.2) {
|
||||
minecart.push(this);
|
||||
}
|
||||
}
|
||||
@Redirect(
|
||||
method = "pushEntities",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/Level;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;"
|
||||
)
|
||||
)
|
||||
private List<Entity> redirectGetEntities(final Level instance, final Entity entity, final AABB list, final Predicate<? super Entity> arg) {
|
||||
return (List)this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), arg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,14 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(BlockPos.class)
|
||||
abstract class BlockPosMixin extends Vec3i {
|
||||
|
||||
@Shadow
|
||||
public abstract BlockPos immutable();
|
||||
|
||||
public BlockPosMixin(int i, int j, int k) {
|
||||
super(i, j, k);
|
||||
}
|
||||
@@ -29,7 +33,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos above(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY() + distance, this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() + distance, this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +53,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos below(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY() - distance, this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() - distance, this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +73,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos north(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY(), this.getZ() - distance);
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() - distance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +93,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos south(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY(), this.getZ() + distance);
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() + distance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +113,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos west(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX() - distance, this.getY(), this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX() - distance, this.getY(), this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +133,6 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos east(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX() + distance, this.getY(), this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX() + distance, this.getY(), this.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
@Shadow
|
||||
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) {
|
||||
super(object, reference2ObjectArrayMap, mapCodec);
|
||||
}
|
||||
@@ -74,7 +80,7 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
}
|
||||
if (neighbours) {
|
||||
for (final Direction direction : DIRECTIONS_CACHED) {
|
||||
initCaches(Shapes.getFaceShape(shape, direction), false);
|
||||
initCaches(((CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction), false);
|
||||
initCaches(shape.getFaceShape(direction), false);
|
||||
}
|
||||
}
|
||||
@@ -107,17 +113,21 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
if (this.constantCollisionShape != null) {
|
||||
initCaches(this.constantCollisionShape, true);
|
||||
}
|
||||
if (this.cache.occlusionShapes != null) {
|
||||
for (final VoxelShape shape : this.cache.occlusionShapes) {
|
||||
initCaches(shape, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.occludesFullBlock = false;
|
||||
this.emptyCollisionShape = false;
|
||||
this.emptyConstantCollisionShape = false;
|
||||
this.constantCollisionShape = null;
|
||||
}
|
||||
|
||||
if (this.occlusionShape != null) {
|
||||
initCaches(this.occlusionShape, true);
|
||||
}
|
||||
if (this.occlusionShapesByFace != null) {
|
||||
for (final VoxelShape shape : this.occlusionShapesByFace) {
|
||||
initCaches(shape, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
@@ -10,11 +10,11 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -48,7 +48,7 @@ abstract class EntityMixin {
|
||||
public abstract Vec3 getEyePosition();
|
||||
|
||||
@Shadow
|
||||
public abstract boolean onGround();
|
||||
private boolean onGround;
|
||||
|
||||
@Unique
|
||||
private static float[] calculateStepHeights(final AABB box, final List<VoxelShape> voxels, final List<AABB> aabbs, final float stepHeight,
|
||||
@@ -144,9 +144,9 @@ abstract class EntityMixin {
|
||||
|
||||
final boolean collidedDownwards = collidedY && movement.y < 0.0;
|
||||
|
||||
final double stepHeight = (double)this.maxUpStep();
|
||||
final double stepHeight;
|
||||
|
||||
if (stepHeight <= 0.0 || (!collidedDownwards && !this.onGround()) || (!collidedX && !collidedZ)) {
|
||||
if ((!collidedDownwards && !this.onGround) || (!collidedX && !collidedZ) || (stepHeight = (double)this.maxUpStep()) <= 0.0) {
|
||||
return collided;
|
||||
}
|
||||
|
||||
@@ -184,64 +184,99 @@ abstract class EntityMixin {
|
||||
return false;
|
||||
}
|
||||
|
||||
final float reducedWith = this.dimensions.width() * 0.8F;
|
||||
final AABB box = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
||||
final double reducedWith = (double)(this.dimensions.width() * 0.8F);
|
||||
final AABB boundingBox = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
||||
final Level world = this.level;
|
||||
|
||||
if (CollisionUtil.isEmpty(box)) {
|
||||
if (CollisionUtil.isEmpty(boundingBox)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final BlockPos.MutableBlockPos tempPos = new BlockPos.MutableBlockPos();
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Mth.floor(boundingBox.minY);
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
final int minX = Mth.floor(box.minX);
|
||||
final int minY = Mth.floor(box.minY);
|
||||
final int minZ = Mth.floor(box.minZ);
|
||||
final int maxX = Mth.floor(box.maxX);
|
||||
final int maxY = Mth.floor(box.maxY);
|
||||
final int maxZ = Mth.floor(box.maxZ);
|
||||
final int maxBlockX = Mth.floor(boundingBox.maxX);
|
||||
final int maxBlockY = Mth.floor(boundingBox.maxY);
|
||||
final int maxBlockZ = Mth.floor(boundingBox.maxZ);
|
||||
|
||||
final ChunkSource chunkProvider = this.level.getChunkSource();
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
|
||||
long lastChunkKey = ChunkPos.INVALID_CHUNK_POS;
|
||||
LevelChunk lastChunk = null;
|
||||
for (int fz = minZ; fz <= maxZ; ++fz) {
|
||||
tempPos.setZ(fz);
|
||||
for (int fx = minX; fx <= maxX; ++fx) {
|
||||
final int newChunkX = fx >> 4;
|
||||
final int newChunkZ = fz >> 4;
|
||||
final LevelChunk chunk = lastChunkKey == (lastChunkKey = CoordinateUtils.getChunkKey(newChunkX, newChunkZ)) ?
|
||||
lastChunk : (lastChunk = (LevelChunk)chunkProvider.getChunk(newChunkX, newChunkZ, ChunkStatus.FULL, true));
|
||||
tempPos.setX(fx);
|
||||
for (int fy = minY; fy <= maxY; ++fy) {
|
||||
tempPos.setY(fy);
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final BlockState state = chunk.getBlockState(tempPos);
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
if (((CollisionBlockState)state).moonrise$emptyCollisionShape() || !state.isSuffocating(this.level, tempPos)) {
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, true).getSections();
|
||||
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, it does not use the Entity context stuff.
|
||||
final VoxelShape collisionShape = state.getCollisionShape(this.level, tempPos);
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
if (collisionShape.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
|
||||
final AABB toCollide = box.move(-(double)fx, -(double)fy, -(double)fz);
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
final int blockY = currY | (currChunkY << 4);
|
||||
mutablePos.setY(blockY);
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
final int blockZ = currZ | (currChunkZ << 4);
|
||||
mutablePos.setZ(blockZ);
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final int blockX = currX | (currChunkX << 4);
|
||||
mutablePos.setX(blockX);
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)collisionShape).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
if (CollisionUtil.voxelShapeIntersect(singleAABB, toCollide)) {
|
||||
return true;
|
||||
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
||||
|
||||
if (((CollisionBlockState)blockState).moonrise$emptyCollisionShape()
|
||||
|| !blockState.isSuffocating(world, mutablePos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, it does not use the Entity context stuff.
|
||||
final VoxelShape collisionShape = blockState.getCollisionShape(world, mutablePos);
|
||||
|
||||
if (collisionShape.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final AABB toCollide = boundingBox.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)collisionShape).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
if (CollisionUtil.voxelShapeIntersect(singleAABB, toCollide)) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CollisionUtil.voxelShapeIntersectNoEmpty(collisionShape, toCollide)) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CollisionUtil.voxelShapeIntersectNoEmpty(collisionShape, toCollide)) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
@@ -16,6 +15,7 @@ import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
@@ -27,20 +27,18 @@ import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(Level.class)
|
||||
abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseable {
|
||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
public abstract LevelChunk getChunk(int x, int z);
|
||||
@@ -48,38 +46,6 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
@Shadow
|
||||
public abstract WorldBorder getWorldBorder();
|
||||
|
||||
|
||||
@Unique
|
||||
private int minSection;
|
||||
|
||||
@Unique
|
||||
private int maxSection;
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMinSection() {
|
||||
return this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMaxSection() {
|
||||
return this.maxSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Init min/max section
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
this.minSection = WorldUtil.getMinSection(this);
|
||||
this.maxSection = WorldUtil.getMaxSection(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to faster lookup.
|
||||
* See {@link EntityGetterMixin#isUnobstructed(Entity, VoxelShape)} for expected behavior
|
||||
@@ -117,7 +83,7 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
final Vec3 to = clipContext.getTo();
|
||||
final Vec3 from = clipContext.getFrom();
|
||||
|
||||
return BlockHitResult.miss(to, Direction.getNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
||||
return BlockHitResult.miss(to, Direction.getApproximateNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -173,7 +139,7 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
int lastChunkY = Integer.MIN_VALUE;
|
||||
int lastChunkZ = Integer.MIN_VALUE;
|
||||
|
||||
final int minSection = ((CollisionLevel)level).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(level);
|
||||
|
||||
for (;;) {
|
||||
currPos.set(currX, currY, currZ);
|
||||
@@ -308,7 +274,7 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
*/
|
||||
@Override
|
||||
public Optional<Vec3> findFreePosition(final Entity entity, final VoxelShape boundsShape, final Vec3 fromPosition,
|
||||
final double rangeX, final double rangeY, final double rangeZ) {
|
||||
final double rangeX, final double rangeY, final double rangeZ) {
|
||||
if (boundsShape.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -368,102 +334,138 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
*/
|
||||
@Override
|
||||
public Optional<BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
||||
final int minSection = WorldUtil.getMinSection((Level)(Object)this);
|
||||
|
||||
final int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
final int minBlockY = Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockY = Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection((Level)(Object)this) << 4) + 16, Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1);
|
||||
|
||||
final int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
CollisionUtil.LazyEntityCollisionContext collisionContext = null;
|
||||
|
||||
final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
final CollisionContext collisionShape = new CollisionUtil.LazyEntityCollisionContext(entity);
|
||||
BlockPos selected = null;
|
||||
double selectedDistance = Double.MAX_VALUE;
|
||||
|
||||
final Vec3 entityPos = entity.position();
|
||||
|
||||
LevelChunk lastChunk = null;
|
||||
int lastChunkX = Integer.MIN_VALUE;
|
||||
int lastChunkZ = Integer.MIN_VALUE;
|
||||
// special cases:
|
||||
if (minBlockY > maxBlockY) {
|
||||
// no point in checking
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final ChunkSource chunkSource = this.getChunkSource();
|
||||
|
||||
for (int currZ = minBlockZ; currZ <= maxBlockZ; ++currZ) {
|
||||
pos.setZ(currZ);
|
||||
for (int currX = minBlockX; currX <= maxBlockX; ++currX) {
|
||||
pos.setX(currX);
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);
|
||||
|
||||
final int newChunkX = currX >> 4;
|
||||
final int newChunkZ = currZ >> 4;
|
||||
|
||||
if (((newChunkX ^ lastChunkX) | (newChunkZ ^ lastChunkZ)) != 0) {
|
||||
lastChunkX = newChunkX;
|
||||
lastChunkZ = newChunkZ;
|
||||
lastChunk = (LevelChunk)chunkSource.getChunk(newChunkX, newChunkZ, ChunkStatus.FULL, false);
|
||||
}
|
||||
|
||||
if (lastChunk == null) {
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
for (int currY = minBlockY; currY <= maxBlockY; ++currY) {
|
||||
int edgeCount = ((currX == minBlockX || currX == maxBlockX) ? 1 : 0) +
|
||||
((currY == minBlockY || currY == maxBlockY) ? 1 : 0) +
|
||||
((currZ == minBlockZ || currZ == maxBlockZ) ? 1 : 0);
|
||||
if (edgeCount == 3) {
|
||||
|
||||
final LevelChunkSection[] sections = chunk.getSections();
|
||||
|
||||
// bound y
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
pos.setY(currY);
|
||||
final boolean hasSpecial = ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks();
|
||||
final int sectionAdjust = !hasSpecial ? 1 : 0;
|
||||
|
||||
final double distance = pos.distToCenterSqr(entityPos);
|
||||
if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(pos) >= 0)) {
|
||||
continue;
|
||||
}
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final BlockState state = ((GetBlockChunk)lastChunk).moonrise$getBlock(currX, currY, currZ);
|
||||
if (((CollisionBlockState)state).moonrise$emptyContextCollisionShape()) {
|
||||
continue;
|
||||
}
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) + sectionAdjust : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) - sectionAdjust : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) + sectionAdjust : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) - sectionAdjust : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) + sectionAdjust : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) - sectionAdjust : 15;
|
||||
|
||||
VoxelShape blockCollision = ((CollisionBlockState)state).moonrise$getConstantContextCollisionShape();
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
final int blockY = currY | (currChunkY << 4);
|
||||
mutablePos.setY(blockY);
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
final int blockZ = currZ | (currChunkZ << 4);
|
||||
mutablePos.setZ(blockZ);
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
|
||||
final int blockX = currX | (currChunkX << 4);
|
||||
mutablePos.setX(blockX);
|
||||
|
||||
if ((edgeCount != 1 || state.hasLargeCollisionShape()) && (edgeCount != 2 || state.getBlock() == Blocks.MOVING_PISTON)) {
|
||||
if (collisionContext == null) {
|
||||
collisionContext = new CollisionUtil.LazyEntityCollisionContext(entity);
|
||||
}
|
||||
final int edgeCount = hasSpecial ? ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
|
||||
((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
|
||||
((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0) : 0;
|
||||
if (edgeCount == 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (blockCollision == null) {
|
||||
blockCollision = state.getCollisionShape((Level)(Object)this, pos, collisionContext);
|
||||
}
|
||||
final double distance = mutablePos.distToCenterSqr(entityPos);
|
||||
if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(mutablePos) >= 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (blockCollision.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
final BlockState blockData = blocks.get(localBlockIndex);
|
||||
|
||||
// avoid VoxelShape#move by shifting the entity collision shape instead
|
||||
final AABB shiftedAABB = aabb.move(-(double)currX, -(double)currY, -(double)currZ);
|
||||
if (((CollisionBlockState)blockData).moonrise$emptyContextCollisionShape()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
if (!CollisionUtil.voxelShapeIntersect(singleAABB, shiftedAABB)) {
|
||||
continue;
|
||||
VoxelShape blockCollision = ((CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape();
|
||||
|
||||
if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) {
|
||||
if (blockCollision == null) {
|
||||
blockCollision = blockData.getCollisionShape((Level)(Object)this, mutablePos, collisionShape);
|
||||
|
||||
if (blockCollision.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid VoxelShape#move by shifting the entity collision shape instead
|
||||
final AABB shiftedAABB = aabb.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
if (!CollisionUtil.voxelShapeIntersect(singleAABB, shiftedAABB)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
selected = mutablePos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CollisionUtil.voxelShapeIntersectNoEmpty(blockCollision, shiftedAABB)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
selected = mutablePos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
selected = pos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CollisionUtil.voxelShapeIntersectNoEmpty(blockCollision, shiftedAABB)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
selected = pos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||
import net.minecraft.client.renderer.block.LiquidBlockRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.shapes.ArrayVoxelShape;
|
||||
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||
@@ -23,12 +21,7 @@ abstract class LiquidBlockRendererMixin {
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
private static boolean isFaceOccludedByState(final BlockGetter world, final Direction direction, final float height,
|
||||
final BlockPos pos, final BlockState state) {
|
||||
if (!state.canOcclude()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isFaceOccludedByState(final Direction direction, final float height, final BlockState state) {
|
||||
// check for created shape is empty
|
||||
if (height < (float)CollisionUtil.COLLISION_EPSILON) {
|
||||
return false;
|
||||
@@ -50,25 +43,26 @@ abstract class LiquidBlockRendererMixin {
|
||||
} else {
|
||||
// the extrusion includes the height
|
||||
heightShape = new ArrayVoxelShape(
|
||||
Shapes.block().shape,
|
||||
CollisionUtil.ZERO_ONE,
|
||||
DoubleArrayList.wrap(new double[] { 0.0, heightDouble }),
|
||||
CollisionUtil.ZERO_ONE
|
||||
Shapes.block().shape,
|
||||
CollisionUtil.ZERO_ONE,
|
||||
DoubleArrayList.wrap(new double[] { 0.0, heightDouble }),
|
||||
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
|
||||
return false;
|
||||
}
|
||||
|
||||
// fast check for box
|
||||
if (heightShape == stateShape) {
|
||||
if (heightShape == occlusionShape) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !Shapes.joinIsNotEmpty(heightShape, stateShape, BooleanOp.ONLY_FIRST);
|
||||
return !Shapes.joinIsNotEmpty(heightShape, occlusionShape, BooleanOp.ONLY_FIRST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() ? 1 : 0);
|
||||
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 boolean collided = CollisionUtil.getCollisionsForBlocksOrWorldBorder(
|
||||
world, entity, collisionBox, voxels, boxes,
|
||||
0,
|
||||
null
|
||||
0, null
|
||||
);
|
||||
|
||||
if (!collided) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache;
|
||||
@@ -10,20 +9,15 @@ import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
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.Explosion;
|
||||
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.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
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.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(Explosion.class)
|
||||
abstract class ExplosionMixin {
|
||||
@Mixin(ServerExplosion.class)
|
||||
abstract class ServerExplosionMixin {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Level level;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Entity source;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private double x;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private double y;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private double z;
|
||||
private ServerLevel level;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
@@ -69,21 +50,13 @@ abstract class ExplosionMixin {
|
||||
@Final
|
||||
private float radius;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private ObjectArrayList<BlockPos> toBlow;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<Player, Vec3> hitPlayers;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private boolean fire;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private DamageSource damageSource;
|
||||
private Vec3 center;
|
||||
|
||||
|
||||
@Unique
|
||||
@@ -142,6 +115,12 @@ abstract class ExplosionMixin {
|
||||
@Unique
|
||||
private LevelChunk[] chunkCache = null;
|
||||
|
||||
@Unique
|
||||
private ExplosionBlockCache[] directMappedBlockCache;
|
||||
|
||||
@Unique
|
||||
private BlockPos.MutableBlockPos mutablePos;
|
||||
|
||||
@Unique
|
||||
private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z,
|
||||
final long key, final boolean calculateResistance) {
|
||||
@@ -343,29 +322,45 @@ abstract class ExplosionMixin {
|
||||
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
|
||||
*/
|
||||
@Overwrite
|
||||
public void explode() {
|
||||
this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
|
||||
public List<BlockPos> calculateExplodedPositions() {
|
||||
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];
|
||||
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];
|
||||
final ExplosionBlockCache[] blockCache = this.directMappedBlockCache;
|
||||
|
||||
// use initial cache value that is most likely to be used: the source position
|
||||
final ExplosionBlockCache initialCache;
|
||||
{
|
||||
final int blockX = Mth.floor(this.x);
|
||||
final int blockY = Mth.floor(this.y);
|
||||
final int blockZ = Mth.floor(this.z);
|
||||
final int blockX = Mth.floor(center.x);
|
||||
final int blockY = Mth.floor(center.y);
|
||||
final int blockZ = Mth.floor(center.z);
|
||||
|
||||
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;) {
|
||||
ExplosionBlockCache cachedBlock = initialCache;
|
||||
|
||||
double currX = this.x;
|
||||
double currY = this.y;
|
||||
double currZ = this.z;
|
||||
double currX = center.x;
|
||||
double currY = center.y;
|
||||
double currZ = center.z;
|
||||
|
||||
final double incX = CACHED_RAYS[ray];
|
||||
final double incY = CACHED_RAYS[ray + 1];
|
||||
@@ -402,7 +397,7 @@ abstract class ExplosionMixin {
|
||||
|
||||
if (cachedBlock.key != key) {
|
||||
final int cacheKey =
|
||||
(blockX & BLOCK_EXPLOSION_CACHE_MASK) |
|
||||
(blockX & BLOCK_EXPLOSION_CACHE_MASK) |
|
||||
(blockY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) |
|
||||
(blockZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
|
||||
cachedBlock = blockCache[cacheKey];
|
||||
@@ -424,7 +419,7 @@ abstract class ExplosionMixin {
|
||||
cachedBlock.shouldExplode = shouldExplode ? Boolean.TRUE : Boolean.FALSE;
|
||||
if (shouldExplode) {
|
||||
if (this.fire || !cachedBlock.blockState.isAir()) {
|
||||
this.toBlow.add(cachedBlock.immutablePos);
|
||||
ret.add(cachedBlock.immutablePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,83 +431,42 @@ abstract class ExplosionMixin {
|
||||
} while (power > 0.0f);
|
||||
}
|
||||
|
||||
final double diameter = (double)this.radius * 2.0;
|
||||
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)),
|
||||
return ret;
|
||||
}
|
||||
|
||||
(double)Mth.floor(this.x + (diameter + 1.0)),
|
||||
(double)Mth.floor(this.y + (diameter + 1.0)),
|
||||
(double)Mth.floor(this.z + (diameter + 1.0))
|
||||
)
|
||||
);
|
||||
final Vec3 center = new Vec3(this.x, this.y, this.z);
|
||||
|
||||
final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
|
||||
|
||||
final PlatformHooks platformHooks = PlatformHooks.get();
|
||||
|
||||
platformHooks.onExplosion(this.level, (Explosion)(Object)this, entities, diameter);
|
||||
for (int i = 0, len = entities.size(); i < len; ++i) {
|
||||
final Entity entity = entities.get(i);
|
||||
if (entity.ignoreExplosion((Explosion)(Object)this)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final double normalizedDistanceToCenter = Math.sqrt(entity.distanceToSqr(center)) / diameter;
|
||||
if (normalizedDistanceToCenter > 1.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double distX = entity.getX() - this.x;
|
||||
double distY = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y;
|
||||
double distZ = entity.getZ() - this.z;
|
||||
final double distMag = Math.sqrt(distX * distX + distY * distY + distZ * distZ);
|
||||
|
||||
if (distMag == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
distX /= distMag;
|
||||
distY /= distMag;
|
||||
distZ /= distMag;
|
||||
|
||||
// route to new visible fraction calculation, using the existing block cache
|
||||
final double seenFraction = (double)this.getSeenFraction(center, entity, blockCache, blockPos);
|
||||
if (this.damageCalculator.shouldDamageEntity((Explosion)(Object)this, entity)) {
|
||||
// inline getEntityDamageAmount so that we can avoid double calling getSeenPercent, which is the MOST
|
||||
// expensive part of this loop!!!!
|
||||
final double factor = (1.0 - normalizedDistanceToCenter) * seenFraction;
|
||||
entity.hurt(this.damageSource, (float)((factor * factor + factor) / 2.0 * 7.0 * diameter + 1.0));
|
||||
}
|
||||
|
||||
final double intensityFraction = (1.0 - normalizedDistanceToCenter) * seenFraction * (double)this.damageCalculator.getKnockbackMultiplier(entity);
|
||||
|
||||
|
||||
final double knockbackFraction;
|
||||
if (entity instanceof LivingEntity livingEntity) {
|
||||
knockbackFraction = intensityFraction * (1.0 - livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE));
|
||||
} else {
|
||||
knockbackFraction = intensityFraction;
|
||||
}
|
||||
|
||||
Vec3 knockback = new Vec3(distX * knockbackFraction, distY * knockbackFraction, distZ * knockbackFraction);
|
||||
knockback = platformHooks.modifyExplosionKnockback(this.level, (Explosion)(Object)this, entity, knockback);
|
||||
entity.setDeltaMovement(entity.getDeltaMovement().add(knockback));
|
||||
|
||||
if (entity instanceof Player player) {
|
||||
if (!player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying)) {
|
||||
this.hitPlayers.put(player, knockback);
|
||||
}
|
||||
}
|
||||
|
||||
entity.onExplosionHit(this.source);
|
||||
}
|
||||
/**
|
||||
* @reason Use optimised getSeenPercent implementation
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {
|
||||
"hurtEntities()V",
|
||||
"hurtEntities(Ljava/util/List;)V" // Neo moves logic into this new method
|
||||
},
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/ServerExplosion;getSeenPercent(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)F"
|
||||
)
|
||||
)
|
||||
private float useBetterSeenPercent(final Vec3 center, final Entity target) {
|
||||
return this.getSeenFraction(center, target, this.directMappedBlockCache, this.mutablePos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Destroy cache fields
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "explode",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void destroyCacheFields(final CallbackInfo ci) {
|
||||
this.blockCache = null;
|
||||
this.chunkPosCache = null;
|
||||
this.chunkCache = null;
|
||||
this.directMappedBlockCache = null;
|
||||
this.mutablePos = null;
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,11 @@ import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Supplier;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Shapes.class)
|
||||
abstract class ShapesMixin {
|
||||
@@ -168,13 +170,13 @@ abstract class ShapesMixin {
|
||||
final VoxelShape first = tmp[i];
|
||||
final VoxelShape second = tmp[next];
|
||||
|
||||
tmp[newSize++] = Shapes.or(first, second);
|
||||
tmp[newSize++] = Shapes.joinUnoptimized(first, second, BooleanOp.OR);
|
||||
}
|
||||
}
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
return tmp[0];
|
||||
return tmp[0].optimize();
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -219,11 +221,24 @@ abstract class ShapesMixin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an inject instead of overwrite to avoid mixin conflicts - obviously this will still disregard any changes made by other
|
||||
* mixins, but at least it won't conflict immediately. This is useful because some library mods mixin here when only the content
|
||||
* mod actually needs the change.
|
||||
*
|
||||
* @reason Route to faster logic
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
@Inject(
|
||||
method = "joinUnoptimized",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void injectJoinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction, final CallbackInfoReturnable<VoxelShape> cir) {
|
||||
cir.setReturnValue(joinUnoptimized(first, second, mergeFunction));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
final VoxelShape ret = CollisionUtil.joinUnoptimized(first, second, mergeFunction);
|
||||
if (DEBUG_SHAPE_MERGING) {
|
||||
final VoxelShape vanilla = joinUnoptimizedVanilla(first, second, mergeFunction);
|
||||
@@ -239,9 +254,19 @@ abstract class ShapesMixin {
|
||||
/**
|
||||
* @reason Route to faster logic
|
||||
* @author Spottedleaf
|
||||
* @see #injectJoinUnoptimized(VoxelShape, VoxelShape, BooleanOp, CallbackInfoReturnable)
|
||||
*/
|
||||
@Overwrite
|
||||
public static boolean joinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
@Inject(
|
||||
method = "joinIsNotEmpty(Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/BooleanOp;)Z",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void injectJoinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction, final CallbackInfoReturnable<Boolean> cir) {
|
||||
cir.setReturnValue(joinIsNotEmpty(first, second, mergeFunction));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean joinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
final boolean ret = CollisionUtil.isJoinNonEmpty(first, second, mergeFunction);
|
||||
if (DEBUG_SHAPE_MERGING) {
|
||||
if (ret != !joinUnoptimizedVanilla(first, second, mergeFunction).isEmpty()) {
|
||||
@@ -253,15 +278,6 @@ abstract class ShapesMixin {
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
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.VoxelShape;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -22,7 +20,7 @@ abstract class SliceShapeMixin {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,13 +687,13 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
final AABB singleAABB = this.singleAABBRepresentation;
|
||||
if (singleAABB != null) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -67,7 +67,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkHolder.PlayerP
|
||||
)
|
||||
)
|
||||
private boolean newTrackerTick(final Iterator<?> iterator) {
|
||||
final NearbyPlayers nearbyPlayers = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers();
|
||||
final ServerEntityLookup entityLookup = (ServerEntityLookup)((ChunkSystemServerLevel)this.level).moonrise$getEntityLookup();;
|
||||
|
||||
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
|
||||
@@ -78,7 +77,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkHolder.PlayerP
|
||||
if (tracker == null) {
|
||||
continue;
|
||||
}
|
||||
((EntityTrackerTrackedEntity)tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
((EntityTrackerTrackedEntity)tracker).moonrise$tick(((ChunkSystemEntity)entity).moonrise$getChunkData().nearbyPlayers);
|
||||
if (((EntityTrackerTrackedEntity)tracker).moonrise$hasPlayers()
|
||||
|| ((ChunkSystemEntity)entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
|
||||
tracker.serverEntity.sendChanges();
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
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.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(ChunkMap.TrackedEntity.class)
|
||||
@@ -22,26 +23,22 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
||||
@Final
|
||||
private Set<ServerPlayerConnection> seenBy;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private int range;
|
||||
|
||||
@Shadow
|
||||
public abstract void updatePlayer(ServerPlayer serverPlayer);
|
||||
|
||||
@Shadow
|
||||
public abstract void removePlayer(ServerPlayer serverPlayer);
|
||||
|
||||
/**
|
||||
* @reason ReferenceOpenHashSet is a better choice than a wrapped IdentityHashMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Sets;newIdentityHashSet()Ljava/util/Set;"
|
||||
)
|
||||
)
|
||||
private <E> Set<E> useBetterIdentitySet() {
|
||||
return new ReferenceOpenHashSet<>();
|
||||
}
|
||||
@Shadow
|
||||
protected abstract int scaledRange(final int i);
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
Entity entity;
|
||||
|
||||
@Unique
|
||||
private long lastChunkUpdate = -1L;
|
||||
@@ -126,4 +123,43 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
||||
public final boolean moonrise$hasPlayers() {
|
||||
return !this.seenBy.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason ReferenceOpenHashSet is a better choice than a wrapped IdentityHashMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Sets;newIdentityHashSet()Ljava/util/Set;"
|
||||
)
|
||||
)
|
||||
private <E> Set<E> useBetterIdentitySet() {
|
||||
return new ReferenceOpenHashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Optimise impl to not retrieve indirect passengers unless needed
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public int getEffectiveRange() {
|
||||
final Entity entity = this.entity;
|
||||
int range = PlatformHooks.get().modifyEntityTrackingRange(entity, this.range);
|
||||
|
||||
if (entity.getPassengers() == ImmutableList.<Entity>of()) {
|
||||
return this.scaledRange(range);
|
||||
}
|
||||
|
||||
// note: we change to List
|
||||
final List<Entity> passengers = (List<Entity>)entity.getIndirectPassengers();
|
||||
for (int i = 0, len = passengers.size(); i < len; ++i) {
|
||||
final Entity passenger = passengers.get(i);
|
||||
// note: max should be branchless
|
||||
range = Math.max(range, PlatformHooks.get().modifyEntityTrackingRange(passenger, passenger.getType().clientTrackingRange() << 4));
|
||||
}
|
||||
|
||||
return this.scaledRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
||||
private void updateData(final PalettedContainer.Data<T> data) {
|
||||
if (data != null) {
|
||||
((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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -35,40 +35,18 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V",
|
||||
// cannot use `<init>*` due to https://github.com/FabricMC/tiny-remapper/issues/137
|
||||
method = {
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V"
|
||||
},
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
),
|
||||
require = 3 // Require matching all 3 constructors
|
||||
)
|
||||
private void constructorHook1(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook2(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook3(final CallbackInfo ci) {
|
||||
private void constructorHook(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
@@ -102,7 +80,7 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
||||
|
||||
@Unique
|
||||
private T readPaletteSlow(final PalettedContainer.Data<T> data, final int paletteIdx) {
|
||||
return data.palette.valueFor(paletteIdx);
|
||||
return data.palette().valueFor(paletteIdx);
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -125,9 +103,9 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
||||
*/
|
||||
@Overwrite
|
||||
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 int prev = data.storage.getAndSet(index, paletteIdx);
|
||||
final int prev = data.storage().getAndSet(index, paletteIdx);
|
||||
return this.readPalette(data, prev);
|
||||
}
|
||||
|
||||
@@ -138,6 +116,6 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
||||
@Overwrite
|
||||
public T get(final int index) {
|
||||
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;
|
||||
|
||||
@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
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
private boolean canPassThroughWall(final Direction direction, final BlockGetter level,
|
||||
final BlockPos fromPos, final BlockState fromState,
|
||||
final BlockPos toPos, final BlockState toState) {
|
||||
public static boolean canPassThroughWall(final Direction direction, final BlockGetter level,
|
||||
final BlockPos fromPos, final BlockState fromState,
|
||||
final BlockPos toPos, final BlockState toState) {
|
||||
if (((CollisionBlockState)fromState).moonrise$emptyCollisionShape() & ((CollisionBlockState)toState).moonrise$emptyCollisionShape()) {
|
||||
// don't even try to cache simple cases
|
||||
return true;
|
||||
@@ -135,7 +135,7 @@ abstract class FlowingFluidMixin extends Fluid {
|
||||
}
|
||||
|
||||
final FluidOcclusionCacheKey[] cache = ((CollisionBlockState)fromState).moonrise$hasCache() & ((CollisionBlockState)toState).moonrise$hasCache() ?
|
||||
COLLISION_OCCLUSION_CACHE : null;
|
||||
COLLISION_OCCLUSION_CACHE.get() : null;
|
||||
|
||||
final int keyIndex
|
||||
= (((CollisionBlockState)fromState).moonrise$uniqueId1() ^ ((CollisionBlockState)toState).moonrise$uniqueId2() ^ ((CollisionDirection)(Object)direction).moonrise$uniqueId())
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fluid;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fluid.FluidFluidState;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -11,12 +12,9 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(FluidState.class)
|
||||
abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> {
|
||||
abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> implements FluidFluidState {
|
||||
|
||||
@Shadow
|
||||
public abstract Fluid getType();
|
||||
@@ -43,23 +41,9 @@ abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> {
|
||||
@Unique
|
||||
private BlockState legacyBlock;
|
||||
|
||||
/**
|
||||
* @reason Initialise caches
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
try {
|
||||
this.amount = this.getType().getAmount((FluidState)(Object)this);
|
||||
} catch (final Exception ex) {
|
||||
// https://github.com/JDKDigital/productivetrees/issues/16
|
||||
new RuntimeException("Failed to retrieve fluid amount for " + this, ex).printStackTrace();
|
||||
}
|
||||
@Override
|
||||
public final void moonrise$initCaches() {
|
||||
this.amount = this.getType().getAmount((FluidState)(Object)this);
|
||||
this.isEmpty = this.getType().isEmpty();
|
||||
this.isSource = this.getType().isSource((FluidState)(Object)this);
|
||||
this.ownHeight = this.getType().getOwnHeight((FluidState)(Object)this);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fluid;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fluid.FluidFluidState;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.RegistrationInfo;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(MappedRegistry.class)
|
||||
abstract class MappedRegistryMixin<T> {
|
||||
/**
|
||||
* @reason Initialise caches
|
||||
* @author jpenilla
|
||||
*/
|
||||
@Inject(
|
||||
method = "register(Lnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lnet/minecraft/core/RegistrationInfo;)Lnet/minecraft/core/Holder$Reference;",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void injectFluidRegister(
|
||||
final ResourceKey<?> resourceKey,
|
||||
final T object,
|
||||
final RegistrationInfo registrationInfo,
|
||||
final CallbackInfoReturnable<Holder.Reference<T>> cir
|
||||
) {
|
||||
if (resourceKey.registryKey() == (Object)Registries.FLUID) {
|
||||
for (final FluidState possibleState : ((Fluid)object).getStateDefinition().getPossibleStates()) {
|
||||
((FluidFluidState)(Object)possibleState).moonrise$initCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
@@ -81,6 +81,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
|
||||
* @reason Route to optimized getBlock
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Override
|
||||
@Overwrite
|
||||
public BlockState getBlockState(final BlockPos pos) {
|
||||
return this.moonrise$getBlock(pos.getX(), pos.getY(), pos.getZ());
|
||||
@@ -101,7 +102,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState moonrise$getBlock(final int x, final int y, final int z) {
|
||||
public final BlockState moonrise$getBlock(final int x, final int y, final int z) {
|
||||
if (this.debug) {
|
||||
return this.getBlockDebug(x, y, z);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
// Higher priority to apply after Lithium mixin.world.inline_height.WorldMixin
|
||||
@Mixin(value = Level.class, priority = 1100)
|
||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Unique
|
||||
private int minY;
|
||||
|
||||
@Unique
|
||||
private int height;
|
||||
|
||||
@Unique
|
||||
private int maxY;
|
||||
|
||||
@Unique
|
||||
private int minSectionY;
|
||||
|
||||
@Unique
|
||||
private int maxSectionY;
|
||||
|
||||
@Unique
|
||||
private int sectionsCount;
|
||||
|
||||
/**
|
||||
* @reason Init min/max section
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "CTOR_HEAD"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci,
|
||||
@Local(ordinal = 0, argsOnly = true) final Holder<DimensionType> dimensionTypeHolder) {
|
||||
final DimensionType dimType = dimensionTypeHolder.value();
|
||||
this.minY = dimType.minY();
|
||||
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
|
||||
public int getMinY() {
|
||||
return this.minY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(EntityType.class)
|
||||
abstract class EntityTypeMixin implements MobSpawningEntityType {
|
||||
|
||||
@Unique
|
||||
private boolean hasBiomeCost = false;
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$hasAnyBiomeCost() {
|
||||
return this.hasBiomeCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$setHasBiomeCost() {
|
||||
this.hasBiomeCost = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LocalMobCapCalculator;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LocalMobCapCalculator.MobCounts.class)
|
||||
abstract class LocalMobCapCalculator$MobCountsMixin {
|
||||
|
||||
@Shadow
|
||||
@Mutable
|
||||
@Final
|
||||
private Object2IntMap<MobCategory> counts;
|
||||
|
||||
@Unique
|
||||
private static final MobCategory[] CATEGORIES = MobCategory.values();
|
||||
|
||||
@Unique
|
||||
private static final Object2IntOpenHashMap<?> DUMMY = new Object2IntOpenHashMap<>();
|
||||
|
||||
@Unique
|
||||
private final int[] newCounts = new int[CATEGORIES.length];
|
||||
|
||||
/**
|
||||
* @reason Ensure accesses of old field blow up
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void destroyField(final CallbackInfo ci) {
|
||||
this.counts = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid allocating the map, we null it later
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "(I)Lit/unimi/dsi/fastutil/objects/Object2IntOpenHashMap;"
|
||||
)
|
||||
)
|
||||
private <T> Object2IntOpenHashMap<T> avoidAllocation(final int expected) {
|
||||
return (Object2IntOpenHashMap<T>)DUMMY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Do not allocate MobCategory[]
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/MobCategory;values()[Lnet/minecraft/world/entity/MobCategory;"
|
||||
)
|
||||
)
|
||||
private MobCategory[] useCachedArray() {
|
||||
return CATEGORIES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use simple array instead of compute call
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void add(final MobCategory category) {
|
||||
++this.newCounts[category.ordinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use simple array instead of map get call
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean canSpawn(final MobCategory category) {
|
||||
return this.newCounts[category.ordinal()] < category.getMaxInstancesPerChunk();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(MobSpawnSettings.class)
|
||||
abstract class MobSpawnSettingsMixin {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<EntityType<?>, MobSpawnSettings.MobSpawnCost> mobSpawnCosts;
|
||||
|
||||
/**
|
||||
* @reason Set biome cost flag for any EntityType which has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void initBiomeCost(final CallbackInfo ci) {
|
||||
for (final EntityType<?> type : this.mobSpawnCosts.keySet()) {
|
||||
((MobSpawningEntityType)type).moonrise$setHasBiomeCost();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.NaturalSpawner;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(NaturalSpawner.class)
|
||||
abstract class NaturalSpawnerMixin {
|
||||
|
||||
@Shadow
|
||||
static Biome getRoughBiome(final BlockPos arg, final ChunkAccess arg2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Delay until we determine if the entity type even has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/NaturalSpawner;getRoughBiome(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/chunk/ChunkAccess;)Lnet/minecraft/world/level/biome/Biome;"
|
||||
)
|
||||
)
|
||||
private static Biome delayRoughBiome(final BlockPos pos, final ChunkAccess chunk) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Delay until we determine if the entity type even has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/biome/Biome;getMobSettings()Lnet/minecraft/world/level/biome/MobSpawnSettings;"
|
||||
)
|
||||
)
|
||||
private static MobSpawnSettings delayMobSpawnSettings(final Biome biome) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid looking up biomes for mobs which have no cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/biome/MobSpawnSettings;getMobSpawnCost(Lnet/minecraft/world/entity/EntityType;)Lnet/minecraft/world/level/biome/MobSpawnSettings$MobSpawnCost;"
|
||||
)
|
||||
)
|
||||
private static MobSpawnSettings.MobSpawnCost avoidBiomeLookupIfPossible(final MobSpawnSettings isNull,
|
||||
final EntityType<?> type,
|
||||
@Local(ordinal = 0, argsOnly = true) final BlockPos pos,
|
||||
@Local(ordinal = 0, argsOnly = true) final LevelChunk chunk) {
|
||||
if (!((MobSpawningEntityType)type).moonrise$hasAnyBiomeCost()) {
|
||||
// if the type has no associated cost with any biome, then no point in looking
|
||||
return null;
|
||||
}
|
||||
|
||||
return getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(type);
|
||||
}
|
||||
}
|
||||
@@ -20,16 +20,17 @@ import org.spongepowered.asm.mixin.Overwrite;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@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) {
|
||||
super(simpleRegionStorage, function, function2, registryAccess, chunkIOErrorReporter, 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, codec, function, biFunction, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,7 @@ abstract class MinecraftMixin extends ReentrantBlockableEventLoop<Runnable> impl
|
||||
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 {
|
||||
|
||||
@Shadow
|
||||
protected abstract float getHeightAdjustedTemperature(BlockPos blockPos);
|
||||
protected abstract float getHeightAdjustedTemperature(BlockPos blockPos, int seaLevel);
|
||||
|
||||
/**
|
||||
* @reason Cache appears ineffective
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public float getTemperature(final BlockPos pos) {
|
||||
return this.getHeightAdjustedTemperature(pos);
|
||||
public float getTemperature(final BlockPos pos, final int seaLevel) {
|
||||
return this.getHeightAdjustedTemperature(pos, seaLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.random_ticking;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
@@ -12,7 +12,6 @@ import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
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.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
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) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
|
||||
protected ServerLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
|
||||
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -85,11 +83,11 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
||||
final int offsetY = (sectionIndex + minSection) << 4;
|
||||
final LevelChunkSection section = sections[sectionIndex];
|
||||
final PalettedContainer<BlockState> states = section.states;
|
||||
if (section == null || !section.isRandomlyTickingBlocks()) {
|
||||
if (!section.isRandomlyTickingBlocks()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final IntList tickList = ((BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
||||
final ShortList tickList = ((BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
||||
|
||||
for (int i = 0; i < tickSpeed; ++i) {
|
||||
final int tickingBlocks = tickList.size();
|
||||
@@ -100,7 +98,7 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int location = tickList.getRaw(index);
|
||||
final int location = (int)tickList.getRaw(index) & 0xFFFF;
|
||||
final BlockState state = states.get(location);
|
||||
|
||||
// do not use a mutable pos, as some random tick implementations store the input without calling immutable()!
|
||||
|
||||
@@ -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
|
||||
* on the status list
|
||||
* on the status list
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@ModifyConstant(
|
||||
|
||||
@@ -19,6 +19,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||
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
|
||||
@Final
|
||||
private boolean useShapeForLightOcclusion;
|
||||
@@ -27,29 +31,26 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
@Final
|
||||
private boolean canOcclude;
|
||||
|
||||
@Shadow
|
||||
protected BlockBehaviour.BlockStateBase.Cache cache;
|
||||
|
||||
@Unique
|
||||
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
|
||||
public final boolean starlight$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.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.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
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.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -18,8 +23,8 @@ abstract class ImposterProtoChunkMixin extends ProtoChunk implements StarlightCh
|
||||
@Shadow
|
||||
private LevelChunk wrapped;
|
||||
|
||||
public ImposterProtoChunkMixin(final LevelChunk levelChunk, final boolean bl) {
|
||||
super(levelChunk.getPos(), UpgradeData.EMPTY, levelChunk, levelChunk.getLevel().registryAccess().registryOrThrow(Registries.BIOME), levelChunk.getBlendingData());
|
||||
public ImposterProtoChunkMixin(final ChunkPos chunkPos, final UpgradeData upgradeData, final LevelHeightAccessor levelHeightAccessor, final Registry<Biome> registry, @Nullable final BlendingData blendingData) {
|
||||
super(chunkPos, upgradeData, levelHeightAccessor, registry, blendingData);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -54,7 +54,24 @@ abstract class LevelLightEngineMixin implements LightEventListener, StarLightLig
|
||||
* TODO since this is a constructor inject, check on update for new constructors
|
||||
*/
|
||||
@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,
|
||||
final CallbackInfo ci) {
|
||||
@@ -179,8 +196,10 @@ abstract class LevelLightEngineMixin implements LightEventListener, StarLightLig
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean lightOnInSection(final SectionPos pos) {
|
||||
final long key = CoordinateUtils.getChunkKey(pos.getX(), pos.getZ());
|
||||
public boolean lightOnInColumn(final long pos) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,12 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
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.ServerPlayer;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.util.thread.ConsecutiveExecutor;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
@@ -50,10 +49,10 @@ import java.util.function.Supplier;
|
||||
abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider {
|
||||
|
||||
@Shadow
|
||||
private ProcessorMailbox<Runnable> taskMailbox;
|
||||
private ConsecutiveExecutor consecutiveExecutor;
|
||||
|
||||
@Shadow
|
||||
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> sorterMailbox;
|
||||
private ChunkTaskDispatcher taskDispatcher;
|
||||
|
||||
public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) {
|
||||
super(chunkProvider, hasBlockLight, hasSkyLight);
|
||||
@@ -184,8 +183,8 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
|
||||
)
|
||||
)
|
||||
private void initHook(final CallbackInfo ci) {
|
||||
this.taskMailbox = null;
|
||||
this.sorterMailbox = null;
|
||||
this.consecutiveExecutor = null;
|
||||
this.taskDispatcher = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +193,7 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
|
||||
*/
|
||||
@Overwrite
|
||||
public void addTask(final int x, final int z, final ThreadedLevelLightEngine.TaskType type,
|
||||
final Runnable task) {
|
||||
final Runnable task) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
|
||||
*/
|
||||
|
||||
@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
|
||||
protected abstract void enableChunkLight(final LevelChunk levelChunk, final int chunkX, final int chunkZ);
|
||||
@@ -127,7 +128,7 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
|
||||
// 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,272 @@
|
||||
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.slf4j.Logger;
|
||||
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;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static Logger LOGGER;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
if (!this.lightCorrect) {
|
||||
((StarlightChunk)ret).starlight$setBlockNibbles(blockNibbles);
|
||||
((StarlightChunk)ret).starlight$setSkyNibbles(skyNibbles);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
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);
|
||||
} catch (final Throwable thr) {
|
||||
ret.setLightCorrect(false);
|
||||
|
||||
LOGGER.error("Failed to parse light data for chunk " + ret.getPos() + " in world '" + WorldUtil.getWorldName(world) + "'", thr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,17 @@ abstract class PalettedContainerMixin {
|
||||
* @author jpenilla
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>*",
|
||||
// cannot use `<init>*` due to https://github.com/FabricMC/tiny-remapper/issues/137
|
||||
method = {
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V"
|
||||
},
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "Lnet/minecraft/util/ThreadingDetector;"
|
||||
)
|
||||
),
|
||||
require = 3 // Require matching all 3 constructors
|
||||
)
|
||||
private static ThreadingDetector threadingDetector(final String name) {
|
||||
return THREADING_DETECTOR;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user