Compare commits
67 Commits
v0.1.0-bet
...
v0.1.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68ea18bc51 | ||
|
|
9944946b29 | ||
|
|
ec1120ed10 | ||
|
|
8040c7a264 | ||
|
|
a9e36795e5 | ||
|
|
a70073ae3e | ||
|
|
6724814c02 | ||
|
|
f32a08738e | ||
|
|
56e48ed069 | ||
|
|
9da99576a6 | ||
|
|
dfbe1bcf8b | ||
|
|
ae29196221 | ||
|
|
f1eb61cc51 | ||
|
|
58c933938f | ||
|
|
1dc3cb5f14 | ||
|
|
2acfc6a68e | ||
|
|
c22538c364 | ||
|
|
e244c60375 | ||
|
|
65253d5199 | ||
|
|
6e82b034b7 | ||
|
|
a7cd78e63b | ||
|
|
05ba7066d9 | ||
|
|
340ac4e8f5 | ||
|
|
a4770aca2b | ||
|
|
a8d4ce526b | ||
|
|
2acd5cc213 | ||
|
|
07dce0ffe6 | ||
|
|
c2e2f0b9f2 | ||
|
|
1374ea34ee | ||
|
|
fef4872a3a | ||
|
|
a9678c76eb | ||
|
|
bafa195546 | ||
|
|
04c6e6c01a | ||
|
|
7351872730 | ||
|
|
77b977ac17 | ||
|
|
f0d122f358 | ||
|
|
aa240eb16d | ||
|
|
f9c99d1e32 | ||
|
|
3fa0ff67a7 | ||
|
|
f7d9e5b422 | ||
|
|
5d1975154b | ||
|
|
d21fa9f48c | ||
|
|
1c150afe83 | ||
|
|
d9988c86f4 | ||
|
|
0958439e54 | ||
|
|
8d9d6488e8 | ||
|
|
8ba83bc9c2 | ||
|
|
0c60a6ac08 | ||
|
|
d404dbc6c5 | ||
|
|
8d456890f1 | ||
|
|
23eddfe918 | ||
|
|
55ff406372 | ||
|
|
3e25a2f4aa | ||
|
|
cfa80c4488 | ||
|
|
453b635ef2 | ||
|
|
e07e4fdcc4 | ||
|
|
81bb9701da | ||
|
|
d0a7f9af62 | ||
|
|
fe7bcfc56a | ||
|
|
44f8058b09 | ||
|
|
d1f8e81913 | ||
|
|
dcef1320b2 | ||
|
|
179b45cac5 | ||
|
|
89e9cd52e4 | ||
|
|
38c3f5fd80 | ||
|
|
98e8a08e85 | ||
|
|
caf2960b6b |
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
|
||||
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -35,7 +35,14 @@ jobs:
|
||||
run: ./installConcurrentUtil.sh
|
||||
- name: "execute gradle build"
|
||||
run: ./gradlew build
|
||||
- name: Publish (Pre-)Release to Modrinth
|
||||
- name: Determine Snapshot Status
|
||||
run: |
|
||||
if [ "$(./gradlew properties | awk '/^version:/ { print $2; }' | grep '\-SNAPSHOT')" ]; then
|
||||
echo "STATUS=snapshot" >> $GITHUB_ENV
|
||||
else
|
||||
echo "STATUS=release" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Publish (Pre-)Release to Modrinth & CurseForge
|
||||
if: "${{ env.STATUS == 'release' && github.event_name == 'release' }}"
|
||||
run: ./gradlew :moonrise-fabric:publishMods :moonrise-neoforge:publishMods
|
||||
env:
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
31
build.gradle
31
build.gradle
@@ -112,18 +112,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")
|
||||
@@ -25,6 +25,7 @@ dependencies {
|
||||
modImplementation "com.terraformersmc:modmenu:11.0.1"
|
||||
|
||||
modImplementation fabricApiLibs.command.api.v2
|
||||
modImplementation fabricApiLibs.lifecycle.events.v1
|
||||
include fabricApiLibs.command.api.v2
|
||||
include fabricApiLibs.base
|
||||
}
|
||||
@@ -63,22 +64,3 @@ publishMods {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a run with lithium for compatibility testing
|
||||
sourceSets.create("lithium")
|
||||
configurations.create("lithium")
|
||||
loom {
|
||||
createRemapConfigurations(sourceSets.lithium)
|
||||
runs {
|
||||
register("lithiumClient") {
|
||||
client()
|
||||
property "mixin.debug", "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||
}
|
||||
dependencies {
|
||||
modLithiumRuntimeOnly "maven.modrinth:lithium:${rootProject.lithium_version}"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ package ca.spottedleaf.moonrise.fabric;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
@@ -15,6 +19,7 @@ import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
@@ -25,6 +30,21 @@ import java.util.function.Predicate;
|
||||
|
||||
public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
|
||||
|
||||
public interface OnExplosionDetonate {
|
||||
void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
|
||||
}
|
||||
|
||||
public static final Event<OnExplosionDetonate> ON_EXPLOSION_DETONATE = EventFactory.createArrayBacked(
|
||||
OnExplosionDetonate.class,
|
||||
listeners -> (final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) -> {
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
listeners[i].onExplosion(world, explosion, possiblyAffecting, diameter);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public String getBrand() {
|
||||
return "Moonrise";
|
||||
@@ -44,7 +64,7 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
|
||||
|
||||
ON_EXPLOSION_DETONATE.invoker().onExplosion(world, explosion, possiblyAffecting, diameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,7 +89,12 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
|
||||
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel) newChunk.getLevel(), newChunk);
|
||||
if (!(original instanceof ImposterProtoChunk)) {
|
||||
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,7 +109,9 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk) {
|
||||
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel) chunk.getLevel(), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package ca.spottedleaf.moonrise.fabric.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
abstract class EntityMixin {
|
||||
|
||||
@Shadow
|
||||
public abstract boolean touchingUnloadedChunk();
|
||||
|
||||
@Shadow
|
||||
public abstract AABB getBoundingBox();
|
||||
|
||||
@Shadow
|
||||
@Deprecated
|
||||
public abstract boolean isPushedByFluid();
|
||||
|
||||
@Shadow
|
||||
private Level level;
|
||||
|
||||
@Shadow
|
||||
@Deprecated
|
||||
protected Object2DoubleMap<TagKey<Fluid>> fluidHeight;
|
||||
|
||||
@Shadow
|
||||
public abstract Vec3 getDeltaMovement();
|
||||
|
||||
@Shadow
|
||||
public abstract void setDeltaMovement(final Vec3 arg);
|
||||
|
||||
/**
|
||||
* @reason Optimise the block reading in this function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, final double flowScale) {
|
||||
if (this.touchingUnloadedChunk()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||
|
||||
final Level world = this.level;
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
|
||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||
|
||||
final boolean isPushable = this.isPushedByFluid();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
Vec3 pushVector = Vec3.ZERO;
|
||||
double totalPushes = 0.0;
|
||||
double maxHeightDiff = 0.0;
|
||||
boolean inFluid = false;
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
|
||||
|
||||
// bound y
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
|
||||
|
||||
if (fluidState.isEmpty() || !fluidState.is(fluid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
|
||||
|
||||
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
|
||||
final double diff = height - boundingBox.minY;
|
||||
|
||||
if (diff < 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inFluid = true;
|
||||
maxHeightDiff = Math.max(maxHeightDiff, diff);
|
||||
|
||||
if (!isPushable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++totalPushes;
|
||||
|
||||
final Vec3 flow = fluidState.getFlow(world, mutablePos);
|
||||
|
||||
if (diff < 0.4) {
|
||||
pushVector = pushVector.add(flow.scale(diff));
|
||||
} else {
|
||||
pushVector = pushVector.add(flow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.fluidHeight.put(fluid, maxHeightDiff);
|
||||
|
||||
if (pushVector.lengthSqr() == 0.0) {
|
||||
return inFluid;
|
||||
}
|
||||
|
||||
// note: totalPushes != 0 as pushVector != 0
|
||||
pushVector = pushVector.scale(1.0 / totalPushes);
|
||||
final Vec3 currMovement = this.getDeltaMovement();
|
||||
|
||||
if (!((Entity)(Object)this instanceof Player)) {
|
||||
pushVector = pushVector.normalize();
|
||||
}
|
||||
|
||||
pushVector = pushVector.scale(flowScale);
|
||||
if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
|
||||
pushVector = pushVector.normalize().scale(0.0045000000000000005);
|
||||
}
|
||||
|
||||
this.setDeltaMovement(currMovement.add(pushVector));
|
||||
|
||||
// note: inFluid = true here as pushVector != 0
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -41,30 +41,34 @@
|
||||
},
|
||||
"custom": {
|
||||
"lithium:options": {
|
||||
"mixin.collections.chunk_tickets": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.collections.entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.ai.poi": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.world.tick_scheduler": false,
|
||||
"mixin.alloc.chunk_ticking": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.alloc.entity_tracker": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.chunk.entity_class_groups": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.collections.chunk_tickets": false,
|
||||
"mixin.collections.entity_ticking": false,
|
||||
"mixin.entity.collisions.intersection": false,
|
||||
"mixin.entity.collisions.movement": false,
|
||||
"mixin.entity.collisions.unpushable_cramming": false,
|
||||
"mixin.chunk.entity_class_groups": false
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.util.entity_movement_tracking": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.world.explosions.block_raycast": false,
|
||||
"mixin.world.explosions.cache_exposure": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.world.tick_scheduler": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"mixins": [
|
||||
"chunk_system.FabricDistanceManagerMixin",
|
||||
"chunk_system.FabricMinecraftServerMixin",
|
||||
"chunk_system.FabricServerLevelMixin"
|
||||
"chunk_system.FabricServerLevelMixin",
|
||||
"collisions.EntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"command.ClientSuggestionProviderMixin"
|
||||
|
||||
@@ -4,13 +4,16 @@ 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
|
||||
supported_minecraft_versions=1.21,1.21.1
|
||||
loader_version=0.16.5
|
||||
neoforge_version=21.1.62
|
||||
snakeyaml_version=2.2
|
||||
concurrentutil_version=0.0.2-SNAPSHOT
|
||||
cloth_version=15.0.128
|
||||
lithium_version=mc1.21.1-0.13.0
|
||||
# version ids from modrinth
|
||||
fabric_lithium_version=mc1.21.1-0.14.0-beta.1
|
||||
neo_lithium_version=BrMIoIMv
|
||||
# Mod Properties
|
||||
mod_version=0.1.0-beta.0
|
||||
mod_version=0.1.0-beta.8
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -36,20 +36,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 +50,33 @@ config = "moonrise.mixins.json"
|
||||
|
||||
[[mixins]]
|
||||
config = "moonrise-neoforge.mixins.json"
|
||||
|
||||
[mods."lithium:options"]
|
||||
"mixin.ai.poi" = false
|
||||
"mixin.alloc.chunk_ticking" = false
|
||||
"mixin.alloc.deep_passengers" = false
|
||||
"mixin.alloc.entity_tracker" = false
|
||||
"mixin.block.flatten_states" = false
|
||||
"mixin.chunk.entity_class_groups" = false
|
||||
"mixin.chunk.no_validation" = false
|
||||
"mixin.collections.chunk_tickets" = false
|
||||
"mixin.collections.entity_ticking" = false
|
||||
"mixin.entity.collisions.intersection" = false
|
||||
"mixin.entity.collisions.movement" = false
|
||||
"mixin.entity.collisions.unpushable_cramming" = false
|
||||
"mixin.entity.replace_entitytype_predicates" = false
|
||||
"mixin.math.fast_blockpos" = false
|
||||
"mixin.math.fast_util" = false
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space" = false
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets" = false
|
||||
"mixin.shapes.blockstate_cache" = false
|
||||
"mixin.shapes.optimized_matching" = false
|
||||
"mixin.shapes.specialized_shapes" = false
|
||||
"mixin.util.block_tracking" = false
|
||||
"mixin.util.entity_movement_tracking" = false
|
||||
"mixin.world.block_entity_ticking" = false
|
||||
"mixin.world.chunk_access" = false
|
||||
"mixin.world.explosions.block_raycast" = false
|
||||
"mixin.world.explosions.cache_exposure" = false
|
||||
"mixin.world.temperature_cache" = false
|
||||
"mixin.world.tick_scheduler" = false
|
||||
|
||||
@@ -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.295" 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:0.107.0+1.21.1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
@@ -121,12 +122,14 @@ public final class ChunkSystem {
|
||||
}
|
||||
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
|
||||
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
|
||||
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$markChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove(
|
||||
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
||||
);
|
||||
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$removeChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
||||
}
|
||||
|
||||
public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
||||
|
||||
@@ -35,6 +35,7 @@ public final class ConfigHolder {
|
||||
program arguments.
|
||||
-DMoonrise.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
|
||||
-DMoonrise.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
|
||||
-DMoonrise.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
|
||||
""";
|
||||
|
||||
static {
|
||||
|
||||
@@ -2,7 +2,7 @@ package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
public final class MoonriseConstants {
|
||||
|
||||
public static final int MAX_VIEW_DISTANCE = 32;
|
||||
public static final int MAX_VIEW_DISTANCE = Integer.getInteger("Moonrise.MaxViewDistance", 32);
|
||||
|
||||
private MoonriseConstants() {}
|
||||
|
||||
|
||||
@@ -11,10 +11,18 @@ public final class WorldUtil {
|
||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
||||
}
|
||||
|
||||
public static int getMaxSection(final Level world) {
|
||||
return world.getMaxSection() - 1; // getMaxSection() is exclusive
|
||||
}
|
||||
|
||||
public static int getMinSection(final LevelHeightAccessor world) {
|
||||
return world.getMinSection();
|
||||
}
|
||||
|
||||
public static int getMinSection(final Level world) {
|
||||
return world.getMinSection();
|
||||
}
|
||||
|
||||
public static int getMaxLightSection(final LevelHeightAccessor world) {
|
||||
return getMaxSection(world) + 1;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -18,15 +19,15 @@ interface BitStorageMixin extends BlockCountingBitStorage {
|
||||
|
||||
// provide default impl in case mods implement this...
|
||||
@Override
|
||||
public default Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>();
|
||||
public default Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
final int size = this.getSize();
|
||||
for (int index = 0; index < size; ++index) {
|
||||
final int paletteIdx = this.get(index);
|
||||
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
||||
return new IntArrayList();
|
||||
}).add(index);
|
||||
return new ShortArrayList(64);
|
||||
}).add((short)index);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
@@ -48,26 +48,32 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
public abstract boolean maybeHas(Predicate<BlockState> predicate);
|
||||
|
||||
@Unique
|
||||
private static final IntArrayList FULL_LIST = new IntArrayList(16*16*16);
|
||||
private static final ShortArrayList FULL_LIST = new ShortArrayList(16*16*16);
|
||||
static {
|
||||
for (int i = 0; i < (16*16*16); ++i) {
|
||||
for (short i = 0; i < (16*16*16); ++i) {
|
||||
FULL_LIST.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private int specialCollidingBlocks;
|
||||
private boolean isClient;
|
||||
|
||||
@Unique
|
||||
private final IntList tickingBlocks = new IntList();
|
||||
private static final short CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS = (short)9999;
|
||||
|
||||
@Unique
|
||||
private short specialCollidingBlocks;
|
||||
|
||||
@Unique
|
||||
private final ShortList tickingBlocks = new ShortList();
|
||||
|
||||
@Override
|
||||
public final int moonrise$getSpecialCollidingBlocks() {
|
||||
return this.specialCollidingBlocks;
|
||||
public final boolean moonrise$hasSpecialCollidingBlocks() {
|
||||
return this.specialCollidingBlocks != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IntList moonrise$getTickingBlockList() {
|
||||
public final ShortList moonrise$getTickingBlockList() {
|
||||
return this.tickingBlocks;
|
||||
}
|
||||
|
||||
@@ -86,20 +92,35 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
if (oldState == newState) {
|
||||
return;
|
||||
}
|
||||
if (CollisionUtil.isSpecialCollidingBlock(oldState)) {
|
||||
--this.specialCollidingBlocks;
|
||||
}
|
||||
|
||||
if (this.isClient) {
|
||||
if (CollisionUtil.isSpecialCollidingBlock(newState)) {
|
||||
this.specialCollidingBlocks = CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isSpecialOld = CollisionUtil.isSpecialCollidingBlock(oldState);
|
||||
final boolean isSpecialNew = CollisionUtil.isSpecialCollidingBlock(newState);
|
||||
if (isSpecialOld != isSpecialNew) {
|
||||
if (isSpecialOld) {
|
||||
--this.specialCollidingBlocks;
|
||||
} else {
|
||||
++this.specialCollidingBlocks;
|
||||
}
|
||||
|
||||
final int position = x | (z << 4) | (y << (4+4));
|
||||
|
||||
if (oldState.isRandomlyTicking()) {
|
||||
this.tickingBlocks.remove(position);
|
||||
}
|
||||
if (newState.isRandomlyTicking()) {
|
||||
this.tickingBlocks.add(position);
|
||||
|
||||
final boolean oldTicking = oldState.isRandomlyTicking();
|
||||
final boolean newTicking = newState.isRandomlyTicking();
|
||||
if (oldTicking != newTicking) {
|
||||
final ShortList tickingBlocks = this.tickingBlocks;
|
||||
final short position = (short)(x | (z << 4) | (y << (4+4)));
|
||||
|
||||
if (oldTicking) {
|
||||
tickingBlocks.remove(position);
|
||||
} else {
|
||||
tickingBlocks.add(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +158,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
final int paletteSize = palette.getSize();
|
||||
final BitStorage storage = data.storage;
|
||||
|
||||
final Int2ObjectOpenHashMap<IntArrayList> counts;
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> counts;
|
||||
if (paletteSize == 1) {
|
||||
counts = new Int2ObjectOpenHashMap<>(1);
|
||||
counts.put(0, FULL_LIST);
|
||||
@@ -145,10 +166,10 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
counts = ((BlockCountingBitStorage)storage).moonrise$countEntries();
|
||||
}
|
||||
|
||||
for (final Iterator<Int2ObjectMap.Entry<IntArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Int2ObjectMap.Entry<IntArrayList> entry = iterator.next();
|
||||
for (final Iterator<Int2ObjectMap.Entry<ShortArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
||||
final Int2ObjectMap.Entry<ShortArrayList> entry = iterator.next();
|
||||
final int paletteIdx = entry.getIntKey();
|
||||
final IntArrayList coordinates = entry.getValue();
|
||||
final ShortArrayList coordinates = entry.getValue();
|
||||
final int paletteCount = coordinates.size();
|
||||
|
||||
final BlockState state = palette.valueFor(paletteIdx);
|
||||
@@ -158,16 +179,21 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
}
|
||||
|
||||
if (CollisionUtil.isSpecialCollidingBlock(state)) {
|
||||
this.specialCollidingBlocks += paletteCount;
|
||||
this.specialCollidingBlocks += (short)paletteCount;
|
||||
}
|
||||
this.nonEmptyBlockCount += paletteCount;
|
||||
this.nonEmptyBlockCount += (short)paletteCount;
|
||||
if (state.isRandomlyTicking()) {
|
||||
this.tickingBlockCount += paletteCount;
|
||||
final int[] raw = coordinates.elements();
|
||||
this.tickingBlockCount += (short)paletteCount;
|
||||
final short[] raw = coordinates.elements();
|
||||
final int rawLen = raw.length;
|
||||
|
||||
Objects.checkFromToIndex(0, paletteCount, raw.length);
|
||||
final ShortList tickingBlocks = this.tickingBlocks;
|
||||
|
||||
tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16));
|
||||
|
||||
Objects.checkFromToIndex(0, paletteCount, rawLen);
|
||||
for (int i = 0; i < paletteCount; ++i) {
|
||||
this.tickingBlocks.add(raw[i]);
|
||||
tickingBlocks.add(raw[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +202,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
if (!fluid.isEmpty()) {
|
||||
//this.nonEmptyBlockCount += count; // fix vanilla bug: make non-empty block count correct
|
||||
if (fluid.isRandomlyTicking()) {
|
||||
this.tickingFluidCount += paletteCount;
|
||||
this.tickingFluidCount += (short)paletteCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +210,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Call recalcBlockCounts on the client, as the client does not invoke it when deserializing chunk sections.
|
||||
* @reason Set up special colliding blocks on the client, as it is too expensive to perform a full calculation
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
@@ -194,6 +220,8 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
|
||||
)
|
||||
)
|
||||
private void callRecalcBlocksClient(final CallbackInfo ci) {
|
||||
this.recalcBlockCounts();
|
||||
this.isClient = true;
|
||||
// force has special colliding blocks to be true
|
||||
this.specialCollidingBlocks = this.nonEmptyBlockCount != (short)0 && this.maybeHas(CollisionUtil::isSpecialCollidingBlock) ? CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS : (short)0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -24,24 +25,48 @@ abstract class SimpleBitStorageMixin implements BitStorage, BlockCountingBitStor
|
||||
@Final
|
||||
private int bits;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private long mask;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private int size;
|
||||
|
||||
@Override
|
||||
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final int valuesPerLong = this.valuesPerLong;
|
||||
final int bits = this.bits;
|
||||
final long mask = this.mask;
|
||||
final long mask = (1L << bits) - 1L;
|
||||
final int size = this.size;
|
||||
|
||||
// we may be backed by global palette, so limit bits for init capacity
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(
|
||||
1 << Math.min(6, bits)
|
||||
if (bits <= 6) {
|
||||
final ShortArrayList[] byId = new ShortArrayList[1 << bits];
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1 << bits);
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (long value : this.data) {
|
||||
int li = 0;
|
||||
do {
|
||||
final int paletteIdx = (int)(value & mask);
|
||||
value >>= bits;
|
||||
++li;
|
||||
|
||||
final ShortArrayList coords = byId[paletteIdx];
|
||||
if (coords != null) {
|
||||
coords.add((short)index++);
|
||||
continue;
|
||||
} else {
|
||||
final ShortArrayList newCoords = new ShortArrayList(64);
|
||||
byId[paletteIdx] = newCoords;
|
||||
newCoords.add((short)index++);
|
||||
ret.put(paletteIdx, newCoords);
|
||||
continue;
|
||||
}
|
||||
} while (li < valuesPerLong && index < size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(
|
||||
1 << 6
|
||||
);
|
||||
|
||||
int index = 0;
|
||||
@@ -51,16 +76,15 @@ abstract class SimpleBitStorageMixin implements BitStorage, BlockCountingBitStor
|
||||
do {
|
||||
final int paletteIdx = (int)(value & mask);
|
||||
value >>= bits;
|
||||
++li;
|
||||
|
||||
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
||||
return new IntArrayList();
|
||||
}).add(index);
|
||||
|
||||
++li;
|
||||
++index;
|
||||
return new ShortArrayList(64);
|
||||
}).add((short)index++);
|
||||
} while (li < valuesPerLong && index < size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.ZeroBitStorage;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -17,17 +18,17 @@ abstract class ZeroBitStorageMixin implements BitStorage, BlockCountingBitStorag
|
||||
private int size;
|
||||
|
||||
@Override
|
||||
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
|
||||
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
|
||||
final int size = this.size;
|
||||
|
||||
final int[] raw = new int[size];
|
||||
final short[] raw = new short[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
raw[i] = i;
|
||||
raw[i] = (short)i;
|
||||
}
|
||||
|
||||
final IntArrayList coordinates = IntArrayList.wrap(raw, size);
|
||||
final ShortArrayList coordinates = ShortArrayList.wrap(raw, size);
|
||||
|
||||
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(1);
|
||||
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1);
|
||||
ret.put(0, coordinates);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,9 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
@Unique
|
||||
private final ReferenceList<ServerPlayer> playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY);
|
||||
|
||||
@Unique
|
||||
private boolean isMarkedDirtyForPlayers;
|
||||
|
||||
@Unique
|
||||
private ChunkMap getChunkMap() {
|
||||
return (ChunkMap)this.playerProvider;
|
||||
@@ -120,6 +123,16 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$isMarkedDirtyForPlayers() {
|
||||
return this.isMarkedDirtyForPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$markDirtyForPlayers(final boolean value) {
|
||||
this.isMarkedDirtyForPlayers = value;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static final ServerPlayer[] EMPTY_PLAYER_ARRAY = new ServerPlayer[0];
|
||||
|
||||
@@ -287,7 +300,14 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
// no players to sent to, so don't need to update anything
|
||||
return null;
|
||||
}
|
||||
return this.getChunkToSend();
|
||||
final LevelChunk ret = this.getChunkToSend();
|
||||
|
||||
if (ret != null) {
|
||||
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,7 +322,18 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
|
||||
)
|
||||
)
|
||||
private LevelChunk redirectLightUpdate(final ChunkHolder instance) {
|
||||
return this.getChunkToSend();
|
||||
if (this.playersSentChunkTo.size() == 0) {
|
||||
// no players to sent to, so don't need to update anything
|
||||
return null;
|
||||
}
|
||||
final LevelChunk ret = this.getChunkToSend();
|
||||
|
||||
if (ret != null) {
|
||||
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,6 +63,21 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
|
||||
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ClientChunkCache chunkSource = this.chunkSource;
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Redirect to new entity manager
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -49,6 +50,9 @@ abstract class EntityMixin implements ChunkSystemEntity {
|
||||
@Unique
|
||||
private FullChunkStatus chunkStatus;
|
||||
|
||||
@Unique
|
||||
private ChunkData chunkData;
|
||||
|
||||
@Unique
|
||||
private int sectionX = Integer.MIN_VALUE;
|
||||
|
||||
@@ -76,6 +80,16 @@ abstract class EntityMixin implements ChunkSystemEntity {
|
||||
this.chunkStatus = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData() {
|
||||
return this.chunkData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$setChunkData(final ChunkData chunkData) {
|
||||
this.chunkData = chunkData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getSectionX() {
|
||||
return this.sectionX;
|
||||
|
||||
@@ -99,8 +99,18 @@ abstract class EntityTickListMixin {
|
||||
* @reason Route to new entity list
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void forEach(final Consumer<Entity> action) {
|
||||
@Inject(
|
||||
method = "forEach",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void injectForEach(final Consumer<Entity> consumer, final CallbackInfo ci) {
|
||||
this.forEach(consumer);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void forEach(final Consumer<Entity> action) {
|
||||
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
|
||||
// (by dfl iterator() is configured to not iterate over new entries)
|
||||
final IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = this.entities.iterator();
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup;
|
||||
@@ -13,6 +16,7 @@ import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.entity.EntityTypeTest;
|
||||
@@ -46,6 +50,9 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
@Unique
|
||||
private EntityLookup entityLookup;
|
||||
|
||||
@Unique
|
||||
private final ConcurrentLong2ReferenceChainedHashTable<ChunkData> chunkData = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
|
||||
@Override
|
||||
public final EntityLookup moonrise$getEntityLookup() {
|
||||
return this.entityLookup;
|
||||
@@ -217,6 +224,52 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
// no-op on ClientLevel
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData(final long chunkKey) {
|
||||
return this.chunkData.get(chunkKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$getChunkData(final int chunkX, final int chunkZ) {
|
||||
return this.chunkData.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$requestChunkData(final long chunkKey) {
|
||||
return this.chunkData.compute(chunkKey, (final long keyInMap, final ChunkData valueInMap) -> {
|
||||
if (valueInMap == null) {
|
||||
final ChunkData ret = new ChunkData();
|
||||
ret.increaseRef();
|
||||
return ret;
|
||||
}
|
||||
|
||||
valueInMap.increaseRef();
|
||||
return valueInMap;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ChunkData moonrise$releaseChunkData(final long chunkKey) {
|
||||
return this.chunkData.compute(chunkKey, (final long keyInMap, final ChunkData chunkData) -> {
|
||||
return chunkData.decreaseRef() == 0 ? null : chunkData;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ChunkSource chunkSource = this.getChunkSource();
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Declare method in this class so that any invocations are virtual, and not interface.
|
||||
* @author Spottedleaf
|
||||
@@ -226,6 +279,13 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
|
||||
return this.getChunkSource().hasChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChunksAt(final int minBlockX, final int minBlockZ, final int maxBlockX, final int maxBlockZ) {
|
||||
return this.moonrise$areChunksLoaded(
|
||||
minBlockX >> 4, minBlockZ >> 4, maxBlockX >> 4, maxBlockZ >> 4
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Turn all getChunk(x, z, status) calls into virtual invokes, instead of interface invokes:
|
||||
* 1. The interface invoke is expensive
|
||||
|
||||
@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
@@ -33,6 +34,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -282,7 +285,8 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
*/
|
||||
@Overwrite
|
||||
public void getFullChunk(final long pos, final Consumer<LevelChunk> consumer) {
|
||||
final LevelChunk fullChunk = this.getChunkNow(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
|
||||
// note: bypass currentlyLoaded from getChunkNow
|
||||
final LevelChunk fullChunk = this.fullChunks.get(pos);
|
||||
if (fullChunk != null) {
|
||||
consumer.accept(fullChunk);
|
||||
}
|
||||
@@ -356,4 +360,35 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
||||
private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Since chunks in non-simulation range are only brought up to FULL status, not TICKING,
|
||||
* those chunks may not be present in the ticking list and as a result we need to use our own list
|
||||
* to ensure these chunks broadcast changes
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V"
|
||||
)
|
||||
)
|
||||
private void fixBroadcastChanges(final List<ServerChunkCache.ChunkAndHolder> instance,
|
||||
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
|
||||
final ReferenceList<ChunkHolder> unsyncedChunks = ((ChunkSystemServerLevel)this.level).moonrise$getUnsyncedChunks();
|
||||
final ChunkHolder[] chunkHolders = unsyncedChunks.getRawDataUnchecked();
|
||||
final int totalUnsyncedChunks = unsyncedChunks.size();
|
||||
|
||||
Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length);
|
||||
for (int i = 0; i < totalUnsyncedChunks; ++i) {
|
||||
final ChunkHolder chunkHolder = chunkHolders[i];
|
||||
final LevelChunk chunk = chunkHolder.getChunkToSend();
|
||||
if (chunk != null) {
|
||||
chunkHolder.broadcastChanges(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
((ChunkSystemServerLevel)this.level).moonrise$clearUnsyncedChunks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityData
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.PoiDataController;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
||||
@@ -26,6 +27,7 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
@@ -62,6 +64,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -120,6 +123,9 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
@Unique
|
||||
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
|
||||
|
||||
@Unique
|
||||
private static final ChunkHolder[] EMPTY_CHUNK_HOLDERS = new ChunkHolder[0];
|
||||
|
||||
@Unique
|
||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
|
||||
|
||||
@@ -129,6 +135,9 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
@Unique
|
||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
|
||||
|
||||
@Unique
|
||||
private final ReferenceList<ChunkHolder> unsyncedChunks = new ReferenceList<>(EMPTY_CHUNK_HOLDERS);
|
||||
|
||||
/**
|
||||
* @reason Initialise fields / destroy entity manager state
|
||||
* @author Spottedleaf
|
||||
@@ -345,6 +354,60 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
|
||||
return this.entityTickingChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks() {
|
||||
return this.unsyncedChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder) {
|
||||
if (((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(true);
|
||||
this.unsyncedChunks.add(chunkHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder) {
|
||||
if (!((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false);
|
||||
this.unsyncedChunks.remove(chunkHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$clearUnsyncedChunks() {
|
||||
final ChunkHolder[] chunkHolders = this.unsyncedChunks.getRawDataUnchecked();
|
||||
final int totalUnsyncedChunks = this.unsyncedChunks.size();
|
||||
|
||||
Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length);
|
||||
for (int i = 0; i < totalUnsyncedChunks; ++i) {
|
||||
final ChunkHolder chunkHolder = chunkHolders[i];
|
||||
|
||||
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false);
|
||||
}
|
||||
this.unsyncedChunks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
|
||||
final ServerChunkCache chunkSource = this.chunkSource;
|
||||
|
||||
for (int currZ = fromZ; currZ <= toZ; ++currZ) {
|
||||
for (int currX = fromX; currX <= toX; ++currX) {
|
||||
if (!chunkSource.hasChunk(currX, currZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Declare method in this class so that any invocations are virtual, and not interface.
|
||||
* @author Spottedleaf
|
||||
|
||||
@@ -33,7 +33,7 @@ abstract class ChunkMapMixin {
|
||||
public ServerLevel level;
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean playerIsCloseEnoughForSpawning(ServerPlayer serverPlayer, ChunkPos chunkPos);
|
||||
public abstract boolean playerIsCloseEnoughForSpawning(ServerPlayer serverPlayer, ChunkPos chunkPos);
|
||||
|
||||
/**
|
||||
* @reason Hook for updating the spawn tracker in distance manager. We add our own hook instead of using the
|
||||
@@ -116,15 +116,15 @@ abstract class ChunkMapMixin {
|
||||
*/
|
||||
@Overwrite
|
||||
public List<ServerPlayer> getPlayersCloseForSpawning(final ChunkPos pos) {
|
||||
final List<ServerPlayer> ret = new ArrayList<>();
|
||||
|
||||
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
|
||||
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
|
||||
);
|
||||
if (players == null) {
|
||||
return ret;
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<ServerPlayer> ret = null;
|
||||
|
||||
final ServerPlayer[] raw = players.getRawDataUnchecked();
|
||||
final int len = players.size();
|
||||
|
||||
@@ -132,10 +132,15 @@ abstract class ChunkMapMixin {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
final ServerPlayer player = raw[i];
|
||||
if (this.playerIsCloseEnoughForSpawning(player, pos)) {
|
||||
if (ret == null) {
|
||||
ret = new ArrayList<>(len - i);
|
||||
ret.add(player);
|
||||
} else {
|
||||
ret.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret == null ? new ArrayList<>() : ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -30,6 +45,12 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
@Unique
|
||||
private ServerChunkCache.ChunkAndHolder[] iterationCopy;
|
||||
|
||||
@Unique
|
||||
private int iterationCopyLen;
|
||||
|
||||
@Unique
|
||||
private final SimpleRandom shuffleRandom = new SimpleRandom(0L);
|
||||
|
||||
/**
|
||||
* @reason Avoid creating the list, which is sized at the chunkholder count. The actual number of ticking
|
||||
* chunks is always lower. The mixin below will initialise the list to non-null.
|
||||
@@ -60,7 +81,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
)
|
||||
private List<ServerChunkCache.ChunkAndHolder> initTickChunks(final List<ServerChunkCache.ChunkAndHolder> shouldBeNull) {
|
||||
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
|
||||
((ChunkSystemServerLevel)this.level).moonrise$getTickingChunks();
|
||||
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
|
||||
|
||||
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
|
||||
final int size = tickingChunks.size();
|
||||
@@ -68,6 +89,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
if (this.iterationCopy == null || this.iterationCopy.length < size) {
|
||||
this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length];
|
||||
}
|
||||
this.iterationCopyLen = size;
|
||||
System.arraycopy(raw, 0, this.iterationCopy, 0, size);
|
||||
|
||||
return ObjectArrayList.wrap(
|
||||
@@ -75,6 +97,23 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
|
||||
* function
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
|
||||
)
|
||||
)
|
||||
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
|
||||
this.shuffleRandom.setSeed(randomSource.nextLong());
|
||||
Util.shuffle(list, this.shuffleRandom);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Do not initialise ticking chunk list, as we did that above.
|
||||
* @author Spottedleaf
|
||||
@@ -92,29 +131,59 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Clear the iteration array, and at the same time broadcast chunk changes.
|
||||
* @reason Use the nearby players cache
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/level/ChunkMap;anyPlayerCloseEnoughForSpawning(Lnet/minecraft/world/level/ChunkPos;)Z"
|
||||
)
|
||||
)
|
||||
private boolean useNearbyCache(final ChunkMap instance, final ChunkPos chunkPos,
|
||||
@Local(ordinal = 0, argsOnly = false) final LevelChunk levelChunk) {
|
||||
final ChunkData chunkData =
|
||||
((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
|
||||
.moonrise$getRealChunkHolder().holderData;
|
||||
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
|
||||
if (nearbyPlayers == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ReferenceList<ServerPlayer> players = nearbyPlayers.getPlayers(NearbyPlayers.NearbyMapType.SPAWN_RANGE);
|
||||
|
||||
if (players == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ServerPlayer[] raw = players.getRawDataUnchecked();
|
||||
final int len = players.size();
|
||||
|
||||
Objects.checkFromIndexSize(0, len, raw.length);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (instance.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Clear the iteration array after the list is done being used.
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "tickChunks",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
||||
ordinal = 0
|
||||
ordinal = 0,
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
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 void broadcastChanges(final CallbackInfo ci) {
|
||||
Arrays.fill(this.iterationCopy, 0, this.iterationCopyLen, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
abstract class ServerLevelMixin implements ChunkTickServerLevel {
|
||||
|
||||
@Unique
|
||||
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_PLAYER_CHUNK_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
|
||||
|
||||
@Unique
|
||||
private final ReferenceList<ServerChunkCache.ChunkAndHolder> playerTickingChunks = new ReferenceList<>(EMPTY_PLAYER_CHUNK_HOLDERS);
|
||||
|
||||
@Unique
|
||||
private final Long2IntOpenHashMap playerTickingRequests = new Long2IntOpenHashMap();
|
||||
|
||||
@Override
|
||||
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks() {
|
||||
return this.playerTickingChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$markChunkForPlayerTicking(final LevelChunk chunk) {
|
||||
final ChunkPos pos = chunk.getPos();
|
||||
if (!this.playerTickingRequests.containsKey(CoordinateUtils.getChunkKey(pos))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.add(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removeChunkForPlayerTicking(final LevelChunk chunk) {
|
||||
this.playerTickingChunks.remove(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$addPlayerTickingRequest(final int chunkX, final int chunkZ) {
|
||||
TickThread.ensureTickThread((ServerLevel)(Object)this, chunkX, chunkZ, "Cannot add ticking request async");
|
||||
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
|
||||
if (this.playerTickingRequests.addTo(chunkKey, 1) != 0) {
|
||||
// already added
|
||||
return;
|
||||
}
|
||||
|
||||
final NewChunkHolder chunkHolder = ((ChunkSystemServerLevel)(ServerLevel)(Object)this).moonrise$getChunkTaskScheduler()
|
||||
.chunkHolderManager.getChunkHolder(chunkKey);
|
||||
|
||||
if (chunkHolder == null || !chunkHolder.isTickingReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.add(
|
||||
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$removePlayerTickingRequest(final int chunkX, final int chunkZ) {
|
||||
TickThread.ensureTickThread((ServerLevel)(Object)this, chunkX, chunkZ, "Cannot remove ticking request async");
|
||||
|
||||
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
final int val = this.playerTickingRequests.addTo(chunkKey, -1);
|
||||
|
||||
if (val <= 0) {
|
||||
throw new IllegalStateException("Negative counter");
|
||||
}
|
||||
|
||||
if (val != 1) {
|
||||
// still has at least one request
|
||||
return;
|
||||
}
|
||||
|
||||
final NewChunkHolder chunkHolder = ((ChunkSystemServerLevel)(ServerLevel)(Object)this).moonrise$getChunkTaskScheduler()
|
||||
.chunkHolderManager.getChunkHolder(chunkKey);
|
||||
|
||||
if (chunkHolder == null || !chunkHolder.isTickingReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playerTickingChunks.remove(
|
||||
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,14 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(BlockPos.class)
|
||||
abstract class BlockPosMixin extends Vec3i {
|
||||
|
||||
@Shadow
|
||||
public abstract BlockPos immutable();
|
||||
|
||||
public BlockPosMixin(int i, int j, int k) {
|
||||
super(i, j, k);
|
||||
}
|
||||
@@ -29,7 +33,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos above(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY() + distance, this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() + distance, this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +53,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos below(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY() - distance, this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() - distance, this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +73,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos north(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY(), this.getZ() - distance);
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() - distance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +93,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos south(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX(), this.getY(), this.getZ() + distance);
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() + distance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +113,7 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos west(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX() - distance, this.getY(), this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX() - distance, this.getY(), this.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +133,6 @@ abstract class BlockPosMixin extends Vec3i {
|
||||
@Overwrite
|
||||
@Override
|
||||
public BlockPos east(final int distance) {
|
||||
return distance == 0 ? (BlockPos)(Object)this : new BlockPos(this.getX() + distance, this.getY(), this.getZ());
|
||||
return distance == 0 ? this.immutable() : new BlockPos(this.getX() + distance, this.getY(), this.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.util.NoneMatchStream;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArraySet;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrays;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -23,8 +27,11 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
abstract class EntityMixin {
|
||||
@@ -48,7 +55,13 @@ abstract class EntityMixin {
|
||||
public abstract Vec3 getEyePosition();
|
||||
|
||||
@Shadow
|
||||
public abstract boolean onGround();
|
||||
private boolean onGround;
|
||||
|
||||
@Shadow
|
||||
public abstract boolean isAlive();
|
||||
|
||||
@Shadow
|
||||
protected abstract void onInsideBlock(final BlockState blockState);
|
||||
|
||||
@Unique
|
||||
private static float[] calculateStepHeights(final AABB box, final List<VoxelShape> voxels, final List<AABB> aabbs, final float stepHeight,
|
||||
@@ -144,9 +157,9 @@ abstract class EntityMixin {
|
||||
|
||||
final boolean collidedDownwards = collidedY && movement.y < 0.0;
|
||||
|
||||
final double stepHeight = (double)this.maxUpStep();
|
||||
final double stepHeight;
|
||||
|
||||
if (stepHeight <= 0.0 || (!collidedDownwards && !this.onGround()) || (!collidedX && !collidedZ)) {
|
||||
if ((!collidedDownwards && !this.onGround) || (!collidedX && !collidedZ) || (stepHeight = (double)this.maxUpStep()) <= 0.0) {
|
||||
return collided;
|
||||
}
|
||||
|
||||
@@ -184,51 +197,83 @@ abstract class EntityMixin {
|
||||
return false;
|
||||
}
|
||||
|
||||
final float reducedWith = this.dimensions.width() * 0.8F;
|
||||
final AABB box = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
||||
final double reducedWith = (double)(this.dimensions.width() * 0.8F);
|
||||
final AABB boundingBox = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
||||
final Level world = this.level;
|
||||
|
||||
if (CollisionUtil.isEmpty(box)) {
|
||||
if (CollisionUtil.isEmpty(boundingBox)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final BlockPos.MutableBlockPos tempPos = new BlockPos.MutableBlockPos();
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Mth.floor(boundingBox.minY);
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
final int minX = Mth.floor(box.minX);
|
||||
final int minY = Mth.floor(box.minY);
|
||||
final int minZ = Mth.floor(box.minZ);
|
||||
final int maxX = Mth.floor(box.maxX);
|
||||
final int maxY = Mth.floor(box.maxY);
|
||||
final int maxZ = Mth.floor(box.maxZ);
|
||||
final int maxBlockX = Mth.floor(boundingBox.maxX);
|
||||
final int maxBlockY = Mth.floor(boundingBox.maxY);
|
||||
final int maxBlockZ = Mth.floor(boundingBox.maxZ);
|
||||
|
||||
final ChunkSource chunkProvider = this.level.getChunkSource();
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
|
||||
long lastChunkKey = ChunkPos.INVALID_CHUNK_POS;
|
||||
LevelChunk lastChunk = null;
|
||||
for (int fz = minZ; fz <= maxZ; ++fz) {
|
||||
tempPos.setZ(fz);
|
||||
for (int fx = minX; fx <= maxX; ++fx) {
|
||||
final int newChunkX = fx >> 4;
|
||||
final int newChunkZ = fz >> 4;
|
||||
final LevelChunk chunk = lastChunkKey == (lastChunkKey = CoordinateUtils.getChunkKey(newChunkX, newChunkZ)) ?
|
||||
lastChunk : (lastChunk = (LevelChunk)chunkProvider.getChunk(newChunkX, newChunkZ, ChunkStatus.FULL, true));
|
||||
tempPos.setX(fx);
|
||||
for (int fy = minY; fy <= maxY; ++fy) {
|
||||
tempPos.setY(fy);
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final BlockState state = chunk.getBlockState(tempPos);
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
if (((CollisionBlockState)state).moonrise$emptyCollisionShape() || !state.isSuffocating(this.level, tempPos)) {
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, true).getSections();
|
||||
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
final int blockY = currY | (currChunkY << 4);
|
||||
mutablePos.setY(blockY);
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
final int blockZ = currZ | (currChunkZ << 4);
|
||||
mutablePos.setZ(blockZ);
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final int blockX = currX | (currChunkX << 4);
|
||||
mutablePos.setX(blockX);
|
||||
|
||||
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
||||
|
||||
if (((CollisionBlockState)blockState).moonrise$emptyCollisionShape()
|
||||
|| !blockState.isSuffocating(world, mutablePos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, it does not use the Entity context stuff.
|
||||
final VoxelShape collisionShape = state.getCollisionShape(this.level, tempPos);
|
||||
final VoxelShape collisionShape = blockState.getCollisionShape(world, mutablePos);
|
||||
|
||||
if (collisionShape.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final AABB toCollide = box.move(-(double)fx, -(double)fy, -(double)fz);
|
||||
final AABB toCollide = boundingBox.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)collisionShape).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
@@ -245,7 +290,169 @@ abstract class EntityMixin {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid streams for retrieving blocks
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "move",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/Level;getBlockStatesIfLoaded(Lnet/minecraft/world/phys/AABB;)Ljava/util/stream/Stream;",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private <T> Stream<T> avoidStreams(final Level world, final AABB boundingBox) {
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Mth.floor(boundingBox.minY);
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ);
|
||||
|
||||
final int maxBlockX = Mth.floor(boundingBox.maxX);
|
||||
final int maxBlockY = Mth.floor(boundingBox.maxY);
|
||||
final int maxBlockZ = Mth.floor(boundingBox.maxZ);
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
if (!((ChunkSystemLevel)world).moonrise$areChunksLoaded(minChunkX, minChunkZ, maxChunkX, maxChunkZ)) {
|
||||
return new NoneMatchStream<>(true);
|
||||
}
|
||||
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
|
||||
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
||||
|
||||
if (blockState.is(Blocks.LAVA) || blockState.is(BlockTags.FIRE)) {
|
||||
return new NoneMatchStream<>(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new NoneMatchStream<>(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Retrieve blocks more efficiently
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void checkInsideBlocks() {
|
||||
final AABB boundingBox = this.getBoundingBox();
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX + CollisionUtil.COLLISION_EPSILON);
|
||||
final int minBlockY = Mth.floor(boundingBox.minY + CollisionUtil.COLLISION_EPSILON);
|
||||
final int minBlockZ = Mth.floor(boundingBox.minZ + CollisionUtil.COLLISION_EPSILON);
|
||||
|
||||
final int maxBlockX = Mth.floor(boundingBox.maxX - CollisionUtil.COLLISION_EPSILON);
|
||||
final int maxBlockY = Mth.floor(boundingBox.maxY - CollisionUtil.COLLISION_EPSILON);
|
||||
final int maxBlockZ = Mth.floor(boundingBox.maxZ - CollisionUtil.COLLISION_EPSILON);
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final Level world = this.level;
|
||||
|
||||
if (!((ChunkSystemLevel)world).moonrise$areChunksLoaded(minChunkX, minChunkZ, maxChunkX, maxChunkZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
|
||||
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
mutablePos.setY(currY | (currChunkY << 4));
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
mutablePos.setZ(currZ | (currChunkZ << 4));
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
mutablePos.setX(currX | (currChunkX << 4));
|
||||
|
||||
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
||||
|
||||
if (!this.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockState.entityInside(world, mutablePos, (Entity)(Object)this);
|
||||
this.onInsideBlock(blockState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
@@ -16,6 +15,7 @@ import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
@@ -27,20 +27,18 @@ import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(Level.class)
|
||||
abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseable {
|
||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Shadow
|
||||
public abstract LevelChunk getChunk(int x, int z);
|
||||
@@ -48,38 +46,6 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
@Shadow
|
||||
public abstract WorldBorder getWorldBorder();
|
||||
|
||||
|
||||
@Unique
|
||||
private int minSection;
|
||||
|
||||
@Unique
|
||||
private int maxSection;
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMinSection() {
|
||||
return this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMaxSection() {
|
||||
return this.maxSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Init min/max section
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
this.minSection = WorldUtil.getMinSection(this);
|
||||
this.maxSection = WorldUtil.getMaxSection(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to faster lookup.
|
||||
* See {@link EntityGetterMixin#isUnobstructed(Entity, VoxelShape)} for expected behavior
|
||||
@@ -173,7 +139,7 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
int lastChunkY = Integer.MIN_VALUE;
|
||||
int lastChunkZ = Integer.MIN_VALUE;
|
||||
|
||||
final int minSection = ((CollisionLevel)level).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(level);
|
||||
|
||||
for (;;) {
|
||||
currPos.set(currX, currY, currZ);
|
||||
@@ -368,83 +334,116 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
*/
|
||||
@Override
|
||||
public Optional<BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
||||
final int minSection = WorldUtil.getMinSection((Level)(Object)this);
|
||||
|
||||
final int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
final int minBlockY = Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockY = Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection((Level)(Object)this) << 4) + 16, Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1);
|
||||
|
||||
final int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
CollisionUtil.LazyEntityCollisionContext collisionContext = null;
|
||||
|
||||
final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
final CollisionContext collisionShape = new CollisionUtil.LazyEntityCollisionContext(entity);
|
||||
BlockPos selected = null;
|
||||
double selectedDistance = Double.MAX_VALUE;
|
||||
|
||||
final Vec3 entityPos = entity.position();
|
||||
|
||||
LevelChunk lastChunk = null;
|
||||
int lastChunkX = Integer.MIN_VALUE;
|
||||
int lastChunkZ = Integer.MIN_VALUE;
|
||||
// special cases:
|
||||
if (minBlockY > maxBlockY) {
|
||||
// no point in checking
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final int minChunkX = minBlockX >> 4;
|
||||
final int maxChunkX = maxBlockX >> 4;
|
||||
|
||||
final int minChunkY = minBlockY >> 4;
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
|
||||
final int minChunkZ = minBlockZ >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final ChunkSource chunkSource = this.getChunkSource();
|
||||
|
||||
for (int currZ = minBlockZ; currZ <= maxBlockZ; ++currZ) {
|
||||
pos.setZ(currZ);
|
||||
for (int currX = minBlockX; currX <= maxBlockX; ++currX) {
|
||||
pos.setX(currX);
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
||||
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);
|
||||
|
||||
final int newChunkX = currX >> 4;
|
||||
final int newChunkZ = currZ >> 4;
|
||||
|
||||
if (((newChunkX ^ lastChunkX) | (newChunkZ ^ lastChunkZ)) != 0) {
|
||||
lastChunkX = newChunkX;
|
||||
lastChunkZ = newChunkZ;
|
||||
lastChunk = (LevelChunk)chunkSource.getChunk(newChunkX, newChunkZ, ChunkStatus.FULL, false);
|
||||
}
|
||||
|
||||
if (lastChunk == null) {
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
for (int currY = minBlockY; currY <= maxBlockY; ++currY) {
|
||||
int edgeCount = ((currX == minBlockX || currX == maxBlockX) ? 1 : 0) +
|
||||
((currY == minBlockY || currY == maxBlockY) ? 1 : 0) +
|
||||
((currZ == minBlockZ || currZ == maxBlockZ) ? 1 : 0);
|
||||
|
||||
final LevelChunkSection[] sections = chunk.getSections();
|
||||
|
||||
// bound y
|
||||
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
||||
final int sectionIdx = currChunkY - minSection;
|
||||
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean hasSpecial = ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks();
|
||||
final int sectionAdjust = !hasSpecial ? 1 : 0;
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) + sectionAdjust : 0;
|
||||
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) - sectionAdjust : 15;
|
||||
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) + sectionAdjust : 0;
|
||||
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) - sectionAdjust : 15;
|
||||
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) + sectionAdjust : 0;
|
||||
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) - sectionAdjust : 15;
|
||||
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
final int blockY = currY | (currChunkY << 4);
|
||||
mutablePos.setY(blockY);
|
||||
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
||||
final int blockZ = currZ | (currChunkZ << 4);
|
||||
mutablePos.setZ(blockZ);
|
||||
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
||||
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
|
||||
final int blockX = currX | (currChunkX << 4);
|
||||
mutablePos.setX(blockX);
|
||||
|
||||
final int edgeCount = hasSpecial ? ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
|
||||
((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
|
||||
((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0) : 0;
|
||||
if (edgeCount == 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pos.setY(currY);
|
||||
|
||||
final double distance = pos.distToCenterSqr(entityPos);
|
||||
if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(pos) >= 0)) {
|
||||
final double distance = mutablePos.distToCenterSqr(entityPos);
|
||||
if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(mutablePos) >= 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final BlockState state = ((GetBlockChunk)lastChunk).moonrise$getBlock(currX, currY, currZ);
|
||||
if (((CollisionBlockState)state).moonrise$emptyContextCollisionShape()) {
|
||||
final BlockState blockData = blocks.get(localBlockIndex);
|
||||
|
||||
if (((CollisionBlockState)blockData).moonrise$emptyContextCollisionShape()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VoxelShape blockCollision = ((CollisionBlockState)state).moonrise$getConstantContextCollisionShape();
|
||||
|
||||
if ((edgeCount != 1 || state.hasLargeCollisionShape()) && (edgeCount != 2 || state.getBlock() == Blocks.MOVING_PISTON)) {
|
||||
if (collisionContext == null) {
|
||||
collisionContext = new CollisionUtil.LazyEntityCollisionContext(entity);
|
||||
}
|
||||
VoxelShape blockCollision = ((CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape();
|
||||
|
||||
if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) {
|
||||
if (blockCollision == null) {
|
||||
blockCollision = state.getCollisionShape((Level)(Object)this, pos, collisionContext);
|
||||
}
|
||||
blockCollision = blockData.getCollisionShape((Level)(Object)this, mutablePos, collisionShape);
|
||||
|
||||
if (blockCollision.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid VoxelShape#move by shifting the entity collision shape instead
|
||||
final AABB shiftedAABB = aabb.move(-(double)currX, -(double)currY, -(double)currZ);
|
||||
final AABB shiftedAABB = aabb.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
||||
if (singleAABB != null) {
|
||||
@@ -452,7 +451,7 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
continue;
|
||||
}
|
||||
|
||||
selected = pos.immutable();
|
||||
selected = mutablePos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
@@ -461,13 +460,16 @@ abstract class LevelMixin implements CollisionLevel, LevelAccessor, AutoCloseabl
|
||||
continue;
|
||||
}
|
||||
|
||||
selected = pos.immutable();
|
||||
selected = mutablePos.immutable();
|
||||
selectedDistance = distance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.ofNullable(selected);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ abstract class LivingEntityMixin extends Entity implements Attackable {
|
||||
int nonPassengers = 0;
|
||||
for (int i = 0, len = nearby.size(); i < len; ++i) {
|
||||
final Entity entity = nearby.get(i);
|
||||
nonPassengers += (entity.isPassenger() ? 1 : 0);
|
||||
nonPassengers += (entity.isPassenger() ? 0 : 1);
|
||||
this.doPush(entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@ import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Supplier;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Shapes.class)
|
||||
abstract class ShapesMixin {
|
||||
@@ -168,13 +170,13 @@ abstract class ShapesMixin {
|
||||
final VoxelShape first = tmp[i];
|
||||
final VoxelShape second = tmp[next];
|
||||
|
||||
tmp[newSize++] = Shapes.or(first, second);
|
||||
tmp[newSize++] = Shapes.joinUnoptimized(first, second, BooleanOp.OR);
|
||||
}
|
||||
}
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
return tmp[0];
|
||||
return tmp[0].optimize();
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -219,11 +221,24 @@ abstract class ShapesMixin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an inject instead of overwrite to avoid mixin conflicts - obviously this will still disregard any changes made by other
|
||||
* mixins, but at least it won't conflict immediately. This is useful because some library mods mixin here when only the content
|
||||
* mod actually needs the change.
|
||||
*
|
||||
* @reason Route to faster logic
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
@Inject(
|
||||
method = "joinUnoptimized",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void injectJoinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction, final CallbackInfoReturnable<VoxelShape> cir) {
|
||||
cir.setReturnValue(joinUnoptimized(first, second, mergeFunction));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
final VoxelShape ret = CollisionUtil.joinUnoptimized(first, second, mergeFunction);
|
||||
if (DEBUG_SHAPE_MERGING) {
|
||||
final VoxelShape vanilla = joinUnoptimizedVanilla(first, second, mergeFunction);
|
||||
@@ -239,9 +254,19 @@ abstract class ShapesMixin {
|
||||
/**
|
||||
* @reason Route to faster logic
|
||||
* @author Spottedleaf
|
||||
* @see #injectJoinUnoptimized(VoxelShape, VoxelShape, BooleanOp, CallbackInfoReturnable)
|
||||
*/
|
||||
@Overwrite
|
||||
public static boolean joinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
@Inject(
|
||||
method = "joinIsNotEmpty(Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/VoxelShape;Lnet/minecraft/world/phys/shapes/BooleanOp;)Z",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static void injectJoinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction, final CallbackInfoReturnable<Boolean> cir) {
|
||||
cir.setReturnValue(joinIsNotEmpty(first, second, mergeFunction));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean joinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp mergeFunction) {
|
||||
final boolean ret = CollisionUtil.isJoinNonEmpty(first, second, mergeFunction);
|
||||
if (DEBUG_SHAPE_MERGING) {
|
||||
if (ret != !joinUnoptimizedVanilla(first, second, mergeFunction).isEmpty()) {
|
||||
|
||||
@@ -67,7 +67,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkHolder.PlayerP
|
||||
)
|
||||
)
|
||||
private boolean newTrackerTick(final Iterator<?> iterator) {
|
||||
final NearbyPlayers nearbyPlayers = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers();
|
||||
final ServerEntityLookup entityLookup = (ServerEntityLookup)((ChunkSystemServerLevel)this.level).moonrise$getEntityLookup();;
|
||||
|
||||
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
|
||||
@@ -78,7 +77,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkHolder.PlayerP
|
||||
if (tracker == null) {
|
||||
continue;
|
||||
}
|
||||
((EntityTrackerTrackedEntity)tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
((EntityTrackerTrackedEntity)tracker).moonrise$tick(((ChunkSystemEntity)entity).moonrise$getChunkData().nearbyPlayers);
|
||||
if (((EntityTrackerTrackedEntity)tracker).moonrise$hasPlayers()
|
||||
|| ((ChunkSystemEntity)entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
|
||||
tracker.serverEntity.sendChanges();
|
||||
|
||||
@@ -4,16 +4,16 @@ import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(ChunkMap.TrackedEntity.class)
|
||||
@@ -22,26 +22,22 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
||||
@Final
|
||||
private Set<ServerPlayerConnection> seenBy;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private int range;
|
||||
|
||||
@Shadow
|
||||
public abstract void updatePlayer(ServerPlayer serverPlayer);
|
||||
|
||||
@Shadow
|
||||
public abstract void removePlayer(ServerPlayer serverPlayer);
|
||||
|
||||
/**
|
||||
* @reason ReferenceOpenHashSet is a better choice than a wrapped IdentityHashMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Sets;newIdentityHashSet()Ljava/util/Set;"
|
||||
)
|
||||
)
|
||||
private <E> Set<E> useBetterIdentitySet() {
|
||||
return new ReferenceOpenHashSet<>();
|
||||
}
|
||||
@Shadow
|
||||
protected abstract int scaledRange(final int i);
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
Entity entity;
|
||||
|
||||
@Unique
|
||||
private long lastChunkUpdate = -1L;
|
||||
@@ -126,4 +122,42 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
|
||||
public final boolean moonrise$hasPlayers() {
|
||||
return !this.seenBy.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason ReferenceOpenHashSet is a better choice than a wrapped IdentityHashMap
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/Sets;newIdentityHashSet()Ljava/util/Set;"
|
||||
)
|
||||
)
|
||||
private <E> Set<E> useBetterIdentitySet() {
|
||||
return new ReferenceOpenHashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Optimise impl to not retrieve indirect passengers unless needed
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public int getEffectiveRange() {
|
||||
int range = this.range;
|
||||
final Entity entity = this.entity;
|
||||
|
||||
if (entity.getPassengers() == ImmutableList.<Entity>of()) {
|
||||
return this.scaledRange(range);
|
||||
}
|
||||
|
||||
// note: we change to List
|
||||
final List<Entity> passengers = (List<Entity>)entity.getIndirectPassengers();
|
||||
for (int i = 0, len = passengers.size(); i < len; ++i) {
|
||||
// note: max should be branchless
|
||||
range = Math.max(range, passengers.get(i).getType().clientTrackingRange() << 4);
|
||||
}
|
||||
|
||||
return this.scaledRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,40 +35,18 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V",
|
||||
// cannot use `<init>*` due to https://github.com/FabricMC/tiny-remapper/issues/137
|
||||
method = {
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V"
|
||||
},
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
),
|
||||
require = 3 // Require matching all 3 constructors
|
||||
)
|
||||
)
|
||||
private void constructorHook1(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook2(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook3(final CallbackInfo ci) {
|
||||
private void constructorHook(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fluid;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fluid.FluidFluidState;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -11,12 +12,9 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(FluidState.class)
|
||||
abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> {
|
||||
abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> implements FluidFluidState {
|
||||
|
||||
@Shadow
|
||||
public abstract Fluid getType();
|
||||
@@ -43,23 +41,9 @@ abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> {
|
||||
@Unique
|
||||
private BlockState legacyBlock;
|
||||
|
||||
/**
|
||||
* @reason Initialise caches
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
try {
|
||||
@Override
|
||||
public void moonrise$initCaches() {
|
||||
this.amount = this.getType().getAmount((FluidState)(Object)this);
|
||||
} catch (final Exception ex) {
|
||||
// https://github.com/JDKDigital/productivetrees/issues/16
|
||||
new RuntimeException("Failed to retrieve fluid amount for " + this, ex).printStackTrace();
|
||||
}
|
||||
this.isEmpty = this.getType().isEmpty();
|
||||
this.isSource = this.getType().isSource((FluidState)(Object)this);
|
||||
this.ownHeight = this.getType().getOwnHeight((FluidState)(Object)this);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fluid;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fluid.FluidFluidState;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.RegistrationInfo;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(MappedRegistry.class)
|
||||
abstract class MappedRegistryMixin<T> {
|
||||
/**
|
||||
* @reason Initialise caches
|
||||
* @author jpenilla
|
||||
*/
|
||||
@Inject(
|
||||
method = "register(Lnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lnet/minecraft/core/RegistrationInfo;)Lnet/minecraft/core/Holder$Reference;",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void injectFluidRegister(
|
||||
final ResourceKey<?> resourceKey,
|
||||
final T object,
|
||||
final RegistrationInfo registrationInfo,
|
||||
final CallbackInfoReturnable<Holder.Reference<T>> cir
|
||||
) {
|
||||
if (resourceKey.registryKey() == (Object) Registries.FLUID) {
|
||||
for (final FluidState possibleState : ((Fluid) object).getStateDefinition().getPossibleStates()) {
|
||||
((FluidFluidState) (Object) possibleState).moonrise$initCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
@@ -81,6 +81,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
|
||||
* @reason Route to optimized getBlock
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Override
|
||||
@Overwrite
|
||||
public BlockState getBlockState(final BlockPos pos) {
|
||||
return this.moonrise$getBlock(pos.getX(), pos.getY(), pos.getZ());
|
||||
@@ -101,7 +102,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState moonrise$getBlock(final int x, final int y, final int z) {
|
||||
public final BlockState moonrise$getBlock(final int x, final int y, final int z) {
|
||||
if (this.debug) {
|
||||
return this.getBlockDebug(x, y, z);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
// Higher priority to apply after Lithium mixin.world.inline_height.WorldMixin
|
||||
@Mixin(value = Level.class, priority = 1100)
|
||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Unique
|
||||
private int height;
|
||||
|
||||
@Unique
|
||||
private int minBuildHeight;
|
||||
|
||||
@Unique
|
||||
private int maxBuildHeight;
|
||||
|
||||
@Unique
|
||||
private int minSection;
|
||||
|
||||
@Unique
|
||||
private int maxSection;
|
||||
|
||||
@Unique
|
||||
private int sectionsCount;
|
||||
|
||||
/**
|
||||
* @reason Init min/max section
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "CTOR_HEAD"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci,
|
||||
@Local(ordinal = 0, argsOnly = true) final Holder<DimensionType> dimensionTypeHolder) {
|
||||
final DimensionType dimType = dimensionTypeHolder.value();
|
||||
this.height = dimType.height();
|
||||
this.minBuildHeight = dimType.minY();
|
||||
this.maxBuildHeight = this.minBuildHeight + this.height;
|
||||
this.minSection = this.minBuildHeight >> 4;
|
||||
this.maxSection = ((this.maxBuildHeight - 1) >> 4) + 1;
|
||||
this.sectionsCount = this.maxSection - this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return this.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight() {
|
||||
return this.minBuildHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxBuildHeight() {
|
||||
return this.maxBuildHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSection() {
|
||||
return this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSection() {
|
||||
return this.maxSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(final int y) {
|
||||
return y < this.minBuildHeight || y >= this.maxBuildHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(final BlockPos blockPos) {
|
||||
return this.isOutsideBuildHeight(blockPos.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndex(final int blockY) {
|
||||
return (blockY >> 4) - this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionIndexFromSectionY(final int sectionY) {
|
||||
return sectionY - this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionYFromSectionIndex(final int sectionIdx) {
|
||||
return sectionIdx + this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionsCount() {
|
||||
return this.sectionsCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(EntityType.class)
|
||||
abstract class EntityTypeMixin implements MobSpawningEntityType {
|
||||
|
||||
@Unique
|
||||
private boolean hasBiomeCost = false;
|
||||
|
||||
@Override
|
||||
public final boolean moonrise$hasAnyBiomeCost() {
|
||||
return this.hasBiomeCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void moonrise$setHasBiomeCost() {
|
||||
this.hasBiomeCost = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.LocalMobCapCalculator;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LocalMobCapCalculator.MobCounts.class)
|
||||
abstract class LocalMobCapCalculator$MobCountsMixin {
|
||||
|
||||
@Shadow
|
||||
@Mutable
|
||||
@Final
|
||||
private Object2IntMap<MobCategory> counts;
|
||||
|
||||
@Unique
|
||||
private static final MobCategory[] CATEGORIES = MobCategory.values();
|
||||
|
||||
@Unique
|
||||
private static final Object2IntOpenHashMap<?> DUMMY = new Object2IntOpenHashMap<>();
|
||||
|
||||
@Unique
|
||||
private final int[] newCounts = new int[CATEGORIES.length];
|
||||
|
||||
/**
|
||||
* @reason Ensure accesses of old field blow up
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void destroyField(final CallbackInfo ci) {
|
||||
this.counts = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid allocating the map, we null it later
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "(I)Lit/unimi/dsi/fastutil/objects/Object2IntOpenHashMap;"
|
||||
)
|
||||
)
|
||||
private <T> Object2IntOpenHashMap<T> avoidAllocation(final int expected) {
|
||||
return (Object2IntOpenHashMap<T>)DUMMY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Do not allocate MobCategory[]
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/MobCategory;values()[Lnet/minecraft/world/entity/MobCategory;"
|
||||
)
|
||||
)
|
||||
private MobCategory[] useCachedArray() {
|
||||
return CATEGORIES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use simple array instead of compute call
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public void add(final MobCategory category) {
|
||||
++this.newCounts[category.ordinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Use simple array instead of map get call
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public boolean canSpawn(final MobCategory category) {
|
||||
return this.newCounts[category.ordinal()] < category.getMaxInstancesPerChunk();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(MobSpawnSettings.class)
|
||||
abstract class MobSpawnSettingsMixin {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<EntityType<?>, MobSpawnSettings.MobSpawnCost> mobSpawnCosts;
|
||||
|
||||
/**
|
||||
* @reason Set biome cost flag for any EntityType which has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void initBiomeCost(final CallbackInfo ci) {
|
||||
for (final EntityType<?> type : this.mobSpawnCosts.keySet()) {
|
||||
((MobSpawningEntityType)type).moonrise$setHasBiomeCost();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ca.spottedleaf.moonrise.mixin.mob_spawning;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.mob_spawning.MobSpawningEntityType;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.NaturalSpawner;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(NaturalSpawner.class)
|
||||
abstract class NaturalSpawnerMixin {
|
||||
|
||||
@Shadow
|
||||
static Biome getRoughBiome(final BlockPos arg, final ChunkAccess arg2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Delay until we determine if the entity type even has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/NaturalSpawner;getRoughBiome(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/chunk/ChunkAccess;)Lnet/minecraft/world/level/biome/Biome;"
|
||||
)
|
||||
)
|
||||
private static Biome delayRoughBiome(final BlockPos pos, final ChunkAccess chunk) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Delay until we determine if the entity type even has a cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/biome/Biome;getMobSettings()Lnet/minecraft/world/level/biome/MobSpawnSettings;"
|
||||
)
|
||||
)
|
||||
private static MobSpawnSettings delayMobSpawnSettings(final Biome biome) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Avoid looking up biomes for mobs which have no cost
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {"method_27819", "lambda$createState$2"}, // Fabric, NeoForge
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/biome/MobSpawnSettings;getMobSpawnCost(Lnet/minecraft/world/entity/EntityType;)Lnet/minecraft/world/level/biome/MobSpawnSettings$MobSpawnCost;"
|
||||
)
|
||||
)
|
||||
private static MobSpawnSettings.MobSpawnCost avoidBiomeLookupIfPossible(final MobSpawnSettings isNull,
|
||||
final EntityType<?> type,
|
||||
@Local(ordinal = 0, argsOnly = true) final BlockPos pos,
|
||||
@Local(ordinal = 0, argsOnly = true) final LevelChunk chunk) {
|
||||
if (!((MobSpawningEntityType)type).moonrise$hasAnyBiomeCost()) {
|
||||
// if the type has no associated cost with any biome, then no point in looking
|
||||
return null;
|
||||
}
|
||||
|
||||
return getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(type);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.mixin.random_ticking;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
@@ -85,11 +85,11 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
||||
final int offsetY = (sectionIndex + minSection) << 4;
|
||||
final LevelChunkSection section = sections[sectionIndex];
|
||||
final PalettedContainer<BlockState> states = section.states;
|
||||
if (section == null || !section.isRandomlyTickingBlocks()) {
|
||||
if (!section.isRandomlyTickingBlocks()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final IntList tickList = ((BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
||||
final ShortList tickList = ((BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
||||
|
||||
for (int i = 0; i < tickSpeed; ++i) {
|
||||
final int tickingBlocks = tickList.size();
|
||||
@@ -100,7 +100,7 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int location = tickList.getRaw(index);
|
||||
final int location = (int)tickList.getRaw(index) & 0xFFFF;
|
||||
final BlockState state = states.get(location);
|
||||
|
||||
// do not use a mutable pos, as some random tick implementations store the input without calling immutable()!
|
||||
|
||||
@@ -17,11 +17,17 @@ abstract class PalettedContainerMixin {
|
||||
* @author jpenilla
|
||||
*/
|
||||
@Redirect(
|
||||
method = "<init>*",
|
||||
// cannot use `<init>*` due to https://github.com/FabricMC/tiny-remapper/issues/137
|
||||
method = {
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
"<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V"
|
||||
},
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "Lnet/minecraft/util/ThreadingDetector;"
|
||||
)
|
||||
),
|
||||
require = 3 // Require matching all 3 constructors
|
||||
)
|
||||
private static ThreadingDetector threadingDetector(final String name) {
|
||||
return THREADING_DETECTOR;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.patches.block_counting;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
|
||||
public interface BlockCountingBitStorage {
|
||||
|
||||
public Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries();
|
||||
public Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package ca.spottedleaf.moonrise.patches.block_counting;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.IntList;
|
||||
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||
|
||||
public interface BlockCountingChunkSection {
|
||||
|
||||
public int moonrise$getSpecialCollidingBlocks();
|
||||
public boolean moonrise$hasSpecialCollidingBlocks();
|
||||
|
||||
public IntList moonrise$getTickingBlockList();
|
||||
public ShortList moonrise$getTickingBlockList();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_system.entity;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.monster.Shulker;
|
||||
@@ -19,6 +20,10 @@ public interface ChunkSystemEntity {
|
||||
|
||||
public void moonrise$setChunkStatus(final FullChunkStatus status);
|
||||
|
||||
public ChunkData moonrise$getChunkData();
|
||||
|
||||
public void moonrise$setChunkData(final ChunkData chunkData);
|
||||
|
||||
public int moonrise$getSectionX();
|
||||
|
||||
public void moonrise$setSectionX(final int x);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_system.level;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
@@ -19,4 +20,14 @@ public interface ChunkSystemLevel {
|
||||
|
||||
public void moonrise$midTickTasks();
|
||||
|
||||
public ChunkData moonrise$getChunkData(final long chunkKey);
|
||||
|
||||
public ChunkData moonrise$getChunkData(final int chunkX, final int chunkZ);
|
||||
|
||||
public ChunkData moonrise$requestChunkData(final long chunkKey);
|
||||
|
||||
public ChunkData moonrise$releaseChunkData(final long chunkKey);
|
||||
|
||||
public boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ);
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
@@ -60,4 +61,12 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
|
||||
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
|
||||
|
||||
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
|
||||
|
||||
public ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks();
|
||||
|
||||
public void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder);
|
||||
|
||||
public void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder);
|
||||
|
||||
public void moonrise$clearUnsyncedChunks();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_system.level.chunk;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
|
||||
public final class ChunkData {
|
||||
|
||||
private int referenceCount = 0;
|
||||
public NearbyPlayers.TrackedChunk nearbyPlayers; // Moonrise - nearby players
|
||||
|
||||
public ChunkData() {
|
||||
|
||||
}
|
||||
|
||||
public int increaseRef() {
|
||||
return ++this.referenceCount;
|
||||
}
|
||||
|
||||
public int decreaseRef() {
|
||||
return --this.referenceCount;
|
||||
}
|
||||
}
|
||||
@@ -23,4 +23,8 @@ public interface ChunkSystemChunkHolder {
|
||||
|
||||
public LevelChunk moonrise$getFullChunk();
|
||||
|
||||
public boolean moonrise$isMarkedDirtyForPlayers();
|
||||
|
||||
public void moonrise$markDirtyForPlayers(final boolean value);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.EntityList;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
@@ -42,6 +43,7 @@ public final class ChunkEntitySlices {
|
||||
private final EntityList entities = new EntityList();
|
||||
|
||||
public FullChunkStatus status;
|
||||
public final ChunkData chunkData;
|
||||
|
||||
private boolean isTransient;
|
||||
|
||||
@@ -54,7 +56,7 @@ public final class ChunkEntitySlices {
|
||||
}
|
||||
|
||||
public ChunkEntitySlices(final Level world, final int chunkX, final int chunkZ, final FullChunkStatus status,
|
||||
final int minSection, final int maxSection) { // inclusive, inclusive
|
||||
final ChunkData chunkData, final int minSection, final int maxSection) { // inclusive, inclusive
|
||||
this.minSection = minSection;
|
||||
this.maxSection = maxSection;
|
||||
this.chunkX = chunkX;
|
||||
@@ -67,6 +69,7 @@ public final class ChunkEntitySlices {
|
||||
this.entitiesByType = new Reference2ObjectOpenHashMap<>();
|
||||
|
||||
this.status = status;
|
||||
this.chunkData = chunkData;
|
||||
}
|
||||
|
||||
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) {
|
||||
@@ -223,6 +226,7 @@ public final class ChunkEntitySlices {
|
||||
return false;
|
||||
}
|
||||
((ChunkSystemEntity)entity).moonrise$setChunkStatus(this.status);
|
||||
((ChunkSystemEntity)entity).moonrise$setChunkData(this.chunkData);
|
||||
final int sectionIndex = chunkSection - this.minSection;
|
||||
|
||||
this.allEntities.addEntity(entity, sectionIndex);
|
||||
@@ -256,6 +260,7 @@ public final class ChunkEntitySlices {
|
||||
return false;
|
||||
}
|
||||
((ChunkSystemEntity)entity).moonrise$setChunkStatus(null);
|
||||
((ChunkSystemEntity)entity).moonrise$setChunkData(null);
|
||||
final int sectionIndex = chunkSection - this.minSection;
|
||||
|
||||
this.allEntities.removeEntity(entity, sectionIndex);
|
||||
|
||||
@@ -46,8 +46,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
|
||||
protected final SWMRLong2ObjectHashTable<ChunkSlicesRegion> regions = new SWMRLong2ObjectHashTable<>(128, 0.5f);
|
||||
|
||||
protected final int minSection; // inclusive
|
||||
protected final int maxSection; // inclusive
|
||||
protected final LevelCallback<Entity> worldCallback;
|
||||
|
||||
protected final ConcurrentLong2ReferenceChainedHashTable<Entity> entityById = new ConcurrentLong2ReferenceChainedHashTable<>();
|
||||
@@ -56,8 +54,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
|
||||
public EntityLookup(final Level world, final LevelCallback<Entity> worldCallback) {
|
||||
this.world = world;
|
||||
this.minSection = WorldUtil.getMinSection(world);
|
||||
this.maxSection = WorldUtil.getMaxSection(world);
|
||||
this.worldCallback = worldCallback;
|
||||
}
|
||||
|
||||
@@ -404,7 +400,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) {
|
||||
final BlockPos pos = entity.blockPosition();
|
||||
final int sectionX = pos.getX() >> 4;
|
||||
final int sectionY = Mth.clamp(pos.getY() >> 4, this.minSection, this.maxSection);
|
||||
final int sectionY = Mth.clamp(pos.getY() >> 4, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world));
|
||||
final int sectionZ = pos.getZ() >> 4;
|
||||
this.checkThread(sectionX, sectionZ, "Cannot add entity off-main thread");
|
||||
|
||||
@@ -523,7 +519,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
||||
final int sectionZ = ((ChunkSystemEntity)entity).moonrise$getSectionZ();
|
||||
final BlockPos newPos = entity.blockPosition();
|
||||
final int newSectionX = newPos.getX() >> 4;
|
||||
final int newSectionY = Mth.clamp(newPos.getY() >> 4, this.minSection, this.maxSection);
|
||||
final int newSectionY = Mth.clamp(newPos.getY() >> 4, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world));
|
||||
final int newSectionZ = newPos.getZ() >> 4;
|
||||
|
||||
if (newSectionX == sectionX && newSectionY == sectionY && newSectionZ == sectionZ) {
|
||||
|
||||
@@ -46,7 +46,8 @@ public final class ClientEntityLookup extends EntityLookup {
|
||||
|
||||
final ChunkEntitySlices ret = new ChunkEntitySlices(
|
||||
this.world, chunkX, chunkZ,
|
||||
ticking ? FullChunkStatus.ENTITY_TICKING : FullChunkStatus.FULL, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
||||
ticking ? FullChunkStatus.ENTITY_TICKING : FullChunkStatus.FULL, null,
|
||||
WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
||||
);
|
||||
|
||||
// note: not handled by superclass
|
||||
|
||||
@@ -32,7 +32,7 @@ public final class DefaultEntityLookup extends EntityLookup {
|
||||
protected ChunkEntitySlices createEntityChunk(final int chunkX, final int chunkZ, final boolean transientChunk) {
|
||||
final ChunkEntitySlices ret = new ChunkEntitySlices(
|
||||
this.world, chunkX, chunkZ, FullChunkStatus.FULL,
|
||||
WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
||||
null, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
||||
);
|
||||
|
||||
// note: not handled by superclass
|
||||
|
||||
@@ -5,7 +5,6 @@ import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTabl
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
||||
@@ -1023,11 +1022,10 @@ public final class ChunkHolderManager {
|
||||
}
|
||||
|
||||
private void removeChunkHolder(final NewChunkHolder holder) {
|
||||
holder.markUnloaded();
|
||||
holder.onUnload();
|
||||
this.autoSaveQueue.remove(holder);
|
||||
ChunkSystem.onChunkHolderDelete(this.world, holder.vanillaChunkHolder);
|
||||
this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ));
|
||||
|
||||
}
|
||||
|
||||
// note: never call while inside the chunk system, this will absolutely break everything
|
||||
|
||||
@@ -14,7 +14,9 @@ import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemFeatures;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.async_save.AsyncChunkSaveData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkStatus;
|
||||
@@ -61,6 +63,8 @@ public final class NewChunkHolder {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NewChunkHolder.class);
|
||||
|
||||
public final ChunkData holderData;
|
||||
|
||||
public final ServerLevel world;
|
||||
public final int chunkX;
|
||||
public final int chunkZ;
|
||||
@@ -92,7 +96,7 @@ public final class NewChunkHolder {
|
||||
if (this.entityChunk == null) {
|
||||
ret = this.entityChunk = new ChunkEntitySlices(
|
||||
this.world, this.chunkX, this.chunkZ, this.getChunkStatus(),
|
||||
WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
||||
this.holderData, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
||||
);
|
||||
|
||||
ret.setTransient(transientChunk);
|
||||
@@ -654,6 +658,7 @@ public final class NewChunkHolder {
|
||||
world.getLightEngine(), null, world.getChunkSource().chunkMap
|
||||
);
|
||||
((ChunkSystemChunkHolder)this.vanillaChunkHolder).moonrise$setRealChunkHolder(this);
|
||||
this.holderData = ((ChunkSystemLevel)this.world).moonrise$requestChunkData(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
public ChunkAccess getCurrentChunk() {
|
||||
@@ -753,8 +758,10 @@ public final class NewChunkHolder {
|
||||
/** Unloaded from chunk map */
|
||||
private boolean unloaded;
|
||||
|
||||
void markUnloaded() {
|
||||
void onUnload() {
|
||||
this.unloaded = true;
|
||||
((ChunkSystemServerLevel)this.world).moonrise$removeUnsyncedChunk(this.vanillaChunkHolder);
|
||||
((ChunkSystemLevel)this.world).moonrise$releaseChunkData(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ));
|
||||
}
|
||||
|
||||
private boolean inUnloadQueue = false;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_tick_iteration;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
|
||||
public interface ChunkTickServerLevel {
|
||||
|
||||
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks();
|
||||
|
||||
public void moonrise$markChunkForPlayerTicking(final LevelChunk chunk);
|
||||
|
||||
public void moonrise$removeChunkForPlayerTicking(final LevelChunk chunk);
|
||||
|
||||
public void moonrise$addPlayerTickingRequest(final int chunkX, final int chunkZ);
|
||||
|
||||
public void moonrise$removePlayerTickingRequest(final int chunkX, final int chunkZ);
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package ca.spottedleaf.moonrise.patches.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionDiscreteVoxelShape;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevel;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleList;
|
||||
@@ -1077,10 +1077,11 @@ public final class CollisionUtil {
|
||||
final int s1z = mergedZ.firstIndices[idxZ];
|
||||
final int s2z = mergedZ.secondIndices[idxZ];
|
||||
|
||||
int idx;
|
||||
int idx1;
|
||||
int idx2;
|
||||
|
||||
final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx) & 1L);
|
||||
final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx) & 1L);
|
||||
final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx1 = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx1) & 1L);
|
||||
final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx2 = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx2) & 1L);
|
||||
|
||||
// idx ff -> 0
|
||||
// idx ft -> 1
|
||||
@@ -1143,10 +1144,11 @@ public final class CollisionUtil {
|
||||
final int s1z = mergedZ.firstIndices[idxZ];
|
||||
final int s2z = mergedZ.secondIndices[idxZ];
|
||||
|
||||
int idx;
|
||||
int idx1;
|
||||
int idx2;
|
||||
|
||||
final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx) & 1L);
|
||||
final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx) & 1L);
|
||||
final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx1 = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx1) & 1L);
|
||||
final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx2 = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx2) & 1L);
|
||||
|
||||
// idx ff -> 0
|
||||
// idx ft -> 1
|
||||
@@ -1932,13 +1934,13 @@ public final class CollisionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
final int minSection = ((CollisionLevel)world).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
|
||||
final int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
|
||||
|
||||
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - COLLISION_EPSILON) - 1);
|
||||
final int maxBlockY = Math.min((((CollisionLevel)world).moonrise$getMaxSection() << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
||||
|
||||
final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
|
||||
final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
|
||||
@@ -1989,12 +1991,12 @@ public final class CollisionUtil {
|
||||
continue;
|
||||
}
|
||||
final LevelChunkSection section = sections[sectionIdx];
|
||||
if (section == null || section.hasOnlyAir()) {
|
||||
if (section.hasOnlyAir()) {
|
||||
// empty
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean hasSpecial = ((BlockCountingChunkSection)section).moonrise$getSpecialCollidingBlocks() != 0;
|
||||
final boolean hasSpecial = ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks();
|
||||
final int sectionAdjust = !hasSpecial ? 1 : 0;
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
|
||||
@@ -22,13 +22,17 @@ import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class EmptyStreamForMoveCall<T> implements Stream<T> {
|
||||
public final class NoneMatchStream<T> implements Stream<T> {
|
||||
|
||||
public static final EmptyStreamForMoveCall INSTANCE = new EmptyStreamForMoveCall();
|
||||
private final boolean value;
|
||||
|
||||
public NoneMatchStream(final boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noneMatch(Predicate<? super T> predicate) {
|
||||
return false; // important: ret false so the branch is never taken by mojang code
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,9 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.patches.collisions.world;
|
||||
|
||||
public interface CollisionLevel {
|
||||
|
||||
public int moonrise$getMinSection();
|
||||
|
||||
public int moonrise$getMaxSection();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ca.spottedleaf.moonrise.patches.fluid;
|
||||
|
||||
public interface FluidFluidState {
|
||||
public void moonrise$initCaches();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_getblock;
|
||||
package ca.spottedleaf.moonrise.patches.getblock;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package ca.spottedleaf.moonrise.patches.mob_spawning;
|
||||
|
||||
public interface MobSpawningEntityType {
|
||||
|
||||
public boolean moonrise$hasAnyBiomeCost();
|
||||
|
||||
public void moonrise$setHasBiomeCost();
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package ca.spottedleaf.moonrise.patches.profiler;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import org.slf4j.Logger;
|
||||
@@ -169,6 +171,7 @@ public final class LeafProfiler {
|
||||
public final List<ProfileNode> children = new ArrayList<>();
|
||||
public long childrenTimingCount;
|
||||
public int depth = -1;
|
||||
public boolean lastChild;
|
||||
|
||||
private ProfileNode(final ProfileNode parent, final int nodeId, final LProfilerRegistry.ProfilerEntry profiler,
|
||||
final long totalTime, final long totalCount) {
|
||||
@@ -188,11 +191,6 @@ public final class LeafProfiler {
|
||||
long[] timers,
|
||||
long[] counters
|
||||
) {
|
||||
private static final char[][] INDENT_PATTERNS = new char[][] {
|
||||
"|---".toCharArray(),
|
||||
"|+++".toCharArray(),
|
||||
};
|
||||
|
||||
public List<String> dumpToString() {
|
||||
final List<LProfileGraph.GraphNode> graphDFS = this.graph.getDFS();
|
||||
final Reference2ReferenceOpenHashMap<LProfileGraph.GraphNode, ProfileNode> nodeMap = new Reference2ReferenceOpenHashMap<>();
|
||||
@@ -232,14 +230,10 @@ public final class LeafProfiler {
|
||||
totalTime += node.totalTime;
|
||||
}
|
||||
|
||||
ProfileNode profileNode;
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
while ((profileNode = orderedNodes.pollFirst()) != null) {
|
||||
if (profileNode.nodeId != LProfileGraph.ROOT_NODE && profileNode.totalCount == 0L) {
|
||||
// skip nodes not recorded
|
||||
continue;
|
||||
}
|
||||
final ArrayDeque<ProfileNode> flatOrderedNodes = new ArrayDeque<>();
|
||||
|
||||
ProfileNode profileNode;
|
||||
while ((profileNode = orderedNodes.pollFirst()) != null) {
|
||||
final int depth = profileNode.depth;
|
||||
profileNode.children.sort((final ProfileNode p1, final ProfileNode p2) -> {
|
||||
final int typeCompare = p1.profiler.type().compareTo(p2.profiler.type());
|
||||
@@ -257,12 +251,32 @@ public final class LeafProfiler {
|
||||
}
|
||||
});
|
||||
|
||||
boolean first = true;
|
||||
for (int i = profileNode.children.size() - 1; i >= 0; --i) {
|
||||
final ProfileNode child = profileNode.children.get(i);
|
||||
if (child.totalCount == 0L) {
|
||||
// skip nodes not recorded
|
||||
continue;
|
||||
}
|
||||
if (first) {
|
||||
child.lastChild = true;
|
||||
first = false;
|
||||
}
|
||||
child.depth = depth + 1;
|
||||
orderedNodes.addFirst(child);
|
||||
}
|
||||
|
||||
flatOrderedNodes.addLast(profileNode);
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final IntList closed = new IntArrayList();
|
||||
while ((profileNode = flatOrderedNodes.pollFirst()) != null) {
|
||||
final int depth = profileNode.depth;
|
||||
closed.removeIf((int d) -> d >= depth);
|
||||
if (profileNode.lastChild) {
|
||||
closed.add(depth);
|
||||
}
|
||||
if (profileNode.nodeId == LProfileGraph.ROOT_NODE) {
|
||||
// don't display root
|
||||
continue;
|
||||
@@ -280,9 +294,18 @@ public final class LeafProfiler {
|
||||
// <indent>#<name> avg X sum Y
|
||||
builder.setLength(0);
|
||||
// prepare indent
|
||||
final char[] indent = INDENT_PATTERNS[ret.size() % INDENT_PATTERNS.length];
|
||||
for (int i = 0; i < depth; ++i) {
|
||||
builder.append(indent);
|
||||
if (i == depth - 1) {
|
||||
if (flatOrderedNodes.peekFirst() == null || profileNode.lastChild) {
|
||||
builder.append(" └─");
|
||||
} else {
|
||||
builder.append(" ├─");
|
||||
}
|
||||
} else if (!closed.contains(i + 1)) {
|
||||
builder.append(" │ ");
|
||||
} else {
|
||||
builder.append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
switch (profilerEntry.type()) {
|
||||
|
||||
@@ -209,7 +209,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
||||
final LevelChunkSection[] sections = chunk.getSections();
|
||||
for (int sectionY = this.minSection; sectionY <= this.maxSection; ++sectionY) {
|
||||
final LevelChunkSection section = sections[sectionY - this.minSection];
|
||||
if (section == null || section.hasOnlyAir()) {
|
||||
if (section.hasOnlyAir()) {
|
||||
// no sources in empty sections
|
||||
continue;
|
||||
}
|
||||
@@ -220,16 +220,17 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
||||
final PalettedContainer<BlockState> states = section.states;
|
||||
final int offY = sectionY << 4;
|
||||
|
||||
final BlockPos.MutableBlockPos mutablePos = this.mutablePos1;
|
||||
for (int index = 0; index < (16 * 16 * 16); ++index) {
|
||||
final BlockState state = states.get(index);
|
||||
this.mutablePos1.set(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15));
|
||||
mutablePos.set(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15));
|
||||
|
||||
if ((platformHooks.getLightEmission(state, world, this.mutablePos1)) == 0) {
|
||||
if ((platformHooks.getLightEmission(state, world, mutablePos)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// index = x | (z << 4) | (y << 8)
|
||||
sources.add(this.mutablePos1.immutable());
|
||||
sources.add(mutablePos.immutable());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ accessible method net/minecraft/server/level/ChunkMap upgradeChunkTag (Lnet/mine
|
||||
accessible field net/minecraft/server/level/ChunkMap worldGenContext Lnet/minecraft/world/level/chunk/status/WorldGenContext;
|
||||
accessible field net/minecraft/server/level/ChunkMap tickingGenerated Ljava/util/concurrent/atomic/AtomicInteger;
|
||||
accessible field net/minecraft/server/level/ChunkMap progressListener Lnet/minecraft/server/level/progress/ChunkProgressListener;
|
||||
accessible method net/minecraft/server/level/ChunkMap playerIsCloseEnoughForSpawning (Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/ChunkPos;)Z
|
||||
|
||||
# ChunkLevel
|
||||
accessible field net/minecraft/server/level/ChunkLevel FULL_CHUNK_LEVEL I
|
||||
@@ -293,3 +294,7 @@ accessible class net/minecraft/world/level/chunk/storage/RegionFile$ChunkBuffer
|
||||
|
||||
# DensityFunctions$EndIslandDensityFunction
|
||||
accessible class net/minecraft/world/level/levelgen/DensityFunctions$EndIslandDensityFunction
|
||||
|
||||
|
||||
# LocalMobCapCalculator$MobCounts
|
||||
accessible class net/minecraft/world/level/LocalMobCapCalculator$MobCounts
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
"blockstate_propertyaccess.IntegerPropertyMixin",
|
||||
"blockstate_propertyaccess.PropertyMixin",
|
||||
"blockstate_propertyaccess.StateHolderMixin",
|
||||
"chunk_getblock.ChunkAccessMixin",
|
||||
"chunk_getblock.LevelChunkMixin",
|
||||
"getblock.ChunkAccessMixin",
|
||||
"getblock.LevelChunkMixin",
|
||||
"getblock.LevelMixin",
|
||||
"chunk_system.ChunkBufferMixin",
|
||||
"chunk_system.ChunkGeneratorMixin",
|
||||
"chunk_system.ChunkHolderMixin",
|
||||
@@ -56,6 +57,7 @@
|
||||
"chunk_tick_iteration.ChunkMapMixin",
|
||||
"chunk_tick_iteration.DistanceManagerMixin",
|
||||
"chunk_tick_iteration.ServerChunkCacheMixin",
|
||||
"chunk_tick_iteration.ServerLevelMixin",
|
||||
"collisions.ArmorStandMixin",
|
||||
"collisions.ArrayVoxelShapeMixin",
|
||||
"collisions.BitSetDiscreteVoxelShapeMixin",
|
||||
@@ -89,7 +91,12 @@
|
||||
"fast_palette.SingleValuePaletteMixin",
|
||||
"fluid.FlowingFluidMixin",
|
||||
"fluid.FluidStateMixin",
|
||||
"fluid.MappedRegistryMixin",
|
||||
"keep_alive_client.ServerGamePacketListenerImplMixin",
|
||||
"mob_spawning.EntityTypeMixin",
|
||||
"mob_spawning.LocalMobCapCalculator$MobCountsMixin",
|
||||
"mob_spawning.MobSpawnSettingsMixin",
|
||||
"mob_spawning.NaturalSpawnerMixin",
|
||||
"poi_lookup.AcquirePoiMixin",
|
||||
"poi_lookup.PoiManagerMixin",
|
||||
"poi_lookup.PortalForcerMixin",
|
||||
|
||||
Reference in New Issue
Block a user