mirror of
https://github.com/Samsuik/Sakura.git
synced 2025-12-21 15:59:26 +00:00
Rewrite local configuration api and expand "physics-version"
This commit is contained in:
@@ -23,3 +23,6 @@ public net.minecraft.world.level.Level neighborUpdater
|
||||
public net.minecraft.world.level.block.RedStoneWireBlock turbo
|
||||
public net.minecraft.world.level.entity.EntityTickList entities
|
||||
public net.minecraft.world.level.material.FlowingFluid getLegacyLevel(Lnet/minecraft/world/level/material/FluidState;)I
|
||||
public net.minecraft.world.entity.Entity axisStepOrder(Lnet/minecraft/world/phys/Vec3;)Ljava/lang/Iterable;
|
||||
public net.minecraft.world.entity.Entity$Movement
|
||||
public net.minecraft.world.entity.Entity addMovementThisTick(Lnet/minecraft/world/entity/Entity$Movement;)V
|
||||
@@ -1,5 +1,13 @@
|
||||
--- a/paper-api/build.gradle.kts
|
||||
+++ b/paper-api/build.gradle.kts
|
||||
@@ -68,6 +_,7 @@
|
||||
api("org.apache.maven:maven-resolver-provider:3.9.6") // make API dependency for Paper Plugins
|
||||
implementation("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
|
||||
implementation("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18")
|
||||
+ compileOnly("org.spongepowered:configurate-yaml:4.2.0")
|
||||
|
||||
// Annotations - Slowly migrate to jspecify
|
||||
val annotations = "org.jetbrains:annotations:$annotationsVersion"
|
||||
@@ -90,7 +_,7 @@
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
--- a/src/main/java/org/bukkit/World.java
|
||||
+++ b/src/main/java/org/bukkit/World.java
|
||||
@@ -205,6 +_,10 @@
|
||||
return new Location(this, x, y, z);
|
||||
}
|
||||
// Paper end
|
||||
+ // Sakura start
|
||||
+ @NotNull
|
||||
+ me.samsuik.sakura.local.storage.LocalStorageHandler getStorageHandler();
|
||||
+ // Sakura end
|
||||
@@ -4387,6 +_,11 @@
|
||||
void setSendViewDistance(int viewDistance);
|
||||
// Paper end - view distance api
|
||||
|
||||
+ // Sakura start - local config api
|
||||
+ @NotNull
|
||||
+ me.samsuik.sakura.configuration.local.LocalConfigurationAccessor localConfig();
|
||||
+ // Sakura end - local config api
|
||||
+
|
||||
/**
|
||||
* Gets the highest non-empty (impassable) block at the given coordinates.
|
||||
* Gets all generated structures that intersect the chunk at the given
|
||||
* coordinates. <br>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import me.samsuik.sakura.explosion.durable.DurableMaterialsContainer.SealedDurableMaterialsContainer;
|
||||
import me.samsuik.sakura.mechanics.MinecraftMechanicsTarget;
|
||||
import me.samsuik.sakura.redstone.RedstoneConfiguration;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A key for a configurable value.
|
||||
*/
|
||||
@NullMarked
|
||||
public record ConfigurableKey<T>(Class<T> expectedType) {
|
||||
public static final ConfigurableKey<MinecraftMechanicsTarget> MECHANICS_TARGET = new ConfigurableKey<>(MinecraftMechanicsTarget.class);
|
||||
public static final ConfigurableKey<SealedDurableMaterialsContainer> DURABLE_MATERIALS = new ConfigurableKey<>(SealedDurableMaterialsContainer.class);
|
||||
public static final ConfigurableKey<RedstoneConfiguration> REDSTONE_BEHAVIOUR = new ConfigurableKey<>(RedstoneConfiguration.class);
|
||||
public static final ConfigurableKey<Boolean> CONSISTENT_EXPLOSION_RADIUS = new ConfigurableKey<>(Boolean.class);
|
||||
public static final ConfigurableKey<Integer> LAVA_FLOW_SPEED = new ConfigurableKey<>(Integer.class);
|
||||
|
||||
public T validate(@Nullable final Object value) {
|
||||
final T casted = this.conform(value);
|
||||
if (casted == null) {
|
||||
throw new IllegalArgumentException("Value cannot be null for key " + this);
|
||||
}
|
||||
return casted;
|
||||
}
|
||||
|
||||
public @Nullable T conform(@Nullable final Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (!this.expectedType.isInstance(value)) {
|
||||
throw new IllegalArgumentException("Expected type " + this.expectedType.getName() + " but got " + value.getClass().getName());
|
||||
}
|
||||
return this.expectedType.cast(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A container for configuration values.
|
||||
*/
|
||||
@NullMarked
|
||||
public sealed class ConfigurationContainer implements Container<ConfigurableKey<?>, Object> {
|
||||
private final IdentityHashMap<ConfigurableKey<?>, Object> values = new IdentityHashMap<>();
|
||||
|
||||
public static SealedConfigurationContainer sealedContainer(final Object... contents) {
|
||||
if (contents.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Expected an even number of contents, got " + contents.length);
|
||||
}
|
||||
final IdentityHashMap<ConfigurableKey<?>, Object> values = new IdentityHashMap<>();
|
||||
for (int index = 0; index < contents.length; index += 2) {
|
||||
final Object key = contents[index];
|
||||
final Object value = contents[index + 1];
|
||||
if (!(key instanceof ConfigurableKey<?> configurableKey)) {
|
||||
throw new IllegalArgumentException("Key at index " + index + " must be of type ConfigurableKey");
|
||||
}
|
||||
values.put(configurableKey, configurableKey.validate(value));
|
||||
}
|
||||
return new SealedConfigurationContainer(values);
|
||||
}
|
||||
|
||||
private ConfigurationContainer(final IdentityHashMap<ConfigurableKey<?>, Object> values) {
|
||||
this.values.putAll(values);
|
||||
}
|
||||
|
||||
public ConfigurationContainer() {}
|
||||
|
||||
public <V> @Nullable V set(final ConfigurableKey<V> key, final V value) {
|
||||
Preconditions.checkNotNull(value, "Value cannot be null");
|
||||
return key.conform(this.values.put(key, value));
|
||||
}
|
||||
|
||||
public <V> @Nullable V remove(final ConfigurableKey<V> key) {
|
||||
return key.conform(this.values.remove(key));
|
||||
}
|
||||
|
||||
public final <V> @Nullable V get(final ConfigurableKey<V> key) {
|
||||
return key.conform(this.values.get(key));
|
||||
}
|
||||
|
||||
public final <V> Optional<V> getOptional(final ConfigurableKey<V> key) {
|
||||
return Optional.ofNullable(this.get(key));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final void fillAbsentValues(final ConfigurationContainer container) {
|
||||
for (final Map.Entry<ConfigurableKey<?>, Object> entry : container.values.entrySet()) {
|
||||
this.values.putIfAbsent(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.values.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<ConfigurableKey<?>, Object> contents() {
|
||||
return Map.copyOf(this.values);
|
||||
}
|
||||
|
||||
public final ConfigurationContainer open() {
|
||||
return this instanceof SealedConfigurationContainer
|
||||
? new ConfigurationContainer(this.values)
|
||||
: this;
|
||||
}
|
||||
|
||||
public final SealedConfigurationContainer seal() {
|
||||
return this instanceof SealedConfigurationContainer sealed
|
||||
? sealed
|
||||
: new SealedConfigurationContainer(this.values);
|
||||
}
|
||||
|
||||
public static final class SealedConfigurationContainer extends ConfigurationContainer {
|
||||
private SealedConfigurationContainer(final IdentityHashMap<ConfigurableKey<?>, Object> values) {
|
||||
super(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V set(final ConfigurableKey<V> key, final V value) {
|
||||
throw new UnsupportedOperationException("Container is sealed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V remove(final ConfigurableKey<V> key) {
|
||||
throw new UnsupportedOperationException("Container is sealed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException("Container is sealed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@NullMarked
|
||||
public interface Container<K, V> {
|
||||
Map<K, V> contents();
|
||||
|
||||
Container<K, V> open();
|
||||
|
||||
Container<K, V> seal();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import io.papermc.paper.math.Position;
|
||||
import me.samsuik.sakura.configuration.local.ConfigurationContainer.SealedConfigurationContainer;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An accessor for local configuration containers.
|
||||
*/
|
||||
@NullMarked
|
||||
public interface LocalConfigurationAccessor {
|
||||
default <V> void set(final BoundingBox area, final ConfigurableKey<V> key, final V value) {
|
||||
final ConfigurationContainer container = this.get(area);
|
||||
final ConfigurationContainer newContainer = container != null
|
||||
? container.open()
|
||||
: new ConfigurationContainer();
|
||||
newContainer.set(key, value);
|
||||
this.set(area, newContainer.seal());
|
||||
}
|
||||
|
||||
default void remove(final BoundingBox area, final ConfigurableKey<?> key) {
|
||||
final ConfigurationContainer container = this.get(area);
|
||||
if (container != null) {
|
||||
final ConfigurationContainer newContainer = container.open();
|
||||
newContainer.remove(key);
|
||||
this.set(area, newContainer.seal());
|
||||
}
|
||||
}
|
||||
|
||||
default <T> @Nullable T get(final BoundingBox area, final ConfigurableKey<T> key) {
|
||||
final ConfigurationContainer container = this.get(area);
|
||||
return container == null ? null : container.get(key);
|
||||
}
|
||||
|
||||
void set(final BoundingBox area, final SealedConfigurationContainer container);
|
||||
|
||||
@Nullable SealedConfigurationContainer remove(final BoundingBox area);
|
||||
|
||||
@Nullable SealedConfigurationContainer get(final BoundingBox area);
|
||||
|
||||
default <T> @Nullable T getValue(final Vector vector, final ConfigurableKey<T> key) {
|
||||
final ConfigurationContainer container = this.getContainer(vector);
|
||||
return container != null ? container.get(key) : null;
|
||||
}
|
||||
|
||||
default <T> @Nullable T getValue(final Position position, final ConfigurableKey<T> key) {
|
||||
final ConfigurationContainer container = this.getContainer(position);
|
||||
return container != null ? container.get(key) : null;
|
||||
}
|
||||
|
||||
default @Nullable ConfigurationContainer getContainer(final Vector vector) {
|
||||
return this.getContainer(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
|
||||
}
|
||||
|
||||
default @Nullable ConfigurationContainer getContainer(final Position position) {
|
||||
return this.getContainer(position.blockX(), position.blockY(), position.blockZ());
|
||||
}
|
||||
|
||||
@Nullable ConfigurationContainer getContainer(final int x, final int y, final int z);
|
||||
|
||||
default List<BoundingBox> getAreas(final Vector vector) {
|
||||
return this.getAreas(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
|
||||
}
|
||||
|
||||
default List<BoundingBox> getAreas(final Position position) {
|
||||
return this.getAreas(position.blockX(), position.blockY(), position.blockZ());
|
||||
}
|
||||
|
||||
List<BoundingBox> getAreas(final int x, final int y, final int z);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.samsuik.sakura.explosion.durable;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
@NullMarked
|
||||
@ConfigSerializable
|
||||
public record DurableMaterial(int durability, float resistance, boolean onlyDamagedByTnt) {
|
||||
public DurableMaterial(final int durability, final float resistance) {
|
||||
this(durability, resistance, true);
|
||||
}
|
||||
|
||||
public boolean replaceBlastResistance() {
|
||||
return this.resistance >= 0.0f;
|
||||
}
|
||||
|
||||
public boolean applyDurability() {
|
||||
return this.durability >= 0;
|
||||
}
|
||||
|
||||
public static DurableMaterial durability(final int durability) {
|
||||
return new DurableMaterial(durability, 0.0f);
|
||||
}
|
||||
|
||||
public static DurableMaterial resistance(final float resistance) {
|
||||
return new DurableMaterial(1, resistance);
|
||||
}
|
||||
|
||||
public static DurableMaterial likeSand(final int durability) {
|
||||
return new DurableMaterial(durability, 3.0f);
|
||||
}
|
||||
|
||||
public static DurableMaterial likeCobblestone(final int durability) {
|
||||
return new DurableMaterial(durability, 6.0f);
|
||||
}
|
||||
|
||||
public static DurableMaterial likeEndstone(final int durability) {
|
||||
return new DurableMaterial(durability, 9.0f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package me.samsuik.sakura.explosion.durable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import me.samsuik.sakura.configuration.local.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A container for durable materials.
|
||||
*/
|
||||
@NullMarked
|
||||
public sealed class DurableMaterialsContainer implements Container<BlockType, DurableMaterial> {
|
||||
private final Map<BlockType, DurableMaterial> materials = new IdentityHashMap<>();
|
||||
|
||||
public static SealedDurableMaterialsContainer sealedContainer(final Object... contents) {
|
||||
if (contents.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Expected an even number of contents, got " + contents.length);
|
||||
}
|
||||
final Map<BlockType, DurableMaterial> materials = new IdentityHashMap<>();
|
||||
for (int index = 0; index < contents.length; index += 2) {
|
||||
Object key = contents[index];
|
||||
Object value = contents[index + 1];
|
||||
if (key instanceof Material bukkitMaterial) {
|
||||
key = blockTypeFromBukkitMaterial(bukkitMaterial);
|
||||
}
|
||||
if (!(key instanceof BlockType blockType)) {
|
||||
throw new IllegalArgumentException("Key at index " + index + " must be of type BlockType or Material");
|
||||
}
|
||||
if (!(value instanceof DurableMaterial material)) {
|
||||
throw new IllegalArgumentException("Value at index " + (index + 1) + " must be of type DurableMaterial");
|
||||
}
|
||||
materials.put(blockType, material);
|
||||
}
|
||||
return new SealedDurableMaterialsContainer(materials);
|
||||
}
|
||||
|
||||
private DurableMaterialsContainer(final Map<BlockType, DurableMaterial> materials) {
|
||||
this.materials.putAll(materials);
|
||||
}
|
||||
|
||||
public DurableMaterialsContainer() {}
|
||||
|
||||
private static BlockType blockTypeFromBukkitMaterial(final Material bukkitMaterial) {
|
||||
final BlockType blockType = bukkitMaterial.asBlockType();
|
||||
Preconditions.checkNotNull(blockType, "Material " + bukkitMaterial + " is not a block");
|
||||
return blockType;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final @Nullable DurableMaterial set(final Material bukkitMaterial, final DurableMaterial material) {
|
||||
return this.set(blockTypeFromBukkitMaterial(bukkitMaterial), material);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final @Nullable DurableMaterial remove(final Material bukkitMaterial) {
|
||||
return this.remove(blockTypeFromBukkitMaterial(bukkitMaterial));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final @Nullable DurableMaterial get(final Material bukkitMaterial) {
|
||||
return this.get(blockTypeFromBukkitMaterial(bukkitMaterial));
|
||||
}
|
||||
|
||||
public @Nullable DurableMaterial set(final BlockType blockType, final DurableMaterial material) {
|
||||
Preconditions.checkNotNull(material, "Material cannot be null");
|
||||
return this.materials.put(blockType, material);
|
||||
}
|
||||
|
||||
public @Nullable DurableMaterial remove(final BlockType blockType) {
|
||||
return this.materials.remove(blockType);
|
||||
}
|
||||
|
||||
public final @Nullable DurableMaterial get(final BlockType blockType) {
|
||||
return this.materials.get(blockType);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.materials.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<BlockType, DurableMaterial> contents() {
|
||||
return Map.copyOf(this.materials);
|
||||
}
|
||||
|
||||
public final DurableMaterialsContainer open() {
|
||||
return this instanceof SealedDurableMaterialsContainer
|
||||
? new DurableMaterialsContainer(this.materials)
|
||||
: this;
|
||||
}
|
||||
|
||||
public final SealedDurableMaterialsContainer seal() {
|
||||
return this instanceof SealedDurableMaterialsContainer sealed
|
||||
? sealed
|
||||
: new SealedDurableMaterialsContainer(this.materials);
|
||||
}
|
||||
|
||||
public static final class SealedDurableMaterialsContainer extends DurableMaterialsContainer {
|
||||
private SealedDurableMaterialsContainer(final Map<BlockType, DurableMaterial> materials) {
|
||||
super(materials);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DurableMaterial set(final BlockType key, final DurableMaterial value) {
|
||||
throw new UnsupportedOperationException("Container is sealed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DurableMaterial remove(final BlockType key) {
|
||||
throw new UnsupportedOperationException("Container is sealed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException("Container is sealed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package me.samsuik.sakura.local;
|
||||
|
||||
import io.papermc.paper.math.Position;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
@Deprecated(forRemoval = true)
|
||||
public record LocalRegion(int minX, int minZ, int maxX, int maxZ) {
|
||||
public static LocalRegion from(BoundingBox boundingBox) {
|
||||
return of(boundingBox.getMin(), boundingBox.getMax());
|
||||
}
|
||||
|
||||
public static LocalRegion of(Vector min, Vector max) {
|
||||
return of(min.getBlockX(), min.getBlockZ(), max.getBlockX(), max.getBlockZ());
|
||||
}
|
||||
|
||||
public static LocalRegion of(Position min, Position max) {
|
||||
return of(min.blockX(), min.blockZ(), max.blockX(), max.blockZ());
|
||||
}
|
||||
|
||||
public static LocalRegion of(int minX, int minZ, int maxX, int maxZ) {
|
||||
return new LocalRegion(
|
||||
Math.min(minX, maxX), Math.min(minZ, maxZ),
|
||||
Math.max(minX, maxX), Math.max(minZ, maxZ)
|
||||
);
|
||||
}
|
||||
|
||||
public static LocalRegion at(int x, int z, int radius) {
|
||||
return new LocalRegion(x-radius, z-radius, x+radius, z+radius);
|
||||
}
|
||||
|
||||
public boolean intersects(LocalRegion region) {
|
||||
return (this.minX <= region.minX() && this.maxX >= region.minX() || this.maxX >= region.maxX() && this.minX < region.maxX())
|
||||
&& (this.minZ <= region.minZ() && this.maxZ >= region.minZ() || this.maxZ >= region.maxZ() && this.minZ < region.maxZ());
|
||||
}
|
||||
|
||||
public boolean contains(LocalRegion region) {
|
||||
return this.minX < region.minX() && this.maxX > region.maxX()
|
||||
&& this.maxZ < region.minZ() && this.maxZ > region.maxZ();
|
||||
}
|
||||
|
||||
public boolean contains(int x, int z) {
|
||||
return this.minX <= x && this.maxX >= x && this.minZ <= z && this.maxZ >= z;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package me.samsuik.sakura.local;
|
||||
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@NullMarked
|
||||
@Deprecated(forRemoval = true)
|
||||
public record LocalValueKey<T>(NamespacedKey key, Supplier<T> defaultSupplier) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || this.getClass() != o.getClass()) return false;
|
||||
|
||||
LocalValueKey<?> that = (LocalValueKey<?>) o;
|
||||
return this.key.equals(that.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.key.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package me.samsuik.sakura.local;
|
||||
|
||||
import me.samsuik.sakura.physics.PhysicsVersion;
|
||||
import me.samsuik.sakura.redstone.RedstoneImplementation;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public final class LocalValueKeys {
|
||||
private static final String NAMESPACE = "sakura";
|
||||
|
||||
public static final LocalValueKey<PhysicsVersion> PHYSICS_VERSION = create("physics-version", () -> PhysicsVersion.LATEST);
|
||||
public static final LocalValueKey<Map<Material, Map.Entry<Integer, Float>>> DURABLE_MATERIALS = create("durable-materials", HashMap::new);
|
||||
public static final LocalValueKey<RedstoneImplementation> REDSTONE_IMPLEMENTATION = create("redstone-implementation", () -> RedstoneImplementation.VANILLA);
|
||||
public static final LocalValueKey<Boolean> CONSISTENT_EXPLOSION_RADIUS = create("consistent-radius", () -> false);
|
||||
public static final LocalValueKey<Boolean> REDSTONE_CACHE = create("redstone-cache", () -> false);
|
||||
public static final LocalValueKey<Integer> LAVA_FLOW_SPEED = create("lava-flow-speed", () -> -1);
|
||||
|
||||
private static <T> LocalValueKey<T> create(String key, Supplier<T> supplier) {
|
||||
return new LocalValueKey<>(new NamespacedKey(NAMESPACE, key), supplier);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package me.samsuik.sakura.local.storage;
|
||||
|
||||
import me.samsuik.sakura.local.LocalRegion;
|
||||
import org.bukkit.Location;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public interface LocalStorageHandler {
|
||||
default @NonNull Optional<LocalRegion> locate(@NonNull Location location) {
|
||||
return this.locate(location.blockX(), location.blockZ());
|
||||
}
|
||||
|
||||
@NonNull Optional<LocalRegion> locate(int x, int z);
|
||||
|
||||
@Nullable LocalValueStorage get(@NonNull LocalRegion region);
|
||||
|
||||
boolean has(@NonNull LocalRegion region);
|
||||
|
||||
void put(@NonNull LocalRegion region, @NonNull LocalValueStorage storage);
|
||||
|
||||
void remove(@NonNull LocalRegion region);
|
||||
|
||||
@NonNull List<LocalRegion> regions();
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package me.samsuik.sakura.local.storage;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import me.samsuik.sakura.local.LocalValueKey;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@NullMarked
|
||||
@Deprecated(forRemoval = true)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class LocalValueStorage {
|
||||
private final Map<LocalValueKey<?>, Object> map = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
public <T> void set(LocalValueKey<T> key, T insert) {
|
||||
this.map.put(key, insert);
|
||||
}
|
||||
|
||||
public void remove(LocalValueKey<?> key) {
|
||||
this.map.remove(key);
|
||||
}
|
||||
|
||||
public <T> Optional<T> get(LocalValueKey<T> key) {
|
||||
T value = (T) this.map.get(key);
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
public <T> T getOrDefault(LocalValueKey<T> key, T def) {
|
||||
return (T) this.map.getOrDefault(key, def);
|
||||
}
|
||||
|
||||
public boolean exists(LocalValueKey<?> key) {
|
||||
return this.map.containsKey(key);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> T value(LocalValueKey<T> key) {
|
||||
return (T) this.map.get(key);
|
||||
}
|
||||
|
||||
public <T> T value(LocalValueKey<T> key, boolean returnDefault) {
|
||||
T val = (T) this.map.get(key);
|
||||
if (!returnDefault || val != null)
|
||||
return val;
|
||||
// update value
|
||||
this.set(key, val = key.defaultSupplier().get());
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
/**
|
||||
* All post-1.8 Minecraft versions with changes to cannon mechanics.
|
||||
* <p>
|
||||
* Versions are encoded as shorts see {@link MinecraftVersionEncoding}.
|
||||
*/
|
||||
public final class MechanicVersion {
|
||||
public static final short LATEST = Short.MAX_VALUE;
|
||||
public static final short LEGACY = Short.MIN_VALUE;
|
||||
public static final short v1_8_2 = MinecraftVersionEncoding.v1xy(8, 2);
|
||||
public static final short v1_9 = MinecraftVersionEncoding.v1xy(9, 0);
|
||||
public static final short v1_10 = MinecraftVersionEncoding.v1xy(10, 0);
|
||||
public static final short v1_11 = MinecraftVersionEncoding.v1xy(11, 0);
|
||||
public static final short v1_12 = MinecraftVersionEncoding.v1xy(12, 0);
|
||||
public static final short v1_13 = MinecraftVersionEncoding.v1xy(13, 0);
|
||||
public static final short v1_14 = MinecraftVersionEncoding.v1xy(14, 0);
|
||||
public static final short v1_16 = MinecraftVersionEncoding.v1xy(16, 0);
|
||||
public static final short v1_17 = MinecraftVersionEncoding.v1xy(17, 0);
|
||||
public static final short v1_18_2 = MinecraftVersionEncoding.v1xy(18, 2);
|
||||
public static final short v1_19_3 = MinecraftVersionEncoding.v1xy(19, 3);
|
||||
public static final short v1_20 = MinecraftVersionEncoding.v1xy(20, 0);
|
||||
public static final short v1_21_2 = MinecraftVersionEncoding.v1xy(21, 2);
|
||||
public static final short v1_21_5 = MinecraftVersionEncoding.v1xy(21, 5);
|
||||
public static final short v1_21_6 = MinecraftVersionEncoding.v1xy(21, 6);
|
||||
|
||||
public static String name(final short version) {
|
||||
if (version == LATEST) {
|
||||
return "latest";
|
||||
} else if (version == LEGACY) {
|
||||
return "legacy";
|
||||
}
|
||||
|
||||
final int significant = MinecraftVersionEncoding.significant(version);
|
||||
final int major = MinecraftVersionEncoding.major(version);
|
||||
final int minor = MinecraftVersionEncoding.minor(version);
|
||||
return String.format("%d.%d.%d", significant, major, minor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The targeted Minecraft version and server type for cannon mechanics.
|
||||
*/
|
||||
@NullMarked
|
||||
public record MinecraftMechanicsTarget(short mechanicVersion, byte serverType) {
|
||||
private static final MinecraftMechanicsTarget LATEST = new MinecraftMechanicsTarget(MechanicVersion.LATEST, ServerType.PAPER);
|
||||
private static final MinecraftMechanicsTarget LEGACY = new MinecraftMechanicsTarget(MechanicVersion.v1_8_2, ServerType.PAPER);
|
||||
|
||||
public boolean isServerType(final byte type) {
|
||||
return this.serverType == type;
|
||||
}
|
||||
|
||||
public boolean is(final short version) {
|
||||
return this.mechanicVersion == version;
|
||||
}
|
||||
|
||||
public boolean before(final short version) {
|
||||
return this.mechanicVersion < version;
|
||||
}
|
||||
|
||||
public boolean after(final short version) {
|
||||
return this.mechanicVersion > version;
|
||||
}
|
||||
|
||||
public boolean atLeast(final short version) {
|
||||
return this.mechanicVersion >= version;
|
||||
}
|
||||
|
||||
public boolean atMost(final short version) {
|
||||
return this.mechanicVersion <= version;
|
||||
}
|
||||
|
||||
public boolean between(final short minVersion, final short maxVersion) {
|
||||
return this.mechanicVersion >= minVersion && this.mechanicVersion < maxVersion;
|
||||
}
|
||||
|
||||
public boolean betweenInclusive(final short minVersion, final short maxVersion) {
|
||||
return this.mechanicVersion >= minVersion && this.mechanicVersion <= maxVersion;
|
||||
}
|
||||
|
||||
public boolean isLegacy() {
|
||||
return this.mechanicVersion == MechanicVersion.LEGACY;
|
||||
}
|
||||
|
||||
public static MinecraftMechanicsTarget latest() {
|
||||
return LATEST;
|
||||
}
|
||||
|
||||
public static MinecraftMechanicsTarget legacy() {
|
||||
return LEGACY;
|
||||
}
|
||||
|
||||
public static MinecraftMechanicsTarget vanilla(final short mechanicVersion) {
|
||||
return new MinecraftMechanicsTarget(mechanicVersion, ServerType.VANILLA);
|
||||
}
|
||||
|
||||
public static MinecraftMechanicsTarget spigot(final short mechanicVersion) {
|
||||
return new MinecraftMechanicsTarget(mechanicVersion, ServerType.SPIGOT);
|
||||
}
|
||||
|
||||
public static MinecraftMechanicsTarget paper(final short mechanicVersion) {
|
||||
return new MinecraftMechanicsTarget(mechanicVersion, ServerType.PAPER);
|
||||
}
|
||||
|
||||
public static @Nullable MinecraftMechanicsTarget fromString(final String target) throws NumberFormatException {
|
||||
// 1.21.8+paper 1.8.8+vanilla 12.2
|
||||
final String[] parts = target.split("\\+");
|
||||
final String serverPart = parts.length == 2 ? parts[1] : "";
|
||||
final byte serverType = switch (serverPart.toLowerCase(Locale.ENGLISH)) {
|
||||
case "vanilla" -> ServerType.VANILLA;
|
||||
case "spigot" -> ServerType.SPIGOT;
|
||||
default -> ServerType.PAPER;
|
||||
};
|
||||
|
||||
if (parts.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String[] version = parts[0].split("\\.");
|
||||
if (version.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final short mechanicVersion;
|
||||
if (version.length == 1) {
|
||||
mechanicVersion = switch (version[0]) {
|
||||
case "latest" -> MechanicVersion.LATEST;
|
||||
case "legacy" -> MechanicVersion.LEGACY;
|
||||
default -> 0;
|
||||
};
|
||||
} else {
|
||||
// 21.1 -> 1.21.1
|
||||
final int first = Integer.parseInt(version[0]);
|
||||
final int second = Integer.parseInt(version[1]);
|
||||
if (version.length == 3) {
|
||||
final int third = Integer.parseInt(version[2]);
|
||||
mechanicVersion = MinecraftVersionEncoding.encode(first, second, third);
|
||||
} else {
|
||||
mechanicVersion = MinecraftVersionEncoding.v1xy(first, second);
|
||||
}
|
||||
}
|
||||
|
||||
return new MinecraftMechanicsTarget(mechanicVersion, serverType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
/**
|
||||
* Encode Minecraft versions into a short.
|
||||
*/
|
||||
public final class MinecraftVersionEncoding {
|
||||
private static final int SIGNIFICANT_SHIFT = Short.SIZE - 4;
|
||||
private static final int MAJOR_SHIFT = SIGNIFICANT_SHIFT - 6;
|
||||
private static final int MINOR_SHIFT = MAJOR_SHIFT - 6;
|
||||
|
||||
/**
|
||||
* Encodes a 1.x.y Minecraft version into a short.
|
||||
*
|
||||
* @param major the major version (x)
|
||||
* @param minor the minor version (y)
|
||||
* @return the encoded version as a short
|
||||
*/
|
||||
public static short v1xy(final int major, final int minor) {
|
||||
return encode(1, major, minor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a Minecraft version into a short.
|
||||
*
|
||||
* @param significant the significant version
|
||||
* @param major the major version
|
||||
* @param minor the minor version
|
||||
* @return the encoded version as a short
|
||||
*/
|
||||
public static short encode(final int significant, final int major, final int minor) {
|
||||
short encoded = 0;
|
||||
encoded |= (short) (significant << SIGNIFICANT_SHIFT);
|
||||
encoded |= (short) (major << MAJOR_SHIFT);
|
||||
encoded |= (short) (minor << MINOR_SHIFT);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the significant version from an encoded version.
|
||||
*
|
||||
* @param version the encoded version
|
||||
* @return the significant version
|
||||
*/
|
||||
public static int significant(final short version) {
|
||||
return (version >>> SIGNIFICANT_SHIFT) & 15;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the major version from an encoded version.
|
||||
*
|
||||
* @param version the encoded version
|
||||
* @return the major version
|
||||
*/
|
||||
public static int major(final short version) {
|
||||
return (version >>> MAJOR_SHIFT) & 63;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the minor version from an encoded version.
|
||||
*
|
||||
* @param version the encoded version
|
||||
* @return the minor version
|
||||
*/
|
||||
public static int minor(final short version) {
|
||||
return (version >>> MINOR_SHIFT) & 63;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Types of server software that have different cannon mechanics.
|
||||
*/
|
||||
@NullMarked
|
||||
public final class ServerType {
|
||||
public static final byte VANILLA = 0;
|
||||
public static final byte SPIGOT = 1;
|
||||
public static final byte PAPER = 2;
|
||||
|
||||
public static String name(final byte serverType) {
|
||||
return switch (serverType) {
|
||||
case 0 -> "vanilla";
|
||||
case 1 -> "spigot";
|
||||
case 2 -> "paper";
|
||||
default -> "unknown";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package me.samsuik.sakura.physics;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public enum PhysicsVersion {
|
||||
LEGACY("legacy", 1_0_0), // replicates patched 1.8.8 paper mechanics
|
||||
v1_8_2("1.8.2", 1_8_2), // vanilla mechanics
|
||||
v1_9("1.9", 1_9_0),
|
||||
v1_10("1.10", 1_10_0),
|
||||
v1_11("1.11", 1_11_0),
|
||||
v1_12("1.12", 1_12_0),
|
||||
v1_13("1.13", 1_13_0),
|
||||
v1_14("1.14", 1_14_0),
|
||||
v1_16("1.16", 1_16_0),
|
||||
v1_17("1.17", 1_17_0),
|
||||
v1_18_2("1.18.2", 1_18_2),
|
||||
v1_19_3("1.19.3", 1_19_3),
|
||||
v1_20("1.20", 1_20_0),
|
||||
v1_21_2("1.21.2", 1_21_2),
|
||||
v1_21_5("1.21.5", 1_21_5),
|
||||
v1_21_6("1.21.6", 1_21_6),
|
||||
LATEST("latest", 9_99_9); // latest version
|
||||
|
||||
private final String friendlyName;
|
||||
private final int version;
|
||||
|
||||
PhysicsVersion(String friendlyName, int version) {
|
||||
this.friendlyName = friendlyName;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public boolean isLegacy() {
|
||||
return this == LEGACY;
|
||||
}
|
||||
|
||||
public boolean afterOrEqual(int version) {
|
||||
return this.version >= version;
|
||||
}
|
||||
|
||||
public boolean before(int version) {
|
||||
return this.version < version;
|
||||
}
|
||||
|
||||
public boolean is(int version) {
|
||||
return this.version == version;
|
||||
}
|
||||
|
||||
public boolean isWithin(int min, int max) {
|
||||
return this.version >= min && this.version <= max;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public String getFriendlyName() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
public static PhysicsVersion from(String string) {
|
||||
int parsedVersion = Integer.MIN_VALUE;
|
||||
try {
|
||||
String versionString = string.replace(".", "");
|
||||
parsedVersion = Integer.parseInt(versionString);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignored
|
||||
}
|
||||
for (PhysicsVersion ver : values()) {
|
||||
if (ver.name().equalsIgnoreCase(string) || ver.getFriendlyName().equalsIgnoreCase(string) || ver.is(parsedVersion)) {
|
||||
return ver;
|
||||
}
|
||||
}
|
||||
return LATEST;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.samsuik.sakura.redstone;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Configuration for redstone behavior
|
||||
*
|
||||
* @param implementation the redstone implementation to use
|
||||
* @param cache whether to cache redstone calculations
|
||||
*/
|
||||
@NullMarked
|
||||
public record RedstoneConfiguration(RedstoneImplementation implementation, boolean cache) {
|
||||
public static RedstoneConfiguration withImplementation(final RedstoneImplementation implementation) {
|
||||
return new RedstoneConfiguration(implementation, false);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package me.samsuik.sakura.redstone;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* The redstone implementation to use.
|
||||
*/
|
||||
@NullMarked
|
||||
public enum RedstoneImplementation {
|
||||
VANILLA("vanilla"),
|
||||
@@ -10,11 +13,11 @@ public enum RedstoneImplementation {
|
||||
|
||||
private final String friendlyName;
|
||||
|
||||
RedstoneImplementation(String friendlyName) {
|
||||
RedstoneImplementation(final String friendlyName) {
|
||||
this.friendlyName = friendlyName;
|
||||
}
|
||||
|
||||
public String getFriendlyName() {
|
||||
public final String getFriendlyName() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ Subject: [PATCH] Add redstone implementation api
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
|
||||
index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae1492499a 100644
|
||||
index ddd70576d1551d77cbefb9d63bbbaf94b569b074..a9db955a90e0b44d3c85e39f2f7ae9ea4f68a316 100644
|
||||
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
|
||||
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
|
||||
@@ -275,7 +275,7 @@ public class RedStoneWireBlock extends Block {
|
||||
@@ -13,7 +13,7 @@ index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae
|
||||
*/
|
||||
private void updateSurroundingRedstone(Level worldIn, BlockPos pos, BlockState state, @Nullable Orientation orientation, boolean blockAdded) {
|
||||
- if (worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) {
|
||||
+ if (worldIn.localConfig().config(pos).redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) { // Sakura - redstone implementation api
|
||||
+ if (worldIn.localConfig().at(pos).paperRedstoneImplementation() == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) { // Sakura - redstone implementation api
|
||||
// since 24w33a the source pos is no longer given, but instead an Orientation parameter
|
||||
// when this is not null, it can be used to find the source pos, which the turbo uses
|
||||
// to find the direction of information flow
|
||||
@@ -22,7 +22,7 @@ index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae
|
||||
if (!oldState.is(state.getBlock()) && !level.isClientSide) {
|
||||
// Paper start - optimize redstone - replace call to updatePowerStrength
|
||||
- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
|
||||
+ if (level.localConfig().config(pos).redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { // Sakura - redstone implementation api
|
||||
+ if (level.localConfig().at(pos).paperRedstoneImplementation() == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { // Sakura - redstone implementation api
|
||||
level.getWireHandler().onWireAdded(pos, state); // Alternate Current
|
||||
} else {
|
||||
this.updateSurroundingRedstone(level, pos, state, null, true); // Vanilla/Eigencraft
|
||||
@@ -31,7 +31,7 @@ index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae
|
||||
|
||||
// Paper start - optimize redstone - replace call to updatePowerStrength
|
||||
- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
|
||||
+ if (level.localConfig().config(pos).redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { // Sakura - redstone implementation api
|
||||
+ if (level.localConfig().at(pos).paperRedstoneImplementation() == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { // Sakura - redstone implementation api
|
||||
level.getWireHandler().onWireRemoved(pos, state); // Alternate Current
|
||||
} else {
|
||||
this.updateSurroundingRedstone(level, pos, state, null, false); // Vanilla/Eigencraft
|
||||
@@ -40,7 +40,7 @@ index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae
|
||||
// Paper start - optimize redstone (Alternate Current)
|
||||
// Alternate Current handles breaking of redstone wires in the WireHandler.
|
||||
- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
|
||||
+ if (level.localConfig().config(pos).redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { // Sakura - redstone implementation api
|
||||
+ if (level.localConfig().at(pos).paperRedstoneImplementation() == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { // Sakura - redstone implementation api
|
||||
level.getWireHandler().onWireUpdated(pos, state, orientation);
|
||||
} else
|
||||
// Paper end - optimize redstone (Alternate Current)
|
||||
|
||||
@@ -5,29 +5,28 @@ Subject: [PATCH] Explosion Durable Blocks
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java
|
||||
index 6db566adf2d0df1d26221eda04aa01738df6d3d2..a12068c3adbb147eeb41a093fcd2938cc2c34a15 100644
|
||||
index 6db566adf2d0df1d26221eda04aa01738df6d3d2..23c135a6355e920535734e946e5bd4d06f7f33b7 100644
|
||||
--- a/net/minecraft/world/item/BlockItem.java
|
||||
+++ b/net/minecraft/world/item/BlockItem.java
|
||||
@@ -38,8 +38,31 @@ public class BlockItem extends Item {
|
||||
@@ -38,8 +38,30 @@ public class BlockItem extends Item {
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
+ // Sakura start - explosion durable blocks
|
||||
+ private void sendBlockDurabilityToPlayer(UseOnContext context) {
|
||||
+ Player player = context.getPlayer();
|
||||
+ BlockState state = context.getLevel().getBlockState(context.getClickedPos());
|
||||
+ Block block = state.getBlock();
|
||||
+ me.samsuik.sakura.explosion.durable.DurableMaterial material = context.getLevel().localConfig().config(context.getClickedPos()).durableMaterials.get(block);
|
||||
+ private void sendBlockDurabilityToPlayer(final UseOnContext context) {
|
||||
+ final Player player = context.getPlayer();
|
||||
+ final Level level = context.getLevel();
|
||||
+ final BlockPos clickedPos = context.getClickedPos();
|
||||
+
|
||||
+ // If the clicked block is a durable material then send the durability to the player
|
||||
+ final BlockState state = level.getBlockState(clickedPos);
|
||||
+ final Block clickedBlock = state.getBlock();
|
||||
+ final me.samsuik.sakura.explosion.durable.DurableMaterial material = level.localConfig().at(clickedPos).durableMaterials.get(clickedBlock);
|
||||
+
|
||||
+ if (material != null) {
|
||||
+ int remaining = context.getLevel().durabilityManager.durability(context.getClickedPos(), material);
|
||||
+ int durability = material.durability();
|
||||
+
|
||||
+ player.getBukkitEntity().sendRichMessage(
|
||||
+ me.samsuik.sakura.configuration.GlobalConfiguration.get().messages.durableBlockInteraction,
|
||||
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.unparsed("remaining", String.valueOf(remaining)),
|
||||
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.unparsed("durability", String.valueOf(durability))
|
||||
+ );
|
||||
+ final int remaining = context.getLevel().durabilityManager.durability(clickedPos, material);
|
||||
+ final int durability = material.durability();
|
||||
+ player.getBukkitEntity().sendMessage(me.samsuik.sakura.configuration.GlobalConfiguration.get().messages.durableBlockInteractionComponent(remaining, durability));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -41,7 +40,7 @@ index 6db566adf2d0df1d26221eda04aa01738df6d3d2..a12068c3adbb147eeb41a093fcd2938c
|
||||
return !interactionResult.consumesAction() && context.getItemInHand().has(DataComponents.CONSUMABLE)
|
||||
? super.use(context.getLevel(), context.getPlayer(), context.getHand())
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 44849a074694a3f4438cf7f0672c78219859a20f..72537c6455ff0e38c98a83fd81c5afe17a0b387e 100644
|
||||
index 7a922d26952be2f19161053542fa3a4b1a3bdf80..68cc49c65c3098f0aeb2d6dfbe75262e1fff1681 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -830,6 +830,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
||||
@@ -53,7 +52,7 @@ index 44849a074694a3f4438cf7f0672c78219859a20f..72537c6455ff0e38c98a83fd81c5afe1
|
||||
protected Level(
|
||||
WritableLevelData levelData,
|
||||
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
|
||||
index d45582bdfc3fa837c5aa95f499183b60b877b7c2..341b162252efc4e65cba8a32b4c7936defede097 100644
|
||||
index dfe4058155a83ec213a37553996d2dbb9744881b..7f57531254031f7134fc6d72a5077aab5b48c7f0 100644
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -131,7 +131,7 @@ public class ServerExplosion implements Explosion {
|
||||
@@ -70,12 +69,12 @@ index d45582bdfc3fa837c5aa95f499183b60b877b7c2..341b162252efc4e65cba8a32b4c7936d
|
||||
}
|
||||
// Sakura end - specialised explosions
|
||||
+ // Sakura start - explosion durable blocks
|
||||
+ private Optional<Float> calculateBlockResistance(BlockState blockState, FluidState fluidState, BlockPos pos) {
|
||||
+ private Optional<Float> calculateBlockResistance(final BlockState blockState, final FluidState fluidState, final BlockPos pos) {
|
||||
+ if (!blockState.isAir()) {
|
||||
+ final Block block = blockState.getBlock();
|
||||
+ final me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(pos).durableMaterials.get(block);
|
||||
+ final me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().at(pos).durableMaterials.get(block);
|
||||
+
|
||||
+ if (material != null && material.resistance() >= 0.0f && pos.getY() > this.level.getMinY()) {
|
||||
+ if (material != null && material.replaceBlastResistance() && pos.getY() > this.level.getMinY()) {
|
||||
+ return Optional.of(material.resistance());
|
||||
+ }
|
||||
+ }
|
||||
@@ -91,8 +90,8 @@ index d45582bdfc3fa837c5aa95f499183b60b877b7c2..341b162252efc4e65cba8a32b4c7936d
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Sakura start - explosion durable blocks
|
||||
+ final me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(blockPos).durableMaterials.get(block);
|
||||
+ if (material != null && material.durability() >= 0) { // if durability is < 0 then only altar the blast resistance
|
||||
+ final me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().at(blockPos).durableMaterials.get(block);
|
||||
+ if (material != null && material.applyDurability()) { // if durability is < 0 then only altar the blast resistance
|
||||
+ if (material.onlyDamagedByTnt() && !(this.source instanceof PrimedTnt) || !this.level.durabilityManager.damage(blockPos, material)) {
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
@@ -5,11 +5,11 @@ Subject: [PATCH] Destroy Waterlogged Blocks
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
|
||||
index 341b162252efc4e65cba8a32b4c7936defede097..9e5fe4314513b6364c51e60c1718ed942fd2dba9 100644
|
||||
index 7f57531254031f7134fc6d72a5077aab5b48c7f0..0664de8feb17805647b7de186bd4a536b17d5aa7 100644
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -399,6 +399,11 @@ public class ServerExplosion implements Explosion {
|
||||
if (material != null && material.resistance() >= 0.0f && pos.getY() > this.level.getMinY()) {
|
||||
if (material != null && material.replaceBlastResistance() && pos.getY() > this.level.getMinY()) {
|
||||
return Optional.of(material.resistance());
|
||||
}
|
||||
+ // Sakura start - destroy water logged blocks
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,10 @@ Subject: [PATCH] Collide with non-solid blocks
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
index 3eedfdf43748a61caad88ec847d9aed8bbec4b5b..2401cca1388298db8b5007c8332f738f793880e5 100644
|
||||
index bfb220f0808767fec561d9903955514729a7448f..7dddc1fda846c362c0f3d0cae89e15aba37abf5a 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
@@ -1908,6 +1908,7 @@ public final class CollisionUtil {
|
||||
@@ -1902,6 +1902,7 @@ public final class CollisionUtil {
|
||||
public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2;
|
||||
public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3;
|
||||
public static final int COLLISION_FLAG_ADD_TICKET = 1 << 4; // Sakura - load chunks on movement
|
||||
@@ -16,7 +16,7 @@ index 3eedfdf43748a61caad88ec847d9aed8bbec4b5b..2401cca1388298db8b5007c8332f738f
|
||||
|
||||
public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb,
|
||||
final List<VoxelShape> intoVoxel, final List<AABB> intoAABB,
|
||||
@@ -1960,6 +1961,7 @@ public final class CollisionUtil {
|
||||
@@ -1954,6 +1955,7 @@ public final class CollisionUtil {
|
||||
|
||||
final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0;
|
||||
final boolean addTicket = (collisionFlags & COLLISION_FLAG_ADD_TICKET) != 0; // Sakura - load chunks on movement
|
||||
@@ -24,7 +24,7 @@ index 3eedfdf43748a61caad88ec847d9aed8bbec4b5b..2401cca1388298db8b5007c8332f738f
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
@@ -2003,7 +2005,7 @@ public final class CollisionUtil {
|
||||
@@ -1997,7 +1999,7 @@ public final class CollisionUtil {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ index 3eedfdf43748a61caad88ec847d9aed8bbec4b5b..2401cca1388298db8b5007c8332f738f
|
||||
final int sectionAdjust = !hasSpecial ? 1 : 0;
|
||||
|
||||
final PalettedContainer<BlockState> blocks = section.states;
|
||||
@@ -2042,6 +2044,11 @@ public final class CollisionUtil {
|
||||
@@ -2036,6 +2038,11 @@ public final class CollisionUtil {
|
||||
mutablePos.set(blockX, blockY, blockZ);
|
||||
if (useEntityCollisionShape) {
|
||||
blockCollision = collisionShape.getCollisionShape(blockData, world, mutablePos);
|
||||
@@ -46,10 +46,10 @@ index 3eedfdf43748a61caad88ec847d9aed8bbec4b5b..2401cca1388298db8b5007c8332f738f
|
||||
blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape);
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 751f8e3045dbb090f16f099097bf31b638df39d7..f8efa8d7ea4e79a4baac820f0042c2da7259a849 100644
|
||||
index a8ace187724ce0e26fd9b1d289ec1db4756b85b6..e63a24188e07f50fc4ef67ca87866063fbc51516 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -548,6 +548,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -547,6 +547,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_ADD_TICKET;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,52 +5,73 @@ Subject: [PATCH] Legacy lava block formation
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/LiquidBlock.java b/net/minecraft/world/level/block/LiquidBlock.java
|
||||
index 1e8574d7900ffde16c2e1ee9f92a77c47c85af61..e9895a986dffd2ca170916b3e11f88bf36adae50 100644
|
||||
index e6091c252f252373481f46a3ef119ddc32baddf0..cf624b29ed2b7ca2d6af24b3cac6bb316199d287 100644
|
||||
--- a/net/minecraft/world/level/block/LiquidBlock.java
|
||||
+++ b/net/minecraft/world/level/block/LiquidBlock.java
|
||||
@@ -199,7 +199,14 @@ public class LiquidBlock extends Block implements BucketPickup {
|
||||
@@ -198,7 +198,14 @@ public class LiquidBlock extends Block implements BucketPickup {
|
||||
// Sakura start - configure cannon physics
|
||||
final FluidState fluidState = state.getFluidState();
|
||||
final Block block = fluidState.isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE;
|
||||
if (block == Blocks.COBBLESTONE) {
|
||||
- final me.samsuik.sakura.physics.PhysicsVersion physics = level.localConfig().config(pos).physicsVersion;
|
||||
- final me.samsuik.sakura.mechanics.MinecraftMechanicsTarget mechanicsTarget = level.localConfig().at(pos).mechanicsTarget;
|
||||
+ // Sakura start - legacy lava block formation
|
||||
+ final me.samsuik.sakura.physics.PhysicsVersion physics;
|
||||
+ final me.samsuik.sakura.mechanics.MinecraftMechanicsTarget mechanicsTarget;
|
||||
+ if (level.sakuraConfig().environment.blockGeneration.legacyBlockFormation) {
|
||||
+ physics = me.samsuik.sakura.physics.PhysicsVersion.v1_12;
|
||||
+ mechanicsTarget = me.samsuik.sakura.mechanics.MinecraftMechanicsTarget.legacy();
|
||||
+ } else {
|
||||
+ physics = level.localConfig().config(pos).physicsVersion;
|
||||
+ mechanicsTarget = level.localConfig().at(pos).mechanicsTarget;
|
||||
+ }
|
||||
+ // Sakura end - legacy lava block formation
|
||||
|
||||
// SANITY: In legacy a patch by paper removes the fluid level condition from vanilla.
|
||||
if (physics.before(1_16_0) && !physics.isLegacy() &&
|
||||
diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java
|
||||
index 34a40cf00c4337acd716358f7767aa81a936f1dc..ac052ccb2bfbd8a824b8f8d2ce8d55d8214b5fa9 100644
|
||||
--- a/net/minecraft/world/level/material/LavaFluid.java
|
||||
+++ b/net/minecraft/world/level/material/LavaFluid.java
|
||||
@@ -186,7 +186,8 @@ public abstract class LavaFluid extends FlowingFluid {
|
||||
public boolean canBeReplacedWith(FluidState fluidState, BlockGetter blockReader, BlockPos pos, Fluid fluid, Direction direction) {
|
||||
// Sakura start - configure cannon physics
|
||||
return fluidState.getHeight(blockReader, pos) >= 0.44444445F && fluid.is(FluidTags.WATER)
|
||||
- && blockReader instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0);
|
||||
+ && blockReader instanceof Level level && level.localConfig().config(pos).physicsVersion.afterOrEqual(1_13_0)
|
||||
+ && !level.sakuraConfig().environment.blockGeneration.legacyBlockFormation; // Sakura - legacy lava block formation
|
||||
// Sakura end - configure cannon physics
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/level/material/WaterFluid.java b/net/minecraft/world/level/material/WaterFluid.java
|
||||
index 62a51972df8edd1cc7f892376ba6e37eba1a301a..c8ebf065b250cc44fddd47c8622fb2110f2bfc0e 100644
|
||||
--- a/net/minecraft/world/level/material/WaterFluid.java
|
||||
+++ b/net/minecraft/world/level/material/WaterFluid.java
|
||||
@@ -128,7 +128,10 @@ public abstract class WaterFluid extends FlowingFluid {
|
||||
if (direction == Direction.DOWN && !fluid.is(FluidTags.WATER) || !(blockReader instanceof Level level)) {
|
||||
if (block == Blocks.COBBLESTONE && !me.samsuik.sakura.mechanics.LiquidBehaviour.canLiquidSolidify(level, pos, fluidState, mechanicsTarget)) {
|
||||
return true;
|
||||
}
|
||||
- return fluid.is(FluidTags.LAVA) && level.localConfig().config(pos).physicsVersion.before(1_13_0);
|
||||
+ // Sakura start - legacy lava block formation
|
||||
+ return fluid.is(FluidTags.LAVA) && (level.localConfig().config(pos).physicsVersion.before(1_13_0)
|
||||
+ || level.sakuraConfig().environment.blockGeneration.legacyBlockFormation);
|
||||
+ // Sakura end - legacy lava block formation
|
||||
// Sakura end - configure cannon physics
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java
|
||||
index 752099419e46070b42f9f2ecff1016d28a6ebf49..b6f198a8ef365c0414d2560b36a9c7d13d86ccbf 100644
|
||||
--- a/net/minecraft/world/level/material/LavaFluid.java
|
||||
+++ b/net/minecraft/world/level/material/LavaFluid.java
|
||||
@@ -184,9 +184,16 @@ public abstract class LavaFluid extends FlowingFluid {
|
||||
|
||||
@Override
|
||||
public boolean canBeReplacedWith(FluidState fluidState, BlockGetter blockReader, BlockPos pos, Fluid fluid, Direction direction) {
|
||||
- // Sakura start - configure cannon physics
|
||||
- if (blockReader instanceof Level level && level.localConfig().at(pos).mechanicsTarget.before(me.samsuik.sakura.mechanics.MechanicVersion.v1_13)) {
|
||||
- return false;
|
||||
+ // Sakura start - configure cannon physics & legacy lava block formation
|
||||
+ if (blockReader instanceof Level level) {
|
||||
+ if (level.localConfig().at(pos).mechanicsTarget.before(me.samsuik.sakura.mechanics.MechanicVersion.v1_13)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (level.sakuraConfig().environment.blockGeneration.legacyBlockFormation) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Sakura end - legacy lava block formation
|
||||
}
|
||||
// Sakura end - configure cannon physics
|
||||
return fluidState.getHeight(blockReader, pos) >= 0.44444445F && fluid.is(FluidTags.WATER);
|
||||
diff --git a/net/minecraft/world/level/material/WaterFluid.java b/net/minecraft/world/level/material/WaterFluid.java
|
||||
index 9fc1c56dec5201c3b700992f88b258225ceb8581..65fab5cb4af7e3357a1f65e89eb87a4c7fcd01a7 100644
|
||||
--- a/net/minecraft/world/level/material/WaterFluid.java
|
||||
+++ b/net/minecraft/world/level/material/WaterFluid.java
|
||||
@@ -124,10 +124,18 @@ public abstract class WaterFluid extends FlowingFluid {
|
||||
|
||||
@Override
|
||||
public boolean canBeReplacedWith(FluidState fluidState, BlockGetter blockReader, BlockPos pos, Fluid fluid, Direction direction) {
|
||||
- // Sakura start - configure cannon physics
|
||||
- final boolean canReplace = direction == Direction.DOWN
|
||||
- || blockReader instanceof Level level
|
||||
- && level.localConfig().at(pos).mechanicsTarget.before(me.samsuik.sakura.mechanics.MechanicVersion.v1_13);
|
||||
+ // Sakura start - configure cannon physics & legacy lava block formation
|
||||
+ boolean canReplace = false;
|
||||
+ if (direction == Direction.DOWN) {
|
||||
+ canReplace = true;
|
||||
+ } else if (blockReader instanceof Level level) {
|
||||
+ if (level.localConfig().at(pos).mechanicsTarget.before(me.samsuik.sakura.mechanics.MechanicVersion.v1_13)) {
|
||||
+ canReplace = true;
|
||||
+ } else if (level.sakuraConfig().environment.blockGeneration.legacyBlockFormation) {
|
||||
+ canReplace = true;
|
||||
+ }
|
||||
+ }
|
||||
+ // Sakura end - legacy lava block formation
|
||||
// Before 1.13 lava could replace water
|
||||
return canReplace && !fluid.is(FluidTags.WATER);
|
||||
// Sakura end - configure cannon physics
|
||||
|
||||
@@ -21,11 +21,11 @@ index bad69adfbc492d851a3542dc7f77884d9f933c8a..9250806b12171ae4f14d8dbc9dd3d947
|
||||
} else {entity.inactiveTick();} // Paper - EAR 2
|
||||
profilerFiller.pop();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index f8efa8d7ea4e79a4baac820f0042c2da7259a849..13b7652cd95b4411927d1e041b246d063fa80fae 100644
|
||||
index e63a24188e07f50fc4ef67ca87866063fbc51516..96f430c55c3cfe7791e25e4506f5fdf1ceb1b18b 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -631,6 +631,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return newPosition;
|
||||
@@ -601,6 +601,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return this.mechanicsTarget;
|
||||
}
|
||||
// Sakura end - configure cannon physics
|
||||
+ // Sakura start - entity travel distance limits
|
||||
@@ -44,7 +44,7 @@ index f8efa8d7ea4e79a4baac820f0042c2da7259a849..13b7652cd95b4411927d1e041b246d06
|
||||
|
||||
public Entity(EntityType<?> entityType, Level level) {
|
||||
this.type = entityType;
|
||||
@@ -660,6 +673,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -630,6 +643,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.setPos(0.0, 0.0, 0.0);
|
||||
this.eyeHeight = this.dimensions.eyeHeight();
|
||||
this.despawnTime = level == null || type == EntityType.PLAYER ? -1 : level.paperConfig().entities.spawning.despawnTime.getOrDefault(type, io.papermc.paper.configuration.type.number.IntOr.Disabled.DISABLED).or(-1); // Paper - entity despawn time limit
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Configurable left shooting and adjusting limits
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 13b7652cd95b4411927d1e041b246d063fa80fae..ee7da44161fee45ce3de26d7e990eab3a3f5ff6e 100644
|
||||
index 96f430c55c3cfe7791e25e4506f5fdf1ceb1b18b..2304e3e33edfce64b79001d2f70d731da3114d77 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -644,6 +644,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -614,6 +614,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return Math.max(x, z) >= this.travelDistanceLimit;
|
||||
}
|
||||
// Sakura end - entity travel distance limits
|
||||
@@ -55,7 +55,7 @@ index 13b7652cd95b4411927d1e041b246d063fa80fae..ee7da44161fee45ce3de26d7e990eab3
|
||||
|
||||
public Entity(EntityType<?> entityType, Level level) {
|
||||
this.type = entityType;
|
||||
@@ -1656,6 +1696,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -1630,6 +1670,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
if (xSmaller && z != 0.0) {
|
||||
@@ -63,7 +63,7 @@ index 13b7652cd95b4411927d1e041b246d063fa80fae..ee7da44161fee45ce3de26d7e990eab3
|
||||
z = this.scanZ(currBoundingBox, z, voxelList, bbList);
|
||||
if (z != 0.0) {
|
||||
currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetZ(currBoundingBox, z);
|
||||
@@ -1663,6 +1704,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -1637,6 +1678,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
|
||||
if (x != 0.0) {
|
||||
@@ -76,10 +76,10 @@ index 13b7652cd95b4411927d1e041b246d063fa80fae..ee7da44161fee45ce3de26d7e990eab3
|
||||
if (x != 0.0) {
|
||||
currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetX(currBoundingBox, x);
|
||||
diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
index fb3f28ee3b018462f2274e997d540029560df8d0..5a6e3769e3d9349927db1986256b3947ef30962e 100644
|
||||
index 33e8073651099d6a3d82fe0886424d5aa3886c5d..38047b725ffab925ac3dd97f1470e25db74e1226 100644
|
||||
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
@@ -288,6 +288,7 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti
|
||||
@@ -270,6 +270,7 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti
|
||||
// Sakura end - configure cannon physics
|
||||
this.time++;
|
||||
this.applyGravity();
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Optimise check inside blocks and traverse blocks
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index ee7da44161fee45ce3de26d7e990eab3a3f5ff6e..b804964e620c49130c89d67e0371f06881b95eca 100644
|
||||
index 2304e3e33edfce64b79001d2f70d731da3114d77..d1688f01a9564b5ef4c9e905015a174550cab6ae 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -1936,6 +1936,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -1910,6 +1910,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
private void checkInsideBlocks(List<Entity.Movement> movements, InsideBlockEffectApplier.StepBasedCollector stepBasedCollector) {
|
||||
if (this.isAffectedByBlocks()) {
|
||||
LongSet set = this.visitedBlocks;
|
||||
@@ -16,7 +16,7 @@ index ee7da44161fee45ce3de26d7e990eab3a3f5ff6e..b804964e620c49130c89d67e0371f068
|
||||
|
||||
for (Entity.Movement movement : movements) {
|
||||
Vec3 vec3 = movement.from;
|
||||
@@ -1945,12 +1946,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -1919,12 +1920,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
double d = vec31.get(axis);
|
||||
if (d != 0.0) {
|
||||
Vec3 vec32 = vec3.relative(axis.getPositive(), d);
|
||||
@@ -31,7 +31,7 @@ index ee7da44161fee45ce3de26d7e990eab3a3f5ff6e..b804964e620c49130c89d67e0371f068
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1958,7 +1959,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -1932,7 +1933,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ index ee7da44161fee45ce3de26d7e990eab3a3f5ff6e..b804964e620c49130c89d67e0371f068
|
||||
+ LongSet set, net.minecraft.world.level.chunk.ChunkAccess[] chunkCache) {
|
||||
+ // Sakura end - optimise check inside blocks
|
||||
// Sakura start - configure cannon physics
|
||||
double margin = this.physics.afterOrEqual(1_21_2) ? 1.0E-5f : this.physics.afterOrEqual(1_19_3) ? 1.0E-7 : 0.001;
|
||||
AABB aabb = this.makeBoundingBox(vec31).deflate(margin);
|
||||
@@ -1972,7 +1976,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
final double margin;
|
||||
if (this.mechanicsTarget.atLeast(me.samsuik.sakura.mechanics.MechanicVersion.v1_21_2)) {
|
||||
@@ -1953,7 +1957,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
if (!this.isAlive()) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -66,19 +66,19 @@ index ee7da44161fee45ce3de26d7e990eab3a3f5ff6e..b804964e620c49130c89d67e0371f068
|
||||
this.debugBlockIntersection(pos, false, false);
|
||||
return true;
|
||||
diff --git a/net/minecraft/world/level/BlockGetter.java b/net/minecraft/world/level/BlockGetter.java
|
||||
index 67c9393133f4509abf1bd352fbbc8e21dbb116e2..9e235b8bec7ed8da7c0cb099c47c3b23fbccb9b4 100644
|
||||
index 353ad0c1db2ee374ac5487539c77b2b067dc7c0a..1cdc68aab80390b5f1fb67c14c0a8aaa64f28250 100644
|
||||
--- a/net/minecraft/world/level/BlockGetter.java
|
||||
+++ b/net/minecraft/world/level/BlockGetter.java
|
||||
@@ -221,7 +221,7 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
@@ -227,7 +227,7 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
Vec3 vec3 = to.subtract(from);
|
||||
if (physics != null && physics.before(1_21_2) || vec3.lengthSqr() < Mth.square(0.99999F)) {
|
||||
if (vec3.lengthSqr() < Mth.square(0.99999F) || mechanicsTarget != null && mechanicsTarget.before(me.samsuik.sakura.mechanics.MechanicVersion.v1_21_2)) {
|
||||
// Sakura end - configure cannon physics
|
||||
- for (BlockPos blockPos : BlockPos.betweenClosed(boundingBox)) {
|
||||
+ for (BlockPos blockPos : me.samsuik.sakura.utils.BlockPosIterator.iterable(boundingBox)) { // Sakura - optimise check inside blocks
|
||||
if (!visitor.visit(blockPos, 0)) {
|
||||
return false;
|
||||
}
|
||||
@@ -229,6 +229,20 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
@@ -235,6 +235,20 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
@@ -99,7 +99,7 @@ index 67c9393133f4509abf1bd352fbbc8e21dbb116e2..9e235b8bec7ed8da7c0cb099c47c3b23
|
||||
LongSet set = new LongOpenHashSet();
|
||||
Vec3 minPosition = boundingBox.getMinPosition();
|
||||
Vec3 vec31 = minPosition.subtract(vec3);
|
||||
@@ -236,7 +250,7 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
@@ -242,7 +256,7 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
if (i < 0) {
|
||||
return false;
|
||||
} else {
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Configure breaking blocks outside the world border
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
|
||||
index 78b44fc3dda798d54656318b948c55dcb3b03ecb..d86f6f6a43584967b6e256be32c3144fbb2a326f 100644
|
||||
index 49dabe47bda4237df9799d3c673a40cab9f2d03e..cf8f4203c06030e36a5a5bfe210ba65582c204cb 100644
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -538,6 +538,11 @@ public class ServerExplosion implements Explosion {
|
||||
@@ -540,6 +540,11 @@ public class ServerExplosion implements Explosion {
|
||||
return ret;
|
||||
}
|
||||
// Sakura end - optimise protected explosions
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Optimise block counting for cannon entities
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
index 2401cca1388298db8b5007c8332f738f793880e5..f43bdff628ce94075137904ff9d714f128126e9d 100644
|
||||
index 7dddc1fda846c362c0f3d0cae89e15aba37abf5a..68b89ee60a5dcb5f38dfbda8dd3bbbf25f92f380 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
@@ -1943,6 +1943,7 @@ public final class CollisionUtil {
|
||||
@@ -1937,6 +1937,7 @@ public final class CollisionUtil {
|
||||
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||
final CollisionContext collisionShape = new LazyEntityCollisionContext(entity);
|
||||
final boolean useEntityCollisionShape = LazyEntityCollisionContext.useEntityCollisionShape(world, entity);
|
||||
@@ -16,7 +16,7 @@ index 2401cca1388298db8b5007c8332f738f793880e5..f43bdff628ce94075137904ff9d714f1
|
||||
|
||||
// special cases:
|
||||
if (minBlockY > maxBlockY) {
|
||||
@@ -2007,15 +2008,19 @@ public final class CollisionUtil {
|
||||
@@ -2001,15 +2002,19 @@ public final class CollisionUtil {
|
||||
|
||||
final boolean hasSpecial = !fullBlocks && ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks(); // Sakura - collide with non-solid blocks
|
||||
final int sectionAdjust = !hasSpecial ? 1 : 0;
|
||||
@@ -42,7 +42,7 @@ index 2401cca1388298db8b5007c8332f738f793880e5..f43bdff628ce94075137904ff9d714f1
|
||||
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
||||
final int blockY = currY | (currChunkY << 4);
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index e894c404d58f95c8f54987739ad24b6a0b96dfc3..e77b59f8415c07ddb65a669423eaad9214abe6ab 100644
|
||||
index 1e8a69bfafebcf292140403227cc378a0b3f81a6..30b01591c9abc4dcb5d1916a782ab45af5db5daa 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -616,6 +616,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
+ return this.sakuraConfig;
|
||||
+ }
|
||||
+ // Sakura end - sakura configuration files
|
||||
+ // Sakura start - local config and property storage
|
||||
+ private final me.samsuik.sakura.configuration.local.LocalConfigManager localConfig = new me.samsuik.sakura.configuration.local.LocalConfigManager(this);
|
||||
+ public final me.samsuik.sakura.configuration.local.LocalConfigManager localConfig() {
|
||||
+ // Sakura start - local config api
|
||||
+ private final me.samsuik.sakura.configuration.local.LocalConfiguration localConfig = new me.samsuik.sakura.configuration.local.LocalConfiguration(this);
|
||||
+ public final me.samsuik.sakura.configuration.local.LocalConfiguration localConfig() {
|
||||
+ return this.localConfig;
|
||||
+ }
|
||||
+ // Sakura end - local config and property storage
|
||||
+ // Sakura end - local config api
|
||||
|
||||
public static @Nullable BlockPos lastPhysicsProblem; // Spigot
|
||||
private int tileTickPosition;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
public float yield;
|
||||
// CraftBukkit end
|
||||
public boolean excludeSourceFromDamage = true; // Paper - Allow explosions to damage source
|
||||
+ private final boolean consistentRadius; // Sakura - consistent explosion radius
|
||||
+ private final boolean consistentExplosionRadius; // Sakura - consistent explosion radius
|
||||
// Paper start - collisions optimisations
|
||||
private static final double[] CACHED_RAYS;
|
||||
static {
|
||||
@@ -12,7 +12,7 @@
|
||||
this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F;
|
||||
this.yield = Double.isFinite(this.yield) ? this.yield : 0; // Paper - Don't allow infinite default yields
|
||||
// Paper end - add yield
|
||||
+ this.consistentRadius = level.localConfig().config(BlockPos.containing(this.center)).consistentRadius; // Sakura - consistent explosion radius
|
||||
+ this.consistentExplosionRadius = level.localConfig().at(this.center).consistentExplosionRadius; // Sakura - consistent explosion radius
|
||||
}
|
||||
|
||||
private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) {
|
||||
@@ -22,7 +22,7 @@
|
||||
ray += 3;
|
||||
-
|
||||
- float power = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F);
|
||||
+ float power = this.radius * (0.7F + (this.consistentRadius ? 0.7F : this.level.random.nextFloat()) * 0.6F); // Sakura - consistent explosion radius
|
||||
+ float power = this.radius * (0.7F + (this.consistentExplosionRadius ? 0.7F : this.level.random.nextFloat()) * 0.6F); // Sakura - consistent explosion radius
|
||||
|
||||
do {
|
||||
final int blockX = Mth.floor(currX);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
+ // Sakura start - lava flow speed api
|
||||
+ @Override
|
||||
+ public final int getTickDelay(Level world, BlockPos pos) {
|
||||
+ final int flowSpeed = world.localConfig().config(pos).lavaFlowSpeed;
|
||||
+ final int flowSpeed = world.localConfig().at(pos).lavaFlowSpeed;
|
||||
+ return flowSpeed >= 0 ? flowSpeed : this.getTickDelay(world);
|
||||
+ }
|
||||
+ // Sakura end - lava flow speed api
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
).isValid();
|
||||
}
|
||||
// Paper end
|
||||
+ // Sakura start - local config and property storage
|
||||
+ // Sakura start - local config api
|
||||
+ @Override
|
||||
+ public final me.samsuik.sakura.local.storage.LocalStorageHandler getStorageHandler() {
|
||||
+ public final me.samsuik.sakura.configuration.local.LocalConfigurationAccessor localConfig() {
|
||||
+ return this.getHandle().localConfig();
|
||||
+ }
|
||||
+ // Sakura end - local config and property storage
|
||||
+ // Sakura end - local config api
|
||||
|
||||
private static final Random rand = new Random();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.samsuik.sakura.command;
|
||||
|
||||
import me.samsuik.sakura.command.subcommands.*;
|
||||
import me.samsuik.sakura.command.subcommands.debug.DebugLocalRegions;
|
||||
import me.samsuik.sakura.command.subcommands.debug.DebugLocalConfiguration;
|
||||
import me.samsuik.sakura.command.subcommands.debug.DebugRedstoneCache;
|
||||
import me.samsuik.sakura.player.visibility.VisibilityTypes;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -30,7 +30,7 @@ public final class SakuraCommands {
|
||||
// "sakura" isn't a subcommand
|
||||
COMMANDS.put("sakura", new SakuraCommand("sakura"));
|
||||
DEBUG_COMMANDS.add(new DebugRedstoneCache("redstone-cache"));
|
||||
DEBUG_COMMANDS.add(new DebugLocalRegions("local-regions"));
|
||||
DEBUG_COMMANDS.add(new DebugLocalConfiguration("local-regions"));
|
||||
}
|
||||
|
||||
public static void registerCommands(MinecraftServer server) {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package me.samsuik.sakura.command.subcommands.debug;
|
||||
|
||||
import me.samsuik.sakura.command.PlayerOnlySubCommand;
|
||||
import me.samsuik.sakura.configuration.local.ConfigurationContainer;
|
||||
import me.samsuik.sakura.configuration.local.LocalConfigurationAccessor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@NullMarked
|
||||
public final class DebugLocalConfiguration extends PlayerOnlySubCommand {
|
||||
private static final int DEFAULT_REGION_SIZE = 16;
|
||||
|
||||
public DebugLocalConfiguration(final String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final Player player, final String[] args) {
|
||||
final Location location = player.getLocation();
|
||||
final LocalConfigurationAccessor localConfigurationAccessor = location.getWorld().localConfig();
|
||||
final BoundingBox boundingBox = localConfigurationAccessor.getAreas(location).stream()
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
if (boundingBox != null) {
|
||||
player.sendRichMessage("<green>You are currently inside a area with a set local-config.");
|
||||
player.sendRichMessage("<green> - %.0f %.0f %.0f".formatted(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ()));
|
||||
player.sendRichMessage("<green> - %.0f %.0f %.0f".formatted(boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxX()));
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("delete".equalsIgnoreCase(args[0]) && boundingBox != null) {
|
||||
localConfigurationAccessor.remove(boundingBox);
|
||||
player.sendRichMessage("<green>Removed area");
|
||||
}
|
||||
|
||||
if ("create".equalsIgnoreCase(args[0]) && args.length > 1) {
|
||||
final int size = parseInt(args, 1).orElse(DEFAULT_REGION_SIZE);
|
||||
final BoundingBox area = BoundingBox.of(location, size, size, size);
|
||||
localConfigurationAccessor.set(area, ConfigurationContainer.sealedContainer());
|
||||
player.sendRichMessage("<green>Created a new area with size " + size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tabComplete(final List<String> completions, final String[] args) throws IllegalArgumentException {
|
||||
completions.addAll(List.of("create", "delete"));
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package me.samsuik.sakura.command.subcommands.debug;
|
||||
|
||||
import me.samsuik.sakura.command.PlayerOnlySubCommand;
|
||||
import me.samsuik.sakura.local.LocalRegion;
|
||||
import me.samsuik.sakura.local.storage.LocalStorageHandler;
|
||||
import me.samsuik.sakura.local.storage.LocalValueStorage;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@NullMarked
|
||||
public final class DebugLocalRegions extends PlayerOnlySubCommand {
|
||||
private static final int DEFAULT_REGION_SIZE = 16;
|
||||
|
||||
public DebugLocalRegions(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player player, String[] args) {
|
||||
if (args.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Location location = player.getLocation();
|
||||
final World world = location.getWorld();
|
||||
final LocalStorageHandler storageHandler = world.getStorageHandler();
|
||||
final int blockX = location.getBlockX();
|
||||
final int blockZ = location.getBlockZ();
|
||||
final Optional<LocalRegion> currentRegion = storageHandler.locate(blockX, blockZ);
|
||||
|
||||
if ("create".equalsIgnoreCase(args[0]) && args.length > 1) {
|
||||
final int size = parseInt(args, 1).orElse(DEFAULT_REGION_SIZE);
|
||||
final LocalRegion region = LocalRegion.at(blockX, blockZ, size);
|
||||
if (currentRegion.isPresent()) {
|
||||
player.sendRichMessage("<red>regions cannot overlap");
|
||||
} else {
|
||||
storageHandler.put(region, new LocalValueStorage());
|
||||
}
|
||||
}
|
||||
|
||||
if ("get".equalsIgnoreCase(args[0])) {
|
||||
player.sendRichMessage("<red>" + (currentRegion.isPresent() ? currentRegion.get() : "not inside of a region"));
|
||||
}
|
||||
|
||||
if (currentRegion.isPresent()) {
|
||||
final LocalRegion region = currentRegion.get();
|
||||
if ("delete".equalsIgnoreCase(args[0])) {
|
||||
storageHandler.remove(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tabComplete(List<String> list, String[] args) throws IllegalArgumentException {
|
||||
list.addAll(List.of("create", "get", "delete"));
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import com.mojang.logging.LogUtils;
|
||||
import io.papermc.paper.configuration.Configuration;
|
||||
import io.papermc.paper.configuration.ConfigurationPart;
|
||||
import io.papermc.paper.configuration.type.number.IntOr;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Material;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
@@ -32,6 +35,14 @@ public final class GlobalConfiguration extends ConfigurationPart {
|
||||
public String durableBlockInteraction = "<dark_gray>(<light_purple>S</light_purple>) <white>This block has <gray><remaining></gray> of <gray><durability>";
|
||||
public String fpsSettingChange = "<dark_gray>(<light_purple>S</light_purple>) <gray><state> <yellow><name>";
|
||||
public boolean tpsShowEntityAndChunkCount = true;
|
||||
|
||||
public Component durableBlockInteractionComponent(final int remaining, final int durability) {
|
||||
return MiniMessage.miniMessage().deserialize(
|
||||
GlobalConfiguration.get().messages.durableBlockInteraction,
|
||||
Placeholder.unparsed("remaining", String.valueOf(remaining)),
|
||||
Placeholder.unparsed("durability", String.valueOf(durability))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Fps fps;
|
||||
|
||||
@@ -14,7 +14,9 @@ import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
|
||||
import me.samsuik.sakura.configuration.serializer.MinecraftMechanicsTargetSerializer;
|
||||
import me.samsuik.sakura.configuration.transformation.ConfigurationTransformations;
|
||||
import me.samsuik.sakura.mechanics.MinecraftMechanicsTarget;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -141,6 +143,7 @@ public final class SakuraConfigurations extends Configurations<GlobalConfigurati
|
||||
.defaultOptions(options -> options
|
||||
.header(contextMap.require(WORLD_NAME).equals(WORLD_DEFAULTS) ? WORLD_DEFAULTS_HEADER : WORLD_HEADER.apply(contextMap))
|
||||
.serializers(serializers -> serializers
|
||||
.register(new TypeToken<MinecraftMechanicsTarget>() {}, new MinecraftMechanicsTargetSerializer())
|
||||
.register(new TypeToken<Reference2IntMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2IntMap<?>>(Reference2IntOpenHashMap::new, Integer.TYPE))
|
||||
.register(new TypeToken<Reference2LongMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2LongMap<?>>(Reference2LongOpenHashMap::new, Long.TYPE))
|
||||
.register(new TypeToken<Table<?, ?, ?>>() {}, new TableSerializer())
|
||||
|
||||
@@ -10,7 +10,7 @@ import io.papermc.paper.configuration.type.number.IntOr;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import me.samsuik.sakura.entity.merge.MergeLevel;
|
||||
import me.samsuik.sakura.explosion.durable.DurableMaterial;
|
||||
import me.samsuik.sakura.physics.PhysicsVersion;
|
||||
import me.samsuik.sakura.mechanics.MinecraftMechanicsTarget;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -28,7 +28,7 @@ import java.util.Set;
|
||||
public final class WorldConfiguration extends ConfigurationPart {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
static final int CURRENT_VERSION = 10; // (when you change the version, change the comment, so it conflicts on rebases): rename filter bad nbt from spawn eggs
|
||||
static final int CURRENT_VERSION = 11; // (when you change the version, change the comment, so it conflicts on rebases): rename filter bad nbt from spawn eggs
|
||||
|
||||
private transient final ResourceLocation worldKey;
|
||||
WorldConfiguration(ResourceLocation worldKey) {
|
||||
@@ -115,7 +115,7 @@ public final class WorldConfiguration extends ConfigurationPart {
|
||||
public TNTSpread tntSpread = TNTSpread.ALL;
|
||||
public boolean tntFlowsInWater = true;
|
||||
public boolean fallingBlockParity = false;
|
||||
public PhysicsVersion physicsVersion = PhysicsVersion.LATEST;
|
||||
public MinecraftMechanicsTarget mechanicsTarget = MinecraftMechanicsTarget.latest();
|
||||
|
||||
public enum TNTSpread {
|
||||
ALL, Y, NONE;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import io.papermc.paper.configuration.WorldConfiguration;
|
||||
import me.samsuik.sakura.explosion.durable.DurableMaterial;
|
||||
import me.samsuik.sakura.mechanics.MinecraftMechanicsTarget;
|
||||
import me.samsuik.sakura.redstone.RedstoneConfiguration;
|
||||
import me.samsuik.sakura.redstone.RedstoneImplementation;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.bukkit.craftbukkit.block.CraftBlockType;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@NullMarked
|
||||
public final class CachedLocalConfiguration {
|
||||
public final long sectionKey;
|
||||
public final MinecraftMechanicsTarget mechanicsTarget;
|
||||
public final Map<Block, DurableMaterial> durableMaterials;
|
||||
public final RedstoneConfiguration redstoneBehaviour;
|
||||
public final boolean consistentExplosionRadius;
|
||||
public final int lavaFlowSpeed;
|
||||
|
||||
public static CachedLocalConfiguration emptyConfiguration() {
|
||||
return new CachedLocalConfiguration();
|
||||
}
|
||||
|
||||
public CachedLocalConfiguration(final Level level, final ConfigurationContainer container, final long sectionKey) {
|
||||
this.sectionKey = sectionKey;
|
||||
this.mechanicsTarget = container.getOptional(ConfigurableKey.MECHANICS_TARGET)
|
||||
.orElse(level.sakuraConfig().cannons.mechanics.mechanicsTarget);
|
||||
this.durableMaterials = container.getOptional(ConfigurableKey.DURABLE_MATERIALS)
|
||||
.map(sealedContainer -> sealedContainer.open().contents().entrySet().stream()
|
||||
.map(entry -> Map.entry(CraftBlockType.bukkitToMinecraftNew(entry.getKey()), entry.getValue()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
|
||||
.orElseGet(() -> level.sakuraConfig().cannons.explosion.durableMaterials);
|
||||
this.redstoneBehaviour = container.getOptional(ConfigurableKey.REDSTONE_BEHAVIOUR)
|
||||
.orElse(createDefaultRedstoneConfiguration(level));
|
||||
this.consistentExplosionRadius = container.getOptional(ConfigurableKey.CONSISTENT_EXPLOSION_RADIUS)
|
||||
.orElse(level.sakuraConfig().cannons.explosion.consistentRadius);
|
||||
this.lavaFlowSpeed = container.getOptional(ConfigurableKey.LAVA_FLOW_SPEED)
|
||||
.orElse(30);
|
||||
}
|
||||
|
||||
private CachedLocalConfiguration() {
|
||||
this.sectionKey = Long.MIN_VALUE;
|
||||
this.mechanicsTarget = MinecraftMechanicsTarget.latest();
|
||||
this.durableMaterials = Map.of();
|
||||
this.redstoneBehaviour = new RedstoneConfiguration(RedstoneImplementation.VANILLA, false);
|
||||
this.consistentExplosionRadius = false;
|
||||
this.lavaFlowSpeed = 30;
|
||||
}
|
||||
|
||||
public WorldConfiguration.Misc.RedstoneImplementation paperRedstoneImplementation() {
|
||||
return WorldConfiguration.Misc.RedstoneImplementation.values()[this.redstoneBehaviour.implementation().ordinal()];
|
||||
}
|
||||
|
||||
private static RedstoneConfiguration createDefaultRedstoneConfiguration(final Level level) {
|
||||
final WorldConfiguration.Misc.RedstoneImplementation paperRedstoneImplementation = level.paperConfig().misc.redstoneImplementation;
|
||||
final RedstoneImplementation sakuraRedstoneImplementation = RedstoneImplementation.values()[paperRedstoneImplementation.ordinal()];
|
||||
return new RedstoneConfiguration(sakuraRedstoneImplementation, level.sakuraConfig().technical.redstone.redstoneCache);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
@NullMarked
|
||||
public record ConfigurationArea(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||
public ConfigurationArea(final BoundingBox boundingBox) {
|
||||
this(
|
||||
Mth.floor(boundingBox.getMinX()),
|
||||
Mth.floor(boundingBox.getMinY()),
|
||||
Mth.floor(boundingBox.getMinZ()),
|
||||
Mth.floor(boundingBox.getMaxX()),
|
||||
Mth.floor(boundingBox.getMaxY()),
|
||||
Mth.floor(boundingBox.getMaxZ())
|
||||
);
|
||||
}
|
||||
|
||||
public BoundingBox asBoundingBox() {
|
||||
return new BoundingBox(this.minX, this.minY, this.minZ, this.maxX, this.maxY, this.maxZ);
|
||||
}
|
||||
|
||||
public boolean contains(final int x, final int y, final int z) {
|
||||
return x >= this.minX && x < this.maxX
|
||||
&& y >= this.minY && y < this.maxY
|
||||
&& z >= this.minZ && z < this.maxZ;
|
||||
}
|
||||
|
||||
public long volume() {
|
||||
return this.countSections(0);
|
||||
}
|
||||
|
||||
public long countSections(final int sectionExponent) {
|
||||
final int sectionsX = (maxX - minX >> sectionExponent) + 1;
|
||||
final int sectionsY = (maxY - minY >> sectionExponent) + 1;
|
||||
final int sectionsZ = (maxZ - minZ >> sectionExponent) + 1;
|
||||
return (long) sectionsX * (long) sectionsY * (long) sectionsZ;
|
||||
}
|
||||
|
||||
public static long sectionKey(final int x, final int y, final int z, final int sectionExponent) {
|
||||
final int sectionX = x >> sectionExponent;
|
||||
final int sectionY = y >> sectionExponent;
|
||||
final int sectionZ = z >> sectionExponent;
|
||||
return (long) sectionX << 40 | (long) sectionY << 20 | (long) sectionZ;
|
||||
}
|
||||
|
||||
public void forEach(final int sectionExponent, final LongConsumer sectionConsumer) {
|
||||
final int minSectionX = this.minX >> sectionExponent;
|
||||
final int minSectionY = this.minY >> sectionExponent;
|
||||
final int minSectionZ = this.minZ >> sectionExponent;
|
||||
final int maxSectionX = this.maxX >> sectionExponent;
|
||||
final int maxSectionY = this.maxY >> sectionExponent;
|
||||
final int maxSectionZ = this.maxZ >> sectionExponent;
|
||||
|
||||
for (int x = minSectionX; x <= maxSectionX; x++) {
|
||||
for (int y = minSectionY; y <= maxSectionY; y++) {
|
||||
for (int z = minSectionZ; z <= maxSectionZ; z++) {
|
||||
sectionConsumer.accept(sectionKey(x, y, z, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.function.BiLongObjectConsumer;
|
||||
import com.google.common.collect.Iterables;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import me.samsuik.sakura.local.LocalRegion;
|
||||
import me.samsuik.sakura.local.storage.LocalStorageHandler;
|
||||
import me.samsuik.sakura.local.storage.LocalValueStorage;
|
||||
import me.samsuik.sakura.utils.TickExpiry;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class LocalConfigManager implements LocalStorageHandler {
|
||||
private static final long MASSIVE_REGION_SIZE = 0x10000000000L;
|
||||
private static final int SMALL_REGION_SIZE = 12;
|
||||
private static final int CONFIG_CACHE_EXPIRATION = 600;
|
||||
|
||||
private final Map<LocalRegion, LocalValueStorage> storageMap = new Object2ObjectOpenHashMap<>();
|
||||
private final List<LocalRegion> largeRegions = new ObjectArrayList<>();
|
||||
private final Long2ObjectMap<List<LocalRegion>> smallRegions = new Long2ObjectOpenHashMap<>();
|
||||
private int regionExponent = 4;
|
||||
private final Long2ObjectMap<Pair<LocalValueConfig, TickExpiry>> chunkConfigCache = new Long2ObjectOpenHashMap<>();
|
||||
private final Level level;
|
||||
private long expirationTick = 0L;
|
||||
|
||||
public LocalConfigManager(Level level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
private int regionChunkCoord(int n) {
|
||||
return n >> this.regionExponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized @NonNull Optional<LocalRegion> locate(int x, int z) {
|
||||
int regionX = this.regionChunkCoord(x);
|
||||
int regionZ = this.regionChunkCoord(z);
|
||||
long regionPos = ChunkPos.asLong(regionX, regionZ);
|
||||
List<LocalRegion> regions = this.smallRegions.getOrDefault(regionPos, List.of());
|
||||
for (LocalRegion region : Iterables.concat(regions, this.largeRegions)) {
|
||||
if (region.contains(x, z)) {
|
||||
return Optional.of(region);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized @Nullable LocalValueStorage get(@NonNull LocalRegion region) {
|
||||
return this.storageMap.get(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean has(@NonNull LocalRegion region) {
|
||||
return this.storageMap.containsKey(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void put(@NonNull LocalRegion region, @NonNull LocalValueStorage storage) {
|
||||
boolean smallRegion = this.isSmallRegion(region);
|
||||
this.ensureRegionIsNotOverlapping(region, smallRegion);
|
||||
|
||||
if (!smallRegion) {
|
||||
this.largeRegions.add(region);
|
||||
|
||||
// The region exponent may be too small
|
||||
if ((this.largeRegions.size() & 15) == 0) {
|
||||
this.resizeRegions();
|
||||
}
|
||||
} else {
|
||||
this.forEachRegionChunks(region, this::addSmallRegion);
|
||||
}
|
||||
|
||||
this.chunkConfigCache.clear();
|
||||
this.storageMap.put(region, storage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void remove(@NonNull LocalRegion region) {
|
||||
this.forEachRegionChunks(region, (pos, r) -> {
|
||||
List<LocalRegion> regions = this.smallRegions.get(pos);
|
||||
if (regions != null) {
|
||||
regions.remove(region);
|
||||
if (regions.isEmpty()) {
|
||||
this.smallRegions.remove(pos);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.chunkConfigCache.clear();
|
||||
this.storageMap.remove(region);
|
||||
this.largeRegions.remove(region);
|
||||
}
|
||||
|
||||
private void addSmallRegion(long pos, LocalRegion region) {
|
||||
this.smallRegions.computeIfAbsent(pos, k -> new ArrayList<>())
|
||||
.add(region);
|
||||
}
|
||||
|
||||
private void forEachRegionChunks(LocalRegion region, BiLongObjectConsumer<LocalRegion> chunkConsumer) {
|
||||
int exponent = this.regionExponent;
|
||||
int minX = region.minX() >> exponent;
|
||||
int minZ = region.minZ() >> exponent;
|
||||
int maxX = region.maxX() >> exponent;
|
||||
int maxZ = region.maxZ() >> exponent;
|
||||
|
||||
for (int x = minX; x <= maxX; ++x) {
|
||||
for (int z = minZ; z <= maxZ; ++z) {
|
||||
chunkConsumer.accept(ChunkPos.asLong(x, z), region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resizeRegions() {
|
||||
int newExponent = this.calculateRegionExponent();
|
||||
if (newExponent == this.regionExponent) {
|
||||
return; // nothing has changed
|
||||
}
|
||||
|
||||
this.regionExponent = newExponent;
|
||||
this.largeRegions.clear();
|
||||
this.smallRegions.clear();
|
||||
|
||||
for (LocalRegion region : this.storageMap.keySet()) {
|
||||
if (!this.isSmallRegion(region)) {
|
||||
this.largeRegions.add(region);
|
||||
} else {
|
||||
this.forEachRegionChunks(region, this::addSmallRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateRegionExponent() {
|
||||
long totalRegionChunks = 0;
|
||||
for (LocalRegion region : this.storageMap.keySet()) {
|
||||
long chunks = regionChunks(region, 0);
|
||||
if (chunks >= MASSIVE_REGION_SIZE) {
|
||||
continue;
|
||||
}
|
||||
totalRegionChunks += chunks;
|
||||
}
|
||||
totalRegionChunks /= this.storageMap.size();
|
||||
|
||||
int exponent = 4;
|
||||
while (true) {
|
||||
if ((totalRegionChunks >> exponent++) <= SMALL_REGION_SIZE / 2) {
|
||||
return exponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSmallRegion(LocalRegion region) {
|
||||
return regionChunks(region, this.regionExponent) <= SMALL_REGION_SIZE;
|
||||
}
|
||||
|
||||
private static long regionChunks(LocalRegion region, int exponent) {
|
||||
int sizeX = region.maxX() - region.minX() >> exponent;
|
||||
int sizeZ = region.maxZ() - region.minZ() >> exponent;
|
||||
return (long) (sizeX + 1) * (long) (sizeZ + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized @NonNull List<LocalRegion> regions() {
|
||||
return new ArrayList<>(this.storageMap.keySet());
|
||||
}
|
||||
|
||||
public synchronized LocalValueConfig config(BlockPos position) {
|
||||
long gameTime = this.level.getGameTime();
|
||||
long ticks = gameTime - this.expirationTick;
|
||||
if (ticks >= CONFIG_CACHE_EXPIRATION / 3) {
|
||||
this.chunkConfigCache.values().removeIf(pair -> pair.value().isExpired(gameTime));
|
||||
this.expirationTick = gameTime;
|
||||
}
|
||||
|
||||
long chunkKey = ChunkPos.asLong(position.getX() >> 4, position.getZ() >> 4);
|
||||
Pair<LocalValueConfig, TickExpiry> pair = this.chunkConfigCache.computeIfAbsent(chunkKey,
|
||||
k -> this.createLocalChunkConfigWithExpiry(position, gameTime));
|
||||
|
||||
pair.value().refresh(gameTime);
|
||||
return pair.key();
|
||||
}
|
||||
|
||||
private Pair<LocalValueConfig, TickExpiry> createLocalChunkConfigWithExpiry(BlockPos position, long gameTime) {
|
||||
// uses defaults from the sakura and paper config
|
||||
LocalValueConfig config = new LocalValueConfig(this.level);
|
||||
this.locate(position.getX(), position.getZ()).ifPresent(region -> {
|
||||
config.loadFromStorage(this.storageMap.get(region));
|
||||
});
|
||||
|
||||
TickExpiry expiry = new TickExpiry(gameTime, CONFIG_CACHE_EXPIRATION);
|
||||
return Pair.of(config, expiry);
|
||||
}
|
||||
|
||||
private void ensureRegionIsNotOverlapping(LocalRegion region, boolean smallRegion) {
|
||||
Set<LocalRegion> nearbyRegions = new ReferenceOpenHashSet<>();
|
||||
if (!smallRegion) {
|
||||
nearbyRegions.addAll(this.storageMap.keySet());
|
||||
} else {
|
||||
this.forEachRegionChunks(region, (pos, r) -> {
|
||||
nearbyRegions.addAll(this.smallRegions.getOrDefault(pos, List.of()));
|
||||
});
|
||||
}
|
||||
|
||||
// Throw if any of the nearby regions are overlapping
|
||||
for (LocalRegion present : Iterables.concat(nearbyRegions, this.largeRegions)) {
|
||||
if (present != region && present.intersects(region)) {
|
||||
throw new OverlappingRegionException(present, region);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import io.papermc.paper.util.MCUtil;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import me.samsuik.sakura.configuration.local.ConfigurationContainer.SealedConfigurationContainer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@NullMarked
|
||||
public final class LocalConfiguration implements LocalConfigurationAccessor {
|
||||
private static final CachedLocalConfiguration EMPTY_CONFIGURATION = CachedLocalConfiguration.emptyConfiguration();
|
||||
|
||||
private final LocalConfigurationContainers containers = new LocalConfigurationContainers();
|
||||
private final Long2ObjectOpenHashMap<CachedLocalConfiguration> cachedConfiguration = new Long2ObjectOpenHashMap<>();
|
||||
private final CachedLocalConfiguration[] recentlyAccessed = new CachedLocalConfiguration[8];
|
||||
private long lastGameTime;
|
||||
private final Level level;
|
||||
|
||||
public LocalConfiguration(final Level level) {
|
||||
this.level = level;
|
||||
Arrays.fill(this.recentlyAccessed, EMPTY_CONFIGURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(final BoundingBox bb, final SealedConfigurationContainer container) {
|
||||
final ConfigurationArea area = new ConfigurationArea(bb);
|
||||
this.containers.add(area, container);
|
||||
|
||||
// As containers are immutable, we just need to clear the cache to provide an immediate update.
|
||||
this.cachedConfiguration.clear();
|
||||
Arrays.fill(this.recentlyAccessed, EMPTY_CONFIGURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SealedConfigurationContainer remove(final BoundingBox bb) {
|
||||
final ConfigurationArea area = new ConfigurationArea(bb);
|
||||
final SealedConfigurationContainer container = this.containers.remove(area);
|
||||
|
||||
this.cachedConfiguration.clear();
|
||||
Arrays.fill(this.recentlyAccessed, EMPTY_CONFIGURATION);
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SealedConfigurationContainer get(final BoundingBox bb) {
|
||||
return this.containers.get(new ConfigurationArea(bb));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigurationContainer getContainer(final int x, final int y, final int z) {
|
||||
return this.containers.getContainer(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BoundingBox> getAreas(final int x, final int y, final int z) {
|
||||
return this.containers.getAreas(x, y, z).stream()
|
||||
.map(ConfigurationArea::asBoundingBox)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public CachedLocalConfiguration at(final Vec3 vec3) {
|
||||
return this.at(BlockPos.containing(vec3));
|
||||
}
|
||||
|
||||
public CachedLocalConfiguration at(final BlockPos pos) {
|
||||
// This is sometimes called off the main thread when loading/generating chunks
|
||||
if (!MCUtil.isMainThread()) {
|
||||
return EMPTY_CONFIGURATION;
|
||||
}
|
||||
|
||||
final int x = pos.getX();
|
||||
final int y = pos.getY();
|
||||
final int z = pos.getZ();
|
||||
|
||||
final long sectionKey = ConfigurationArea.sectionKey(x, y, z, 2);
|
||||
final int recentCacheIndex = ((x & 1) << 2) | ((y & 1) << 1) | (z & 1);
|
||||
final CachedLocalConfiguration recentCache = recentlyAccessed[recentCacheIndex];
|
||||
|
||||
// Fast path if the local configuration was recently accessed
|
||||
if (recentCache.sectionKey == sectionKey) {
|
||||
return recentCache;
|
||||
}
|
||||
|
||||
// Clear the cache every minute
|
||||
final long gameTime = this.level.getGameTime();
|
||||
if (gameTime - this.lastGameTime >= 60 * 20) {
|
||||
this.cachedConfiguration.clear();
|
||||
this.lastGameTime = gameTime;
|
||||
}
|
||||
|
||||
// Get the local configuration from the cache, if that isn't possible then create one
|
||||
CachedLocalConfiguration cache = this.cachedConfiguration.get(sectionKey);
|
||||
//noinspection ConstantValue
|
||||
if (cache == null) {
|
||||
final ConfigurationContainer container = this.getContainer(x, y, z);
|
||||
cache = new CachedLocalConfiguration(level, container, sectionKey);
|
||||
this.cachedConfiguration.put(sectionKey, cache);
|
||||
}
|
||||
|
||||
this.recentlyAccessed[recentCacheIndex] = cache;
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static me.samsuik.sakura.configuration.local.ConfigurationContainer.*;
|
||||
|
||||
@NullMarked
|
||||
public final class LocalConfigurationContainers {
|
||||
private static final long MASSIVE_REGION_SIZE = 0x180000000L;
|
||||
private static final int LARGE_AREA_THRESHOLD = 6 * 6 * 6;
|
||||
private static final Comparator<ConfigurationArea> AREA_BY_VOLUME = Comparator.comparingLong(ConfigurationArea::volume);
|
||||
|
||||
private final Map<ConfigurationArea, SealedConfigurationContainer> containers = new HashMap<>();
|
||||
private final List<ConfigurationArea> largeAreas = new ArrayList<>();
|
||||
private final Long2ObjectOpenHashMap<List<ConfigurationArea>> smallAreas = new Long2ObjectOpenHashMap<>();
|
||||
private int sectionExponent = 4;
|
||||
private int changes = 0;
|
||||
|
||||
private boolean isLargeArea(final ConfigurationArea area) {
|
||||
return area.countSections(this.sectionExponent) > LARGE_AREA_THRESHOLD;
|
||||
}
|
||||
|
||||
public void add(final ConfigurationArea area, final SealedConfigurationContainer container) {
|
||||
final ConfigurationContainer presentContainer = this.containers.put(area, container);
|
||||
if (presentContainer == null) {
|
||||
if (this.isLargeArea(area)) {
|
||||
this.largeAreas.add(area);
|
||||
} else {
|
||||
this.updateSections(area, false);
|
||||
}
|
||||
|
||||
if ((changes++ & 15) == 0) {
|
||||
this.resizeSections();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable SealedConfigurationContainer remove(final ConfigurationArea area) {
|
||||
final SealedConfigurationContainer container = this.containers.remove(area);
|
||||
if (this.isLargeArea(area)) {
|
||||
this.largeAreas.remove(area);
|
||||
} else if (container != null) {
|
||||
this.updateSections(area, true);
|
||||
}
|
||||
|
||||
if ((changes++ & 15) == 0) {
|
||||
this.resizeSections();
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
public @Nullable SealedConfigurationContainer get(final ConfigurationArea area) {
|
||||
return this.containers.get(area);
|
||||
}
|
||||
|
||||
public @Nullable ConfigurationContainer getContainer(final int x, final int y, final int z) {
|
||||
final ConfigurationContainer newContainer = new ConfigurationContainer();
|
||||
final List<ConfigurationArea> areas = this.getAreas(x, y, z);
|
||||
areas.sort(AREA_BY_VOLUME); // sorted by size
|
||||
|
||||
for (final ConfigurationArea area : areas) {
|
||||
if (area.contains(x, y, z)) {
|
||||
newContainer.fillAbsentValues(this.containers.get(area));
|
||||
}
|
||||
}
|
||||
|
||||
return newContainer.contents().isEmpty() ? null : newContainer;
|
||||
}
|
||||
|
||||
public List<ConfigurationArea> getAreas(final int x, final int y, final int z) {
|
||||
final long sectionKey = ConfigurationArea.sectionKey(x, y, z, this.sectionExponent);
|
||||
final List<ConfigurationArea> nearby = this.smallAreas.getOrDefault(sectionKey, Collections.emptyList());
|
||||
final List<ConfigurationArea> foundAreas = new ArrayList<>();
|
||||
|
||||
for (final ConfigurationArea area : Iterables.concat(nearby, this.largeAreas)) {
|
||||
if (area.contains(x, y, z)) {
|
||||
foundAreas.add(area);
|
||||
}
|
||||
}
|
||||
|
||||
return foundAreas;
|
||||
}
|
||||
|
||||
private int calculateNewSectionExponent() {
|
||||
long totalSectionCount = 0;
|
||||
int totalAreas = 0;
|
||||
for (final ConfigurationArea area : this.containers.keySet()) {
|
||||
final long sections = area.countSections(4);
|
||||
if (sections < MASSIVE_REGION_SIZE) {
|
||||
totalSectionCount += sections;
|
||||
totalAreas++;
|
||||
}
|
||||
}
|
||||
|
||||
final long averageSectionCount = totalSectionCount / Math.max(totalAreas, 1);
|
||||
for (int exponent = 4;; exponent++) {
|
||||
if ((averageSectionCount >> exponent) < LARGE_AREA_THRESHOLD) {
|
||||
return exponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resizeSections() {
|
||||
final int newExponent = this.calculateNewSectionExponent();
|
||||
if (newExponent == this.sectionExponent) {
|
||||
return; // nothing has changed
|
||||
}
|
||||
|
||||
this.sectionExponent = newExponent;
|
||||
this.smallAreas.clear();
|
||||
this.largeAreas.clear();
|
||||
|
||||
for (final ConfigurationArea area : this.containers.keySet()) {
|
||||
if (this.isLargeArea(area)) {
|
||||
this.largeAreas.add(area);
|
||||
} else {
|
||||
this.updateSections(area, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSections(final ConfigurationArea area, final boolean remove) {
|
||||
area.forEach(this.sectionExponent, sectionKey -> {
|
||||
if (remove) {
|
||||
final List<ConfigurationArea> areas = this.smallAreas.get(sectionKey);
|
||||
//noinspection ConstantValue
|
||||
if (areas != null) {
|
||||
areas.remove(area);
|
||||
if (areas.isEmpty()) {
|
||||
this.smallAreas.remove(sectionKey);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.smallAreas.computeIfAbsent(sectionKey, k -> new ArrayList<>()).add(area);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import me.samsuik.sakura.explosion.durable.DurableMaterial;
|
||||
import me.samsuik.sakura.local.LocalValueKeys;
|
||||
import me.samsuik.sakura.local.storage.LocalValueStorage;
|
||||
import me.samsuik.sakura.physics.PhysicsVersion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class LocalValueConfig {
|
||||
public Map<Block, DurableMaterial> durableMaterials;
|
||||
public RedstoneImplementation redstoneImplementation;
|
||||
public PhysicsVersion physicsVersion;
|
||||
public boolean consistentRadius;
|
||||
public boolean redstoneCache;
|
||||
public int lavaFlowSpeed = -1;
|
||||
|
||||
LocalValueConfig(Level level) {
|
||||
this.durableMaterials = new Reference2ObjectOpenHashMap<>(level.sakuraConfig().cannons.explosion.durableMaterials);
|
||||
this.redstoneImplementation = level.paperConfig().misc.redstoneImplementation;
|
||||
this.physicsVersion = level.sakuraConfig().cannons.mechanics.physicsVersion;
|
||||
this.consistentRadius = level.sakuraConfig().cannons.explosion.consistentRadius;
|
||||
this.redstoneCache = level.sakuraConfig().technical.redstone.redstoneCache;
|
||||
}
|
||||
|
||||
void loadFromStorage(LocalValueStorage storage) {
|
||||
storage.get(LocalValueKeys.DURABLE_MATERIALS).ifPresent(materials -> {
|
||||
materials.forEach((materialType, materialProperties) -> {
|
||||
Block nmsBlock = CraftMagicNumbers.getBlock(materialType);
|
||||
// temp, will be updated later
|
||||
DurableMaterial durableMaterial = new DurableMaterial(materialProperties.getKey(), materialProperties.getValue(), false);
|
||||
this.durableMaterials.put(nmsBlock, durableMaterial);
|
||||
});
|
||||
});
|
||||
storage.get(LocalValueKeys.REDSTONE_IMPLEMENTATION).ifPresent(implementation -> {
|
||||
this.redstoneImplementation = RedstoneImplementation.values()[implementation.ordinal()];
|
||||
});
|
||||
this.physicsVersion = storage.getOrDefault(LocalValueKeys.PHYSICS_VERSION, this.physicsVersion);
|
||||
this.consistentRadius = storage.getOrDefault(LocalValueKeys.CONSISTENT_EXPLOSION_RADIUS, this.consistentRadius);
|
||||
this.redstoneCache = storage.getOrDefault(LocalValueKeys.REDSTONE_CACHE, this.redstoneCache);
|
||||
this.lavaFlowSpeed = storage.getOrDefault(LocalValueKeys.LAVA_FLOW_SPEED, this.lavaFlowSpeed);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package me.samsuik.sakura.configuration.local;
|
||||
|
||||
import me.samsuik.sakura.local.LocalRegion;
|
||||
|
||||
public final class OverlappingRegionException extends RuntimeException {
|
||||
public OverlappingRegionException(LocalRegion presentRegion, LocalRegion region) {
|
||||
super("overlapping region (%s, %s)".formatted(presentRegion, region));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package me.samsuik.sakura.configuration.serializer;
|
||||
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import me.samsuik.sakura.mechanics.MechanicVersion;
|
||||
import me.samsuik.sakura.mechanics.MinecraftMechanicsTarget;
|
||||
import me.samsuik.sakura.mechanics.MinecraftVersionEncoding;
|
||||
import me.samsuik.sakura.mechanics.ServerType;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.NodePath;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@NullMarked
|
||||
public final class MinecraftMechanicsTargetSerializer implements TypeSerializer<MinecraftMechanicsTarget> {
|
||||
private static final NodePath MECHANIC_VERSION = NodePath.path("mechanic-version");
|
||||
private static final NodePath SERVER_TYPE = NodePath.path("server-type");
|
||||
|
||||
@Override
|
||||
public MinecraftMechanicsTarget deserialize(final Type type, final ConfigurationNode root) throws SerializationException {
|
||||
final String mechanicVersion = root.node(MECHANIC_VERSION).getString();
|
||||
final String serverType = root.node(SERVER_TYPE).getString();
|
||||
final MinecraftMechanicsTarget mechanicsTarget = MinecraftMechanicsTarget.fromString("%s+%s".formatted(mechanicVersion, serverType));
|
||||
|
||||
if (mechanicsTarget == null) {
|
||||
throw new IllegalArgumentException("Unable to deserialize MinecraftMechanicsTarget (" + mechanicVersion + ", " + serverType + ")");
|
||||
}
|
||||
|
||||
return mechanicsTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(
|
||||
final Type type,
|
||||
@Nullable MinecraftMechanicsTarget mechanicsTarget,
|
||||
final ConfigurationNode root
|
||||
) throws SerializationException {
|
||||
if (mechanicsTarget == null) {
|
||||
mechanicsTarget = MinecraftMechanicsTarget.latest();
|
||||
}
|
||||
|
||||
root.node(MECHANIC_VERSION).set(MechanicVersion.name(mechanicsTarget.mechanicVersion()));
|
||||
root.node(SERVER_TYPE).set(ServerType.name(mechanicsTarget.serverType()));
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ public final class ConfigurationTransformations {
|
||||
V8_RenameExplosionResistantItems.apply(versionedBuilder);
|
||||
V9_RenameAllowNonTntBreakingDurableBlocks.apply(versionedBuilder);
|
||||
V10_DurableMaterialOnlyDamagedByTnt.apply(versionedBuilder);
|
||||
V11_RemovePhysicsVersion.apply(versionedBuilder);
|
||||
// ADD FUTURE VERSIONED TRANSFORMS TO versionedBuilder HERE
|
||||
versionedBuilder.build().apply(node);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.samsuik.sakura.configuration.transformation.world;
|
||||
|
||||
import org.spongepowered.configurate.NodePath;
|
||||
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
|
||||
import org.spongepowered.configurate.transformation.TransformAction;
|
||||
|
||||
import static org.spongepowered.configurate.NodePath.path;
|
||||
|
||||
public final class V11_RemovePhysicsVersion {
|
||||
private static final int VERSION = 11;
|
||||
private static final NodePath PATH = path("cannons", "mechanics", "physics-version");
|
||||
|
||||
public static void apply(final ConfigurationTransformation.VersionedBuilder builder) {
|
||||
builder.addVersion(VERSION, ConfigurationTransformation.builder()
|
||||
.addAction(PATH, TransformAction.remove())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
package me.samsuik.sakura.explosion;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class LegacyExplosionClipping {
|
||||
public static BlockHitResult.Type clipLegacy(Level level, Vec3 from, Vec3 to) {
|
||||
int toX = Mth.floor(to.x);
|
||||
int toY = Mth.floor(to.y);
|
||||
int toZ = Mth.floor(to.z);
|
||||
int fromX = Mth.floor(from.x);
|
||||
int fromY = Mth.floor(from.y);
|
||||
int fromZ = Mth.floor(from.z);
|
||||
|
||||
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(fromX, fromY, fromZ);
|
||||
LevelChunk chunk = level.getChunkIfLoaded(fromX >> 4, fromZ >> 4);
|
||||
if (chunk == null) {
|
||||
return BlockHitResult.Type.MISS;
|
||||
}
|
||||
|
||||
BlockState state = chunk.getBlockState(mutableBlockPos);
|
||||
VoxelShape shape = state.getShape(level, mutableBlockPos);
|
||||
for (AABB bb : shape.toAabbs()) {
|
||||
if (clip(bb, mutableBlockPos, from, to)) {
|
||||
return BlockHitResult.Type.BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
for (int steps = 0; steps < 16; ++steps) {
|
||||
if (fromX == toX && fromY == toY && fromZ == toZ) {
|
||||
return BlockHitResult.Type.MISS;
|
||||
}
|
||||
|
||||
boolean moveX = true;
|
||||
boolean moveY = true;
|
||||
boolean moveZ = true;
|
||||
double d0 = 999.0D;
|
||||
double d1 = 999.0D;
|
||||
double d2 = 999.0D;
|
||||
|
||||
if (toX > fromX) {
|
||||
d0 = (double) fromX + 1.0D;
|
||||
} else if (toX < fromX) {
|
||||
d0 = (double) fromX + 0.0D;
|
||||
} else {
|
||||
moveX = false;
|
||||
}
|
||||
|
||||
if (toY > fromY) {
|
||||
d1 = (double) fromY + 1.0D;
|
||||
} else if (toY < fromY) {
|
||||
d1 = (double) fromY + 0.0D;
|
||||
} else {
|
||||
moveY = false;
|
||||
}
|
||||
|
||||
if (toZ > fromZ) {
|
||||
d2 = (double) fromZ + 1.0D;
|
||||
} else if (toZ < fromZ) {
|
||||
d2 = (double) fromZ + 0.0D;
|
||||
} else {
|
||||
moveZ = false;
|
||||
}
|
||||
|
||||
double d3 = 999.0D;
|
||||
double d4 = 999.0D;
|
||||
double d5 = 999.0D;
|
||||
double d6 = to.x - from.x;
|
||||
double d7 = to.y - from.y;
|
||||
double d8 = to.z - from.z;
|
||||
|
||||
if (moveX) d3 = (d0 - from.x) / d6;
|
||||
if (moveY) d4 = (d1 - from.y) / d7;
|
||||
if (moveZ) d5 = (d2 - from.z) / d8;
|
||||
|
||||
if (d3 == -0.0D) d3 = -1.0E-4D;
|
||||
if (d4 == -0.0D) d4 = -1.0E-4D;
|
||||
if (d5 == -0.0D) d5 = -1.0E-4D;
|
||||
|
||||
Direction moveDir;
|
||||
if (d3 < d4 && d3 < d5) {
|
||||
moveDir = toX > fromX ? Direction.WEST : Direction.EAST;
|
||||
from = new Vec3(d0, from.y + d7 * d3, from.z + d8 * d3);
|
||||
} else if (d4 < d5) {
|
||||
moveDir = toY > fromY ? Direction.DOWN : Direction.UP;
|
||||
from = new Vec3(from.x + d6 * d4, d1, from.z + d8 * d4);
|
||||
} else {
|
||||
moveDir = toZ > fromZ ? Direction.NORTH : Direction.SOUTH;
|
||||
from = new Vec3(from.x + d6 * d5, from.y + d7 * d5, d2);
|
||||
}
|
||||
|
||||
fromX = Mth.floor(from.x) - (moveDir == Direction.EAST ? 1 : 0);
|
||||
fromY = Mth.floor(from.y) - (moveDir == Direction.UP ? 1 : 0);
|
||||
fromZ = Mth.floor(from.z) - (moveDir == Direction.SOUTH ? 1 : 0);
|
||||
mutableBlockPos.set(fromX, fromY, fromZ);
|
||||
|
||||
int chunkX = fromX >> 4;
|
||||
int chunkZ = fromZ >> 4;
|
||||
if (chunkX != chunk.locX || chunkZ != chunk.locZ) {
|
||||
chunk = level.getChunkIfLoaded(chunkX, chunkZ);
|
||||
}
|
||||
if (chunk == null) {
|
||||
return BlockHitResult.Type.MISS;
|
||||
}
|
||||
|
||||
state = chunk.getBlockState(mutableBlockPos);
|
||||
shape = state.getShape(level, mutableBlockPos);
|
||||
for (AABB bb : shape.toAabbs()) {
|
||||
if (clip(bb, mutableBlockPos, from, to)) {
|
||||
return BlockHitResult.Type.BLOCK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BlockHitResult.Type.MISS;
|
||||
}
|
||||
|
||||
private static boolean clip(AABB bb, BlockPos pos, Vec3 from, Vec3 to) {
|
||||
from = from.subtract(pos.getX(), pos.getY(), pos.getZ());
|
||||
to = to.subtract(pos.getX(), pos.getY(), pos.getZ());
|
||||
|
||||
double x = to.x - from.x;
|
||||
double y = to.y - from.y;
|
||||
double z = to.z - from.z;
|
||||
|
||||
double minXd = clip(bb.minX, x, from.x);
|
||||
double minYd = clip(bb.minY, y, from.y);
|
||||
double minZd = clip(bb.minZ, z, from.z);
|
||||
double maxXd = clip(bb.maxX, x, from.x);
|
||||
double maxYd = clip(bb.maxY, y, from.y);
|
||||
double maxZd = clip(bb.maxZ, z, from.z);
|
||||
|
||||
return clipX(from, bb, minXd, y, z) || clipY(from, bb, minYd, x, z) || clipZ(from, bb, minZd, x, y)
|
||||
|| clipX(from, bb, maxXd, y, z) || clipY(from, bb, maxYd, x, z) || clipZ(from, bb, maxZd, x, y);
|
||||
}
|
||||
|
||||
private static double clip(double bound, double axisD, double axisN) {
|
||||
if (axisD * axisD < 1.0000000116860974E-7D) {
|
||||
return -1.0;
|
||||
}
|
||||
return (bound - axisN) / axisD;
|
||||
}
|
||||
|
||||
private static boolean clipX(Vec3 from, AABB bb, double n, double y, double z) {
|
||||
if (n < 0.0 || n > 1.0) {
|
||||
return false;
|
||||
}
|
||||
y = from.y + y * n;
|
||||
z = from.z + z * n;
|
||||
return y >= bb.minY && y <= bb.maxY && z >= bb.minZ && z <= bb.maxZ;
|
||||
}
|
||||
|
||||
private static boolean clipY(Vec3 from, AABB bb, double n, double x, double z) {
|
||||
if (n < 0.0 || n > 1.0) {
|
||||
return false;
|
||||
}
|
||||
x = from.x + x * n;
|
||||
z = from.z + z * n;
|
||||
return x >= bb.minX && x <= bb.maxX && z >= bb.minZ && z <= bb.maxZ;
|
||||
}
|
||||
|
||||
private static boolean clipZ(Vec3 from, AABB bb, double n, double x, double y) {
|
||||
if (n < 0.0 || n > 1.0) {
|
||||
return false;
|
||||
}
|
||||
x = from.x + x * n;
|
||||
y = from.y + y * n;
|
||||
return x >= bb.minX && x <= bb.maxX && y >= bb.minY && y <= bb.maxY;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import me.samsuik.sakura.mechanics.MechanicVersion;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.Mth;
|
||||
@@ -159,7 +160,7 @@ public abstract class SpecialisedExplosion<T extends Entity> extends ServerExplo
|
||||
double z = entity.getZ() - pos.z;
|
||||
double distance = Math.sqrt(x * x + y * y + z * z);
|
||||
// Sakura start - configure cannon physics
|
||||
if (this.physics.before(1_17_0)) {
|
||||
if (this.mechanicsTarget.before(MechanicVersion.v1_17)) {
|
||||
distanceFromBottom = (float) distanceFromBottom;
|
||||
distance = (float) distance;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import me.samsuik.sakura.entity.EntityState;
|
||||
import me.samsuik.sakura.entity.merge.MergeLevel;
|
||||
import me.samsuik.sakura.entity.merge.MergeableEntity;
|
||||
import me.samsuik.sakura.mechanics.MechanicVersion;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -38,7 +39,7 @@ public final class TntExplosion extends SpecialisedExplosion<PrimedTnt> {
|
||||
// Sakura start - configure cannon physics
|
||||
@Override
|
||||
protected double getExplosionOffset() {
|
||||
return this.physics.before(1_10_0) ? (double) 0.49f : super.getExplosionOffset();
|
||||
return this.mechanicsTarget.before(MechanicVersion.v1_10) ? (double) 0.49f : super.getExplosionOffset();
|
||||
}
|
||||
// Sakura end - configure cannon physics
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@ package me.samsuik.sakura.explosion.durable;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@NullMarked
|
||||
public final class DurableBlockManager {
|
||||
private final Cache<BlockPos, DurableBlock> durableBlocks = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(1, TimeUnit.MINUTES)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package me.samsuik.sakura.explosion.durable;
|
||||
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Required;
|
||||
|
||||
@ConfigSerializable
|
||||
public record DurableMaterial(int durability, float resistance, @Required boolean onlyDamagedByTnt) {
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public final class EntityBehaviour {
|
||||
public static void pre1_21_6$changeEntityPosition(
|
||||
final Entity entity,
|
||||
final Vec3 position,
|
||||
final Vec3 relativeMovement,
|
||||
final MinecraftMechanicsTarget mechanicsTarget
|
||||
) {
|
||||
final Vec3 newPosition = position.add(relativeMovement);
|
||||
final Vec3 newEntityPosition;
|
||||
if (mechanicsTarget.is(MechanicVersion.v1_21_5)) {
|
||||
newEntityPosition = manglePosition(position, relativeMovement);
|
||||
entity.addMovementThisTick(new Entity.Movement(position, newPosition, true));
|
||||
} else {
|
||||
newEntityPosition = newPosition;
|
||||
}
|
||||
|
||||
entity.setPos(newEntityPosition);
|
||||
}
|
||||
|
||||
private static Vec3 manglePosition(final Vec3 position, final Vec3 relativeMovement) {
|
||||
Vec3 newPosition = position;
|
||||
for (final Direction.Axis axis : Entity.axisStepOrder(relativeMovement)) {
|
||||
final double movement = relativeMovement.get(axis);
|
||||
if (movement != 0.0) {
|
||||
newPosition = newPosition.relative(axis.getPositive(), movement);
|
||||
}
|
||||
}
|
||||
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
public static boolean canMoveEntity(final double relativeMovementSqr, final Vec3 movement, final MinecraftMechanicsTarget mechanicsTarget) {
|
||||
return relativeMovementSqr > 1.0E-7
|
||||
|| mechanicsTarget.atLeast(MechanicVersion.v1_21_2) && movement.lengthSqr() - relativeMovementSqr < 1.0E-7
|
||||
|| mechanicsTarget.before(MechanicVersion.v1_14);
|
||||
}
|
||||
|
||||
public static boolean prioritiseXFirst(final double x, final double z, final @Nullable MinecraftMechanicsTarget mechanicsTarget) {
|
||||
return mechanicsTarget == null || mechanicsTarget.atLeast(MechanicVersion.v1_14)
|
||||
? Math.abs(x) < Math.abs(z)
|
||||
: mechanicsTarget.isLegacy() && Math.abs(x) > Math.abs(z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.item.FallingBlockEntity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.FallingBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class FallingBlockBehaviour {
|
||||
public static boolean isAbleToStackOnBlock(final FallingBlockEntity fallingBlock, final MinecraftMechanicsTarget mechanicsTarget) {
|
||||
if (!mechanicsTarget.between(me.samsuik.sakura.mechanics.MechanicVersion.v1_9, me.samsuik.sakura.mechanics.MechanicVersion.v1_14)) {
|
||||
return true;
|
||||
}
|
||||
// This is patched by default on Paper.
|
||||
if (mechanicsTarget.isServerType(me.samsuik.sakura.mechanics.ServerType.PAPER)) {
|
||||
return true;
|
||||
}
|
||||
// todo: Entity#getOnPos might be a good alternative to this
|
||||
final BlockPos blockPos = BlockPos.containing(fallingBlock.getX(), fallingBlock.getY() - 0.001f, fallingBlock.getZ());
|
||||
final BlockState state = fallingBlock.level().getBlockState(blockPos);
|
||||
return !FallingBlock.isFree(state);
|
||||
}
|
||||
|
||||
public static void removeBlockOnFall(final FallingBlockEntity fallingBlock, final Block block) {
|
||||
final Level level = fallingBlock.level();
|
||||
final BlockPos blockPos = fallingBlock.blockPosition();
|
||||
final BlockState state = level.getBlockState(blockPos);
|
||||
|
||||
// todo: Do we need to call the event here? This event is already called in the fall method that spawns the falling block entity.
|
||||
if (state.is(block) && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(fallingBlock, blockPos, Blocks.AIR.defaultBlockState())) {
|
||||
level.removeBlock(blockPos, false);
|
||||
} else {
|
||||
if (state.is(block)) {
|
||||
((ServerLevel) level).getChunkSource().blockChanged(blockPos);
|
||||
}
|
||||
fallingBlock.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* A replica of the explosion raytrace code before it was replaced in Minecraft 1.14.
|
||||
*/
|
||||
@NullMarked
|
||||
public final class LegacyExplosionBlockClipping {
|
||||
private static final double EPSILON = 1.0e-7f; // the precision loss is intentional
|
||||
|
||||
private Vec3 currentPos;
|
||||
private final Vec3 endPos;
|
||||
private final int toX;
|
||||
private final int toY;
|
||||
private final int toZ;
|
||||
|
||||
private LegacyExplosionBlockClipping(final Vec3 currentPos, final Vec3 endPos) {
|
||||
this.currentPos = currentPos;
|
||||
this.endPos = endPos;
|
||||
this.toX = Mth.floor(endPos.x);
|
||||
this.toY = Mth.floor(endPos.y);
|
||||
this.toZ = Mth.floor(endPos.z);
|
||||
}
|
||||
|
||||
public static BlockHitResult.Type clip(final Level level, final Vec3 currentPos, final Vec3 endPos) {
|
||||
final LegacyExplosionBlockClipping clipDetection = new LegacyExplosionBlockClipping(currentPos, endPos);
|
||||
final BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
||||
LevelChunk chunk = null;
|
||||
int steps = 0;
|
||||
|
||||
do {
|
||||
final int chunkX = Mth.floor(currentPos.x) >> 4;
|
||||
final int chunkZ = Mth.floor(currentPos.z) >> 4;
|
||||
if (chunk == null || chunkX != chunk.locX || chunkZ != chunk.locZ) {
|
||||
chunk = level.getChunkIfLoaded(chunkX, chunkZ);
|
||||
if (chunk == null) break;
|
||||
}
|
||||
|
||||
final BlockState state = chunk.getBlockState(mutableBlockPos);
|
||||
final VoxelShape shape = state.getShape(level, mutableBlockPos);
|
||||
for (final AABB shapeBB : shape.toAabbs()) {
|
||||
if (clip(shapeBB, mutableBlockPos, currentPos, endPos)) {
|
||||
return HitResult.Type.BLOCK;
|
||||
}
|
||||
}
|
||||
} while (++steps < 16 && clipDetection.next(mutableBlockPos));
|
||||
|
||||
return HitResult.Type.MISS;
|
||||
}
|
||||
|
||||
private boolean next(final BlockPos.MutableBlockPos mutableBlockPos) {
|
||||
final int currX = mutableBlockPos.getX();
|
||||
final int currY = mutableBlockPos.getY();
|
||||
final int currZ = mutableBlockPos.getZ();
|
||||
final int toX = this.toX;
|
||||
final int toY = this.toY;
|
||||
final int toZ = this.toZ;
|
||||
|
||||
if (currX == toX && currY == toY && currZ == toZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean moveX = true;
|
||||
boolean moveY = true;
|
||||
boolean moveZ = true;
|
||||
double d0 = 999.0D;
|
||||
double d1 = 999.0D;
|
||||
double d2 = 999.0D;
|
||||
|
||||
if (toX > currX) {
|
||||
d0 = (double) currX + 1.0D;
|
||||
} else if (toX < currX) {
|
||||
d0 = (double) currX + 0.0D;
|
||||
} else {
|
||||
moveX = false;
|
||||
}
|
||||
|
||||
if (toY > currY) {
|
||||
d1 = (double) currY + 1.0D;
|
||||
} else if (toY < currY) {
|
||||
d1 = (double) currY + 0.0D;
|
||||
} else {
|
||||
moveY = false;
|
||||
}
|
||||
|
||||
if (toZ > currZ) {
|
||||
d2 = (double) currZ + 1.0D;
|
||||
} else if (toZ < currZ) {
|
||||
d2 = (double) currZ + 0.0D;
|
||||
} else {
|
||||
moveZ = false;
|
||||
}
|
||||
|
||||
double d3 = 999.0D;
|
||||
double d4 = 999.0D;
|
||||
double d5 = 999.0D;
|
||||
|
||||
final Vec3 currPos = this.currentPos;
|
||||
final Vec3 endPos = this.endPos;
|
||||
final double d6 = endPos.x - currPos.x;
|
||||
final double d7 = endPos.y - currPos.y;
|
||||
final double d8 = endPos.z - currPos.z;
|
||||
|
||||
if (moveX) d3 = (d0 - currPos.x) / d6;
|
||||
if (moveY) d4 = (d1 - currPos.y) / d7;
|
||||
if (moveZ) d5 = (d2 - currPos.z) / d8;
|
||||
|
||||
if (d3 == -0.0D) d3 = -1.0E-4D;
|
||||
if (d4 == -0.0D) d4 = -1.0E-4D;
|
||||
if (d5 == -0.0D) d5 = -1.0E-4D;
|
||||
|
||||
final Direction moveDir;
|
||||
final Vec3 newCurrentPos;
|
||||
if (d3 < d4 && d3 < d5) {
|
||||
moveDir = toX > currX ? Direction.WEST : Direction.EAST;
|
||||
newCurrentPos = new Vec3(d0, currPos.y + d7 * d3, currPos.z + d8 * d3);
|
||||
} else if (d4 < d5) {
|
||||
moveDir = toY > currY ? Direction.DOWN : Direction.UP;
|
||||
newCurrentPos = new Vec3(currPos.x + d6 * d4, d1, currPos.z + d8 * d4);
|
||||
} else {
|
||||
moveDir = toZ > currZ ? Direction.NORTH : Direction.SOUTH;
|
||||
newCurrentPos = new Vec3(currPos.x + d6 * d5, currPos.y + d7 * d5, d2);
|
||||
}
|
||||
|
||||
mutableBlockPos.set(
|
||||
Mth.floor(currPos.x) - (moveDir == Direction.EAST ? 1 : 0),
|
||||
Mth.floor(currPos.y) - (moveDir == Direction.UP ? 1 : 0),
|
||||
Mth.floor(currPos.z) - (moveDir == Direction.SOUTH ? 1 : 0)
|
||||
);
|
||||
|
||||
this.currentPos = newCurrentPos;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean clip(final AABB bb, final BlockPos pos, final Vec3 from, final Vec3 to) {
|
||||
final Vec3 origin = from.subtract(pos.getX(), pos.getY(), pos.getZ());
|
||||
final Vec3 direction = to.subtract(pos.getX(), pos.getY(), pos.getZ()).subtract(origin);
|
||||
double tmin = Double.NEGATIVE_INFINITY;
|
||||
double tmax = Double.POSITIVE_INFINITY;
|
||||
|
||||
if (direction.x * direction.x >= EPSILON) {
|
||||
final double t1 = (bb.minX - origin.x) / direction.x;
|
||||
final double t2 = (bb.maxX - origin.x) / direction.x;
|
||||
tmin = Math.max(tmin, Math.min(t1, t2));
|
||||
tmax = Math.min(tmax, Math.max(t1, t2));
|
||||
} else if (origin.x < bb.minX || origin.x > bb.maxX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (direction.y * direction.y >= EPSILON) {
|
||||
final double t1 = (bb.minY - origin.y) / direction.y;
|
||||
final double t2 = (bb.maxY - origin.y) / direction.y;
|
||||
tmin = Math.max(tmin, Math.min(t1, t2));
|
||||
tmax = Math.min(tmax, Math.max(t1, t2));
|
||||
} else if (origin.y < bb.minY || origin.y > bb.maxY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (direction.z * direction.z >= EPSILON) {
|
||||
double t1 = (bb.minZ - origin.z) / direction.z;
|
||||
double t2 = (bb.maxZ - origin.z) / direction.z;
|
||||
tmin = Math.max(tmin, Math.min(t1, t2));
|
||||
tmax = Math.min(tmax, Math.max(t1, t2));
|
||||
} else if (origin.z < bb.minZ || origin.z > bb.maxZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return tmax >= tmin && tmax >= 0.0 && tmin <= 1.0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package me.samsuik.sakura.mechanics;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.material.FlowingFluid;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class LiquidBehaviour {
|
||||
public static boolean canLiquidSolidify(
|
||||
final Level level,
|
||||
final BlockPos pos,
|
||||
final FluidState fluidState,
|
||||
final MinecraftMechanicsTarget mechanicsTarget
|
||||
) {
|
||||
// In legacy-paper and versions since 1.16, liquids should always solidify.
|
||||
if (mechanicsTarget.atLeast(MechanicVersion.v1_16) || mechanicsTarget.before(MechanicVersion.v1_10) && mechanicsTarget.isServerType(ServerType.PAPER)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In 1.13 and later, liquids can only solidify if they occupy at least half of the block.
|
||||
if (mechanicsTarget.atLeast(MechanicVersion.v1_13) && fluidState.getHeight(level, pos) >= 0.44444445f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// todo: not sure if this is necessary, this looks identical to the condition above.
|
||||
if (mechanicsTarget.before(MechanicVersion.v1_13)) {
|
||||
return FlowingFluid.getLegacyLevel(fluidState) < 4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.samsuik.sakura.redstone;
|
||||
|
||||
import io.papermc.paper.configuration.WorldConfiguration;
|
||||
import me.samsuik.sakura.configuration.local.LocalValueConfig;
|
||||
import me.samsuik.sakura.configuration.local.CachedLocalConfiguration;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.redstone.Orientation;
|
||||
@@ -13,10 +13,9 @@ public record RedstoneNetworkSource(WorldConfiguration.Misc.RedstoneImplementati
|
||||
BlockPos position, @Nullable Orientation orientation,
|
||||
int updateDepth, int newPower, int oldPower) {
|
||||
|
||||
public static RedstoneNetworkSource createNetworkSource(Level level, LocalValueConfig localConfig, BlockPos pos,
|
||||
public static RedstoneNetworkSource createNetworkSource(Level level, CachedLocalConfiguration localConfiguration, BlockPos pos,
|
||||
@Nullable Orientation orientation, int newPower, int oldPower) {
|
||||
WorldConfiguration.Misc.RedstoneImplementation redstoneImplementation = localConfig.redstoneImplementation;
|
||||
int updateDepth = level.neighborUpdater.getUpdateDepth();
|
||||
return new RedstoneNetworkSource(redstoneImplementation, pos, orientation, updateDepth, newPower, oldPower);
|
||||
return new RedstoneNetworkSource(localConfiguration.paperRedstoneImplementation(), pos, orientation, updateDepth, newPower, oldPower);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.samsuik.sakura.redstone;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.*;
|
||||
import me.samsuik.sakura.configuration.local.LocalValueConfig;
|
||||
import me.samsuik.sakura.configuration.local.CachedLocalConfiguration;
|
||||
import me.samsuik.sakura.utils.TickExpiry;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -41,8 +41,8 @@ public final class RedstoneWireCache {
|
||||
}
|
||||
|
||||
public boolean tryApplyFromCache(BlockPos pos, @Nullable Orientation orientation, int newPower, int oldPower) {
|
||||
LocalValueConfig localConfig = this.level.localConfig().config(pos);
|
||||
if (!localConfig.redstoneCache || this.isTrackingWireUpdates()) {
|
||||
final CachedLocalConfiguration localConfiguration = this.level.localConfig().at(pos);
|
||||
if (!localConfiguration.redstoneBehaviour.cache() || this.isTrackingWireUpdates()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public final class RedstoneWireCache {
|
||||
return true;
|
||||
}
|
||||
|
||||
RedstoneNetworkSource networkSource = RedstoneNetworkSource.createNetworkSource(this.level, localConfig, pos, orientation, newPower, oldPower);
|
||||
RedstoneNetworkSource networkSource = RedstoneNetworkSource.createNetworkSource(this.level, localConfiguration, pos, orientation, newPower, oldPower);
|
||||
RedstoneNetwork network = this.networkCache.get(networkSource);
|
||||
if (network != null) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user