Compare commits
15 Commits
v0.1.0-bet
...
v0.1.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4597f04a6d | ||
|
|
20764890a7 | ||
|
|
f719abdb64 | ||
|
|
0dce47bba6 | ||
|
|
47584ceee1 | ||
|
|
2fc858a683 | ||
|
|
68ea18bc51 | ||
|
|
9944946b29 | ||
|
|
ec1120ed10 | ||
|
|
8040c7a264 | ||
|
|
a9e36795e5 | ||
|
|
a70073ae3e | ||
|
|
6724814c02 | ||
|
|
f32a08738e | ||
|
|
56e48ed069 |
53
.github/ISSUE_TEMPLATE/a_incompatibility.yml
vendored
Normal file
53
.github/ISSUE_TEMPLATE/a_incompatibility.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Mod Incompatibility
|
||||
description: Report an incompatibility with another mod.
|
||||
title: '[Incompatibility: <NeoForge/Fabric/NeoForge & Fabric>]: Mod Name'
|
||||
labels:
|
||||
- mod-incompatibility
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1 or 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: moonrise_ver
|
||||
attributes:
|
||||
label: Moonrise Version
|
||||
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: >-
|
||||
The mod loader(s) this conflict happens with. When selecting 'Both' it
|
||||
is expected you will submit logs for both as well. Be sure to update the
|
||||
issue title.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs_crashes
|
||||
attributes:
|
||||
label: Logs and Crash Reports
|
||||
description: >-
|
||||
Include links to all relevant logs and crash reports (prefer using
|
||||
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||
applicable fill in 'N/A' (it is expected you will provide context
|
||||
below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Provide any relevant context, including version of the conflicting mod.
|
||||
validations:
|
||||
required: true
|
||||
55
.github/ISSUE_TEMPLATE/b_bug.yml
vendored
Normal file
55
.github/ISSUE_TEMPLATE/b_bug.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Bug Report
|
||||
description: Report a bug or Vanilla behavior parity issue.
|
||||
title: '[Bug: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||
labels:
|
||||
- bug
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1 or 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: moonrise_ver
|
||||
attributes:
|
||||
label: Moonrise Version
|
||||
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: >-
|
||||
The mod loader(s) this bug happens with. When selecting 'Both' it
|
||||
is expected you will submit logs for both as well. Be sure to update the
|
||||
issue title.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs_crashes
|
||||
attributes:
|
||||
label: Logs and Crash Reports
|
||||
description: >-
|
||||
Include links to all relevant logs and crash reports (prefer using
|
||||
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||
applicable fill in 'N/A' (it is expected you will provide context
|
||||
below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: >-
|
||||
Provide any relevant context, including expected vs. actual behavior,
|
||||
and reproduction steps.
|
||||
validations:
|
||||
required: true
|
||||
65
.github/ISSUE_TEMPLATE/c_performance.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/c_performance.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Performance Problem
|
||||
description: Report a performance problem that doesn't fall under the bug category.
|
||||
title: '[Performance: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||
labels:
|
||||
- performance
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1, 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: moonrise_ver
|
||||
attributes:
|
||||
label: Moonrise Version
|
||||
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: >-
|
||||
The mod loader(s) this bug happens with. When selecting 'Both' it is
|
||||
expected you have tested both as well. Be sure to update the issue
|
||||
title.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs_crashes
|
||||
attributes:
|
||||
label: Logs and Crash Reports
|
||||
description: >-
|
||||
Include links to all relevant logs and crash reports (prefer using
|
||||
https://gist.github.com/ or https://mclo.gs/). In case this is not
|
||||
applicable fill in 'N/A' (it is expected you will provide context
|
||||
below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: profiler
|
||||
attributes:
|
||||
label: Profiler Data
|
||||
description: >-
|
||||
Provide profiler data showing the problem, from Spark or other tools. In
|
||||
case this is not applicable fill in 'N/A' (it is expected you will
|
||||
provide context below).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: >-
|
||||
Provide any relevant context, including reproduction steps, world data,
|
||||
videos, etc.
|
||||
validations:
|
||||
required: true
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord
|
||||
url: https://discord.gg/tuinity
|
||||
about: If you are unsure about something or have general questions, ask in our Discord before opening an issue.
|
||||
34
.github/ISSUE_TEMPLATE/d_feature.yml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/d_feature.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Feature Request
|
||||
description: Request a new feature.
|
||||
title: '[Feature Request: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
|
||||
labels:
|
||||
- enhancement
|
||||
- needs-triage
|
||||
body:
|
||||
- type: input
|
||||
id: mc_ver
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
placeholder: e.g. 1.21.1, 1.21.2
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mod_loader
|
||||
attributes:
|
||||
label: Mod Loader
|
||||
description: The mod loader(s) this feature request is relevant for.
|
||||
options:
|
||||
- NeoForge
|
||||
- Fabric
|
||||
- Both (NeoForge and Fabric)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Description
|
||||
description: >-
|
||||
Describe the feature request in detail, including alternatives
|
||||
considered.
|
||||
validations:
|
||||
required: true
|
||||
@@ -24,8 +24,7 @@ patches. Listed below are notable patches:
|
||||
| Mod | Status |
|
||||
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Lithium | <details><summary>✅ compatible</summary>Lithium optimises many of the same parts of the game as Moonrise, for example the chunk system. Moonrise will automatically disable conflicting parts of Lithium. This mechanism needs to be manually validated for each Moonrise and Lithium release.</details> |
|
||||
| Radium | <details><summary>✅ compatible</summary>Radium is an unofficial port of Lithium to NeoForge. Radium will automatically disable conflicting parts of itself when Moonrise is present. Any compatibility issues should be reported to Radium first.</details> |
|
||||
| FerriteCore | <details><summary>📝 requires config changes</summary>In `config/ferritecore-mixin.toml`:<br/>Set `replaceNeighborLookup` and `replacePropertyMap` to `false`</details> |
|
||||
| FerriteCore | <details><summary>✅ compatible</summary>FerriteCore optimises some of the same parts of the game as Moonrise. Moonrise will automatically disable conflicting parts of FerriteCore. This mechanism needs to be manually validated for each Moonrise and FerriteCore release.</details> |
|
||||
| C2ME | <details><summary>❌ incompatible</summary>C2ME is based around modifications to the chunk system, which Moonrise replaces wholesale. This makes them fundamentally incompatible.</details> |
|
||||
|
||||
## Configuration
|
||||
|
||||
25
build.gradle
25
build.gradle
@@ -126,6 +126,31 @@ subprojects {
|
||||
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 {
|
||||
|
||||
@@ -24,7 +24,8 @@ dependencies {
|
||||
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||
modImplementation "com.terraformersmc:modmenu:11.0.1"
|
||||
|
||||
modImplementation fabricApiLibs.fabric.api
|
||||
modImplementation fabricApiLibs.command.api.v2
|
||||
modImplementation fabricApiLibs.lifecycle.events.v1
|
||||
include fabricApiLibs.command.api.v2
|
||||
include fabricApiLibs.base
|
||||
}
|
||||
@@ -59,26 +60,7 @@ publishMods {
|
||||
incompatible(
|
||||
"not-enough-crashes",
|
||||
"starlight",
|
||||
"c2me-fabric"
|
||||
"c2me"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a run with lithium for compatibility testing
|
||||
sourceSets.create("lithium")
|
||||
configurations.create("lithium")
|
||||
loom {
|
||||
createRemapConfigurations(sourceSets.lithium)
|
||||
runs {
|
||||
register("lithiumClient") {
|
||||
client()
|
||||
property "mixin.debug", "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
|
||||
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
|
||||
}
|
||||
dependencies {
|
||||
modLithiumRuntimeOnly "maven.modrinth:lithium:${rootProject.lithium_version}"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ 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;
|
||||
@@ -18,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;
|
||||
@@ -28,6 +30,8 @@ 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);
|
||||
}
|
||||
@@ -85,7 +89,12 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
|
||||
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel) newChunk.getLevel(), newChunk);
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel) newChunk.getLevel(), newChunk);
|
||||
if (!(original instanceof ImposterProtoChunk)) {
|
||||
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,7 +109,9 @@ public final class FabricHooks implements PlatformHooks {
|
||||
|
||||
@Override
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk) {
|
||||
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel) chunk.getLevel(), chunk);
|
||||
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
|
||||
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel) chunk.getLevel(), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.fabric.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.tags.TagKey;
|
||||
@@ -9,7 +9,6 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
@@ -61,7 +60,7 @@ abstract class EntityMixin {
|
||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||
|
||||
final Level world = this.level;
|
||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||
@@ -69,7 +68,7 @@ abstract class EntityMixin {
|
||||
|
||||
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
|
||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||
|
||||
final boolean isPushable = this.isPushedByFluid();
|
||||
|
||||
@@ -41,34 +41,38 @@
|
||||
},
|
||||
"custom": {
|
||||
"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.world.temperature_cache": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.world.tick_scheduler": false,
|
||||
"mixin.world.explosions.block_raycast": false,
|
||||
"mixin.world.explosions.cache_exposure": false,
|
||||
"mixin.block.flatten_states": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.alloc.deep_passengers": false,
|
||||
"mixin.alloc.chunk_ticking": false,
|
||||
"mixin.alloc.entity_tracker": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.ai.poi": false,
|
||||
"mixin.chunk.no_validation": false,
|
||||
"mixin.chunk.entity_class_groups": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.util.entity_movement_tracking": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.entity.collisions.intersection": false,
|
||||
"mixin.entity.collisions.movement": false,
|
||||
"mixin.entity.collisions.unpushable_cramming": false
|
||||
}
|
||||
"mixin.entity.collisions.unpushable_cramming": false,
|
||||
"mixin.entity.replace_entitytype_predicates": false,
|
||||
"mixin.math.fast_blockpos": false,
|
||||
"mixin.math.fast_util": false,
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space": false,
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
|
||||
"mixin.shapes.blockstate_cache": false,
|
||||
"mixin.shapes.optimized_matching": false,
|
||||
"mixin.shapes.specialized_shapes": false,
|
||||
"mixin.util.block_tracking": false,
|
||||
"mixin.util.entity_movement_tracking": false,
|
||||
"mixin.world.block_entity_ticking": false,
|
||||
"mixin.world.chunk_access": false,
|
||||
"mixin.world.explosions.block_raycast": false,
|
||||
"mixin.world.explosions.cache_exposure": false,
|
||||
"mixin.world.temperature_cache": false,
|
||||
"mixin.world.tick_scheduler": false
|
||||
},
|
||||
"ferritecore:disabled_options": [
|
||||
"replaceNeighborLookup",
|
||||
"replacePropertyMap"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ org.gradle.daemon=false
|
||||
minecraft_version=1.21.1
|
||||
supported_minecraft_versions=1.21,1.21.1
|
||||
loader_version=0.16.5
|
||||
neoforge_version=21.1.62
|
||||
neoforge_version=21.1.79
|
||||
snakeyaml_version=2.2
|
||||
concurrentutil_version=0.0.2-SNAPSHOT
|
||||
cloth_version=15.0.128
|
||||
lithium_version=mc1.21.1-0.13.1
|
||||
# version ids from modrinth
|
||||
fabric_lithium_version=frXUdgvL
|
||||
neo_lithium_version=KhdehJ6l
|
||||
# Mod Properties
|
||||
mod_version=0.1.0-beta.6
|
||||
mod_version=0.1.0-beta.9
|
||||
maven_group=ca.spottedleaf.moonrise
|
||||
archives_base_name=moonrise
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.neoforge.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.neoforge.patches.collisions.FluidPushCalculation;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -10,7 +10,6 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
@@ -59,7 +58,7 @@ abstract class EntityMixin implements IEntityExtension {
|
||||
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
||||
|
||||
final Level world = this.level;
|
||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(boundingBox.minX);
|
||||
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
||||
@@ -67,7 +66,7 @@ abstract class EntityMixin implements IEntityExtension {
|
||||
|
||||
// note: bounds are exclusive in Vanilla, so we subtract 1
|
||||
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
||||
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
||||
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
||||
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
@@ -13,6 +13,10 @@ displayURL = "https://github.com/Tuinity/Moonrise"
|
||||
authors = "Spottedleaf"
|
||||
description = "Optimisation mod for the dedicated and integrated server."
|
||||
displayTest = "IGNORE_ALL_VERSION"
|
||||
"ferritecore:disabled_options" = [
|
||||
"replaceNeighborLookup",
|
||||
"replacePropertyMap"
|
||||
]
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "neoforge"
|
||||
@@ -36,10 +40,6 @@ type = "incompatible"
|
||||
modId = "starlight"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
modId = "lithium"
|
||||
type = "incompatible"
|
||||
|
||||
[[dependencies.moonrise]]
|
||||
# unofficial lithium port
|
||||
modId = "canary"
|
||||
@@ -54,3 +54,33 @@ config = "moonrise.mixins.json"
|
||||
|
||||
[[mixins]]
|
||||
config = "moonrise-neoforge.mixins.json"
|
||||
|
||||
["lithium:options"]
|
||||
"mixin.ai.poi" = false
|
||||
"mixin.alloc.chunk_ticking" = false
|
||||
"mixin.alloc.deep_passengers" = false
|
||||
"mixin.alloc.entity_tracker" = false
|
||||
"mixin.block.flatten_states" = false
|
||||
"mixin.chunk.entity_class_groups" = false
|
||||
"mixin.chunk.no_validation" = false
|
||||
"mixin.collections.chunk_tickets" = false
|
||||
"mixin.collections.entity_ticking" = false
|
||||
"mixin.entity.collisions.intersection" = false
|
||||
"mixin.entity.collisions.movement" = false
|
||||
"mixin.entity.collisions.unpushable_cramming" = false
|
||||
"mixin.entity.replace_entitytype_predicates" = false
|
||||
"mixin.math.fast_blockpos" = false
|
||||
"mixin.math.fast_util" = false
|
||||
"mixin.minimal_nonvanilla.collisions.empty_space" = false
|
||||
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets" = false
|
||||
"mixin.shapes.blockstate_cache" = false
|
||||
"mixin.shapes.optimized_matching" = false
|
||||
"mixin.shapes.specialized_shapes" = false
|
||||
"mixin.util.block_tracking" = false
|
||||
"mixin.util.entity_movement_tracking" = false
|
||||
"mixin.world.block_entity_ticking" = false
|
||||
"mixin.world.chunk_access" = false
|
||||
"mixin.world.explosions.block_raycast" = false
|
||||
"mixin.world.explosions.cache_exposure" = false
|
||||
"mixin.world.temperature_cache" = false
|
||||
"mixin.world.tick_scheduler" = false
|
||||
|
||||
@@ -24,7 +24,7 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.295" apply false
|
||||
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.300" apply false
|
||||
id 'com.gradleup.shadow' version '8.3.0' apply false
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ dependencyResolutionManagement {
|
||||
}
|
||||
versionCatalogs {
|
||||
create("fabricApiLibs") {
|
||||
from("net.fabricmc.fabric-api:fabric-api-catalog:0.103.0+1.21.1")
|
||||
from("net.fabricmc.fabric-api:fabric-api-catalog:0.107.0+1.21.1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
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 ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArraySet;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrays;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -16,7 +16,6 @@ import net.minecraft.world.entity.EntityDimensions;
|
||||
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.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
@@ -222,7 +221,7 @@ abstract class EntityMixin {
|
||||
final int maxChunkY = maxBlockY >> 4;
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
@@ -331,7 +330,7 @@ abstract class EntityMixin {
|
||||
return new NoneMatchStream<>(true);
|
||||
}
|
||||
|
||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
@@ -406,7 +405,7 @@ abstract class EntityMixin {
|
||||
return;
|
||||
}
|
||||
|
||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.mixin.collisions;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
||||
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
@@ -139,7 +139,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
int lastChunkY = Integer.MIN_VALUE;
|
||||
int lastChunkZ = Integer.MIN_VALUE;
|
||||
|
||||
final int minSection = ((GetBlockLevel)level).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(level);
|
||||
|
||||
for (;;) {
|
||||
currPos.set(currX, currY, currZ);
|
||||
@@ -334,13 +334,13 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
*/
|
||||
@Override
|
||||
public Optional<BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
||||
final int minSection = ((GetBlockLevel)(Level)(Object)this).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection((Level)(Object)this);
|
||||
|
||||
final int minBlockX = Mth.floor(aabb.minX - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockX = Mth.floor(aabb.maxX + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - CollisionUtil.COLLISION_EPSILON) - 1);
|
||||
final int maxBlockY = Math.min((((GetBlockLevel)(Level)(Object)this).moonrise$getMaxSection() << 4) + 16, Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection((Level)(Object)this) << 4) + 16, Mth.floor(aabb.maxY + CollisionUtil.COLLISION_EPSILON) + 1);
|
||||
|
||||
final int minBlockZ = Mth.floor(aabb.minZ - CollisionUtil.COLLISION_EPSILON) - 1;
|
||||
final int maxBlockZ = Mth.floor(aabb.maxZ + CollisionUtil.COLLISION_EPSILON) + 1;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package ca.spottedleaf.moonrise.mixin.getblock;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.getblock.GetBlockLevel;
|
||||
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;
|
||||
@@ -13,7 +14,16 @@ 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 GetBlockLevel, LevelAccessor, AutoCloseable {
|
||||
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
|
||||
@Unique
|
||||
private int height;
|
||||
|
||||
@Unique
|
||||
private int minBuildHeight;
|
||||
|
||||
@Unique
|
||||
private int maxBuildHeight;
|
||||
|
||||
@Unique
|
||||
private int minSection;
|
||||
@@ -22,30 +32,7 @@ abstract class LevelMixin implements GetBlockLevel, LevelAccessor, AutoCloseable
|
||||
private int maxSection;
|
||||
|
||||
@Unique
|
||||
private int minBuildHeight;
|
||||
|
||||
@Unique
|
||||
private int maxBuildHeight;
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMinSection() {
|
||||
return this.minSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMaxSection() {
|
||||
return this.maxSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMinBuildHeight() {
|
||||
return this.minBuildHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int moonrise$getMaxBuildHeight() {
|
||||
return this.maxBuildHeight;
|
||||
}
|
||||
private int sectionsCount;
|
||||
|
||||
/**
|
||||
* @reason Init min/max section
|
||||
@@ -54,14 +41,43 @@ abstract class LevelMixin implements GetBlockLevel, LevelAccessor, AutoCloseable
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
value = "CTOR_HEAD"
|
||||
)
|
||||
)
|
||||
private void init(final CallbackInfo ci) {
|
||||
this.minSection = WorldUtil.getMinSection(this);
|
||||
this.maxSection = WorldUtil.getMaxSection(this);
|
||||
this.minBuildHeight = this.getMinBuildHeight();
|
||||
this.maxBuildHeight = this.getMaxBuildHeight();
|
||||
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
|
||||
@@ -73,4 +89,24 @@ abstract class LevelMixin implements GetBlockLevel, LevelAccessor, AutoCloseable
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import it.unimi.dsi.fastutil.objects.AbstractReference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -27,7 +27,7 @@ public final class ZeroCollidingReferenceStateTable<O, S> {
|
||||
|
||||
public ZeroCollidingReferenceStateTable(final Collection<Property<?>> properties) {
|
||||
this.propertyToIndexer = new Int2ObjectOpenHashMap<>(properties.size());
|
||||
this.properties = new ReferenceOpenHashSet<>(properties);
|
||||
this.properties = new ReferenceArrayList<>(properties);
|
||||
|
||||
final List<Property<?>> sortedProperties = new ArrayList<>(properties);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.getblock.GetBlockLevel;
|
||||
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleList;
|
||||
@@ -1934,13 +1934,13 @@ public final class CollisionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
|
||||
final int minSection = WorldUtil.getMinSection(world);
|
||||
|
||||
final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
|
||||
final int 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((((GetBlockLevel)world).moonrise$getMaxSection() << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
||||
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
||||
|
||||
final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
|
||||
final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.patches.getblock;
|
||||
|
||||
public interface GetBlockLevel {
|
||||
|
||||
public int moonrise$getMinSection();
|
||||
|
||||
public int moonrise$getMaxSection();
|
||||
|
||||
public int moonrise$getMinBuildHeight();
|
||||
|
||||
public int moonrise$getMaxBuildHeight();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user