9
0
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:
Samsuik
2025-09-23 16:05:33 +01:00
parent 3246217d29
commit 65bc3daa96
63 changed files with 2001 additions and 1244 deletions

View File

@@ -23,3 +23,6 @@ public net.minecraft.world.level.Level neighborUpdater
public net.minecraft.world.level.block.RedStoneWireBlock turbo public net.minecraft.world.level.block.RedStoneWireBlock turbo
public net.minecraft.world.level.entity.EntityTickList entities 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.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

View File

@@ -1,5 +1,13 @@
--- a/paper-api/build.gradle.kts --- a/paper-api/build.gradle.kts
+++ b/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 @@ @@ -90,7 +_,7 @@
testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-launcher")
} }

View File

@@ -1,13 +1,14 @@
--- a/src/main/java/org/bukkit/World.java --- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java
@@ -205,6 +_,10 @@ @@ -4387,6 +_,11 @@
return new Location(this, x, y, z); void setSendViewDistance(int viewDistance);
} // Paper end - view distance api
// Paper end
+ // Sakura start
+ @NotNull
+ me.samsuik.sakura.local.storage.LocalStorageHandler getStorageHandler();
+ // Sakura end
+ // 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>

View File

@@ -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);
}
}

View File

@@ -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");
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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";
};
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -2,6 +2,9 @@ package me.samsuik.sakura.redstone;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
/**
* The redstone implementation to use.
*/
@NullMarked @NullMarked
public enum RedstoneImplementation { public enum RedstoneImplementation {
VANILLA("vanilla"), VANILLA("vanilla"),
@@ -10,11 +13,11 @@ public enum RedstoneImplementation {
private final String friendlyName; private final String friendlyName;
RedstoneImplementation(String friendlyName) { RedstoneImplementation(final String friendlyName) {
this.friendlyName = friendlyName; this.friendlyName = friendlyName;
} }
public String getFriendlyName() { public final String getFriendlyName() {
return this.friendlyName; return this.friendlyName;
} }
} }

View File

@@ -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 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 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -275,7 +275,7 @@ public class RedStoneWireBlock extends Block { @@ -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) { 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.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 // 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 // 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 // to find the direction of information flow
@@ -22,7 +22,7 @@ index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae
if (!oldState.is(state.getBlock()) && !level.isClientSide) { if (!oldState.is(state.getBlock()) && !level.isClientSide) {
// Paper start - optimize redstone - replace call to updatePowerStrength // Paper start - optimize redstone - replace call to updatePowerStrength
- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { - 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 level.getWireHandler().onWireAdded(pos, state); // Alternate Current
} else { } else {
this.updateSurroundingRedstone(level, pos, state, null, true); // Vanilla/Eigencraft this.updateSurroundingRedstone(level, pos, state, null, true); // Vanilla/Eigencraft
@@ -31,7 +31,7 @@ index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae
// Paper start - optimize redstone - replace call to updatePowerStrength // Paper start - optimize redstone - replace call to updatePowerStrength
- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { - 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 level.getWireHandler().onWireRemoved(pos, state); // Alternate Current
} else { } else {
this.updateSurroundingRedstone(level, pos, state, null, false); // Vanilla/Eigencraft this.updateSurroundingRedstone(level, pos, state, null, false); // Vanilla/Eigencraft
@@ -40,7 +40,7 @@ index ddd70576d1551d77cbefb9d63bbbaf94b569b074..e76b6c44de16f4bf136bc9959f1eedae
// Paper start - optimize redstone (Alternate Current) // Paper start - optimize redstone (Alternate Current)
// Alternate Current handles breaking of redstone wires in the WireHandler. // 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.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); level.getWireHandler().onWireUpdated(pos, state, orientation);
} else } else
// Paper end - optimize redstone (Alternate Current) // Paper end - optimize redstone (Alternate Current)

View File

@@ -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 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 --- a/net/minecraft/world/item/BlockItem.java
+++ b/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; this.block = block;
} }
+ // Sakura start - explosion durable blocks + // Sakura start - explosion durable blocks
+ private void sendBlockDurabilityToPlayer(UseOnContext context) { + private void sendBlockDurabilityToPlayer(final UseOnContext context) {
+ Player player = context.getPlayer(); + final Player player = context.getPlayer();
+ BlockState state = context.getLevel().getBlockState(context.getClickedPos()); + final Level level = context.getLevel();
+ Block block = state.getBlock(); + final BlockPos clickedPos = context.getClickedPos();
+ me.samsuik.sakura.explosion.durable.DurableMaterial material = context.getLevel().localConfig().config(context.getClickedPos()).durableMaterials.get(block); +
+ // 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) { + if (material != null) {
+ int remaining = context.getLevel().durabilityManager.durability(context.getClickedPos(), material); + final int remaining = context.getLevel().durabilityManager.durability(clickedPos, material);
+ int durability = material.durability(); + final int durability = material.durability();
+ + player.getBukkitEntity().sendMessage(me.samsuik.sakura.configuration.GlobalConfiguration.get().messages.durableBlockInteractionComponent(remaining, 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))
+ );
+ } + }
+ } + }
+ +
@@ -41,7 +40,7 @@ index 6db566adf2d0df1d26221eda04aa01738df6d3d2..a12068c3adbb147eeb41a093fcd2938c
return !interactionResult.consumesAction() && context.getItemInHand().has(DataComponents.CONSUMABLE) return !interactionResult.consumesAction() && context.getItemInHand().has(DataComponents.CONSUMABLE)
? super.use(context.getLevel(), context.getPlayer(), context.getHand()) ? super.use(context.getLevel(), context.getPlayer(), context.getHand())
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java 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 --- a/net/minecraft/world/level/Level.java
+++ b/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 @@ -830,6 +830,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
@@ -53,7 +52,7 @@ index 44849a074694a3f4438cf7f0672c78219859a20f..72537c6455ff0e38c98a83fd81c5afe1
protected Level( protected Level(
WritableLevelData levelData, WritableLevelData levelData,
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java 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 --- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java
@@ -131,7 +131,7 @@ public class ServerExplosion implements Explosion { @@ -131,7 +131,7 @@ public class ServerExplosion implements Explosion {
@@ -70,12 +69,12 @@ index d45582bdfc3fa837c5aa95f499183b60b877b7c2..341b162252efc4e65cba8a32b4c7936d
} }
// Sakura end - specialised explosions // Sakura end - specialised explosions
+ // Sakura start - explosion durable blocks + // 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()) { + if (!blockState.isAir()) {
+ final Block block = blockState.getBlock(); + 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()); + return Optional.of(material.resistance());
+ } + }
+ } + }
@@ -91,8 +90,8 @@ index d45582bdfc3fa837c5aa95f499183b60b877b7c2..341b162252efc4e65cba8a32b4c7936d
} }
// CraftBukkit end // CraftBukkit end
+ // Sakura start - explosion durable blocks + // Sakura start - explosion durable blocks
+ final me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(blockPos).durableMaterials.get(block); + final me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().at(blockPos).durableMaterials.get(block);
+ if (material != null && material.durability() >= 0) { // if durability is < 0 then only altar the blast resistance + 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)) { + if (material.onlyDamagedByTnt() && !(this.source instanceof PrimedTnt) || !this.level.durabilityManager.damage(blockPos, material)) {
+ continue; + continue;
+ } + }

View File

@@ -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 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 --- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java
@@ -399,6 +399,11 @@ public class ServerExplosion implements Explosion { @@ -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()); return Optional.of(material.resistance());
} }
+ // Sakura start - destroy water logged blocks + // Sakura start - destroy water logged blocks

View File

@@ -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 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 --- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
+++ b/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_BORDER = 1 << 2;
public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3; 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 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, public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb,
final List<VoxelShape> intoVoxel, final List<AABB> intoAABB, 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 loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0;
final boolean addTicket = (collisionFlags & COLLISION_FLAG_ADD_TICKET) != 0; // Sakura - load chunks on movement 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(); final ChunkSource chunkSource = world.getChunkSource();
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
@@ -2003,7 +2005,7 @@ public final class CollisionUtil { @@ -1997,7 +1999,7 @@ public final class CollisionUtil {
continue; continue;
} }
@@ -33,7 +33,7 @@ index 3eedfdf43748a61caad88ec847d9aed8bbec4b5b..2401cca1388298db8b5007c8332f738f
final int sectionAdjust = !hasSpecial ? 1 : 0; final int sectionAdjust = !hasSpecial ? 1 : 0;
final PalettedContainer<BlockState> blocks = section.states; 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); mutablePos.set(blockX, blockY, blockZ);
if (useEntityCollisionShape) { if (useEntityCollisionShape) {
blockCollision = collisionShape.getCollisionShape(blockData, world, mutablePos); blockCollision = collisionShape.getCollisionShape(blockData, world, mutablePos);
@@ -46,10 +46,10 @@ index 3eedfdf43748a61caad88ec847d9aed8bbec4b5b..2401cca1388298db8b5007c8332f738f
blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape);
} }
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java 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 --- a/net/minecraft/world/entity/Entity.java
+++ b/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; flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_ADD_TICKET;
} }

View File

@@ -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 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 --- a/net/minecraft/world/level/block/LiquidBlock.java
+++ b/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 FluidState fluidState = state.getFluidState();
final Block block = fluidState.isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; final Block block = fluidState.isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE;
if (block == Blocks.COBBLESTONE) { - final me.samsuik.sakura.mechanics.MinecraftMechanicsTarget mechanicsTarget = level.localConfig().at(pos).mechanicsTarget;
- final me.samsuik.sakura.physics.PhysicsVersion physics = level.localConfig().config(pos).physicsVersion;
+ // Sakura start - legacy lava block formation + // 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) { + if (level.sakuraConfig().environment.blockGeneration.legacyBlockFormation) {
+ physics = me.samsuik.sakura.physics.PhysicsVersion.v1_12; + mechanicsTarget = me.samsuik.sakura.mechanics.MinecraftMechanicsTarget.legacy();
+ } else { + } else {
+ physics = level.localConfig().config(pos).physicsVersion; + mechanicsTarget = level.localConfig().at(pos).mechanicsTarget;
+ } + }
+ // Sakura end - legacy lava block formation + // Sakura end - legacy lava block formation
if (block == Blocks.COBBLESTONE && !me.samsuik.sakura.mechanics.LiquidBehaviour.canLiquidSolidify(level, pos, fluidState, mechanicsTarget)) {
// 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)) {
return true; return true;
} }
- return fluid.is(FluidTags.LAVA) && level.localConfig().config(pos).physicsVersion.before(1_13_0); diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java
+ // Sakura start - legacy lava block formation index 752099419e46070b42f9f2ecff1016d28a6ebf49..b6f198a8ef365c0414d2560b36a9c7d13d86ccbf 100644
+ return fluid.is(FluidTags.LAVA) && (level.localConfig().config(pos).physicsVersion.before(1_13_0) --- a/net/minecraft/world/level/material/LavaFluid.java
+ || level.sakuraConfig().environment.blockGeneration.legacyBlockFormation); +++ b/net/minecraft/world/level/material/LavaFluid.java
+ // Sakura end - legacy lava block formation @@ -184,9 +184,16 @@ public abstract class LavaFluid extends FlowingFluid {
// Sakura end - configure cannon physics
}
@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

View File

@@ -21,11 +21,11 @@ index bad69adfbc492d851a3542dc7f77884d9f933c8a..9250806b12171ae4f14d8dbc9dd3d947
} else {entity.inactiveTick();} // Paper - EAR 2 } else {entity.inactiveTick();} // Paper - EAR 2
profilerFiller.pop(); profilerFiller.pop();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java 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 --- a/net/minecraft/world/entity/Entity.java
+++ b/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 @@ -601,6 +601,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return newPosition; return this.mechanicsTarget;
} }
// Sakura end - configure cannon physics // Sakura end - configure cannon physics
+ // Sakura start - entity travel distance limits + // Sakura start - entity travel distance limits
@@ -44,7 +44,7 @@ index f8efa8d7ea4e79a4baac820f0042c2da7259a849..13b7652cd95b4411927d1e041b246d06
public Entity(EntityType<?> entityType, Level level) { public Entity(EntityType<?> entityType, Level level) {
this.type = entityType; 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.setPos(0.0, 0.0, 0.0);
this.eyeHeight = this.dimensions.eyeHeight(); 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 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

View File

@@ -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 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 --- a/net/minecraft/world/entity/Entity.java
+++ b/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; return Math.max(x, z) >= this.travelDistanceLimit;
} }
// Sakura end - entity travel distance limits // Sakura end - entity travel distance limits
@@ -55,7 +55,7 @@ index 13b7652cd95b4411927d1e041b246d063fa80fae..ee7da44161fee45ce3de26d7e990eab3
public Entity(EntityType<?> entityType, Level level) { public Entity(EntityType<?> entityType, Level level) {
this.type = entityType; 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) { if (xSmaller && z != 0.0) {
@@ -63,7 +63,7 @@ index 13b7652cd95b4411927d1e041b246d063fa80fae..ee7da44161fee45ce3de26d7e990eab3
z = this.scanZ(currBoundingBox, z, voxelList, bbList); z = this.scanZ(currBoundingBox, z, voxelList, bbList);
if (z != 0.0) { if (z != 0.0) {
currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetZ(currBoundingBox, z); 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) { if (x != 0.0) {
@@ -76,10 +76,10 @@ index 13b7652cd95b4411927d1e041b246d063fa80fae..ee7da44161fee45ce3de26d7e990eab3
if (x != 0.0) { if (x != 0.0) {
currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetX(currBoundingBox, x); 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 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 --- a/net/minecraft/world/entity/item/FallingBlockEntity.java
+++ b/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 // Sakura end - configure cannon physics
this.time++; this.time++;
this.applyGravity(); this.applyGravity();

View File

@@ -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 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 --- a/net/minecraft/world/entity/Entity.java
+++ b/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) { private void checkInsideBlocks(List<Entity.Movement> movements, InsideBlockEffectApplier.StepBasedCollector stepBasedCollector) {
if (this.isAffectedByBlocks()) { if (this.isAffectedByBlocks()) {
LongSet set = this.visitedBlocks; LongSet set = this.visitedBlocks;
@@ -16,7 +16,7 @@ index ee7da44161fee45ce3de26d7e990eab3a3f5ff6e..b804964e620c49130c89d67e0371f068
for (Entity.Movement movement : movements) { for (Entity.Movement movement : movements) {
Vec3 vec3 = movement.from; 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); double d = vec31.get(axis);
if (d != 0.0) { if (d != 0.0) {
Vec3 vec32 = vec3.relative(axis.getPositive(), d); 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) { + LongSet set, net.minecraft.world.level.chunk.ChunkAccess[] chunkCache) {
+ // Sakura end - optimise check inside blocks + // Sakura end - optimise check inside blocks
// Sakura start - configure cannon physics // 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; final double margin;
AABB aabb = this.makeBoundingBox(vec31).deflate(margin); if (this.mechanicsTarget.atLeast(me.samsuik.sakura.mechanics.MechanicVersion.v1_21_2)) {
@@ -1972,7 +1976,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -1953,7 +1957,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
if (!this.isAlive()) { if (!this.isAlive()) {
return false; return false;
} else { } else {
@@ -66,19 +66,19 @@ index ee7da44161fee45ce3de26d7e990eab3a3f5ff6e..b804964e620c49130c89d67e0371f068
this.debugBlockIntersection(pos, false, false); this.debugBlockIntersection(pos, false, false);
return true; return true;
diff --git a/net/minecraft/world/level/BlockGetter.java b/net/minecraft/world/level/BlockGetter.java 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 --- a/net/minecraft/world/level/BlockGetter.java
+++ b/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); 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 // Sakura end - configure cannon physics
- for (BlockPos blockPos : BlockPos.betweenClosed(boundingBox)) { - for (BlockPos blockPos : BlockPos.betweenClosed(boundingBox)) {
+ for (BlockPos blockPos : me.samsuik.sakura.utils.BlockPosIterator.iterable(boundingBox)) { // Sakura - optimise check inside blocks + for (BlockPos blockPos : me.samsuik.sakura.utils.BlockPosIterator.iterable(boundingBox)) { // Sakura - optimise check inside blocks
if (!visitor.visit(blockPos, 0)) { if (!visitor.visit(blockPos, 0)) {
return false; return false;
} }
@@ -229,6 +229,20 @@ public interface BlockGetter extends LevelHeightAccessor { @@ -235,6 +235,20 @@ public interface BlockGetter extends LevelHeightAccessor {
return true; return true;
} else { } else {
@@ -99,7 +99,7 @@ index 67c9393133f4509abf1bd352fbbc8e21dbb116e2..9e235b8bec7ed8da7c0cb099c47c3b23
LongSet set = new LongOpenHashSet(); LongSet set = new LongOpenHashSet();
Vec3 minPosition = boundingBox.getMinPosition(); Vec3 minPosition = boundingBox.getMinPosition();
Vec3 vec31 = minPosition.subtract(vec3); 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) { if (i < 0) {
return false; return false;
} else { } else {

View File

@@ -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 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 --- a/net/minecraft/world/level/ServerExplosion.java
+++ b/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; return ret;
} }
// Sakura end - optimise protected explosions // Sakura end - optimise protected explosions

View File

@@ -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 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 --- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
+++ b/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 BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
final CollisionContext collisionShape = new LazyEntityCollisionContext(entity); final CollisionContext collisionShape = new LazyEntityCollisionContext(entity);
final boolean useEntityCollisionShape = LazyEntityCollisionContext.useEntityCollisionShape(world, entity); final boolean useEntityCollisionShape = LazyEntityCollisionContext.useEntityCollisionShape(world, entity);
@@ -16,7 +16,7 @@ index 2401cca1388298db8b5007c8332f738f793880e5..f43bdff628ce94075137904ff9d714f1
// special cases: // special cases:
if (minBlockY > maxBlockY) { 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 boolean hasSpecial = !fullBlocks && ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks(); // Sakura - collide with non-solid blocks
final int sectionAdjust = !hasSpecial ? 1 : 0; final int sectionAdjust = !hasSpecial ? 1 : 0;
@@ -42,7 +42,7 @@ index 2401cca1388298db8b5007c8332f738f793880e5..f43bdff628ce94075137904ff9d714f1
for (int currY = minYIterate; currY <= maxYIterate; ++currY) { for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
final int blockY = currY | (currChunkY << 4); final int blockY = currY | (currChunkY << 4);
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java 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 --- a/net/minecraft/world/level/Level.java
+++ b/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 @@ -616,6 +616,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl

View File

@@ -19,12 +19,12 @@
+ return this.sakuraConfig; + return this.sakuraConfig;
+ } + }
+ // Sakura end - sakura configuration files + // Sakura end - sakura configuration files
+ // Sakura start - local config and property storage + // Sakura start - local config api
+ private final me.samsuik.sakura.configuration.local.LocalConfigManager localConfig = new me.samsuik.sakura.configuration.local.LocalConfigManager(this); + private final me.samsuik.sakura.configuration.local.LocalConfiguration localConfig = new me.samsuik.sakura.configuration.local.LocalConfiguration(this);
+ public final me.samsuik.sakura.configuration.local.LocalConfigManager localConfig() { + public final me.samsuik.sakura.configuration.local.LocalConfiguration localConfig() {
+ return this.localConfig; + return this.localConfig;
+ } + }
+ // Sakura end - local config and property storage + // Sakura end - local config api
public static @Nullable BlockPos lastPhysicsProblem; // Spigot public static @Nullable BlockPos lastPhysicsProblem; // Spigot
private int tileTickPosition; private int tileTickPosition;

View File

@@ -4,7 +4,7 @@
public float yield; public float yield;
// CraftBukkit end // CraftBukkit end
public boolean excludeSourceFromDamage = true; // Paper - Allow explosions to damage source 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 // Paper start - collisions optimisations
private static final double[] CACHED_RAYS; private static final double[] CACHED_RAYS;
static { static {
@@ -12,7 +12,7 @@
this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; 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 this.yield = Double.isFinite(this.yield) ? this.yield : 0; // Paper - Don't allow infinite default yields
// Paper end - add yield // 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) { private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) {
@@ -22,7 +22,7 @@
ray += 3; ray += 3;
- -
- float power = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F); - 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 { do {
final int blockX = Mth.floor(currX); final int blockX = Mth.floor(currX);

View File

@@ -12,7 +12,7 @@
+ // Sakura start - lava flow speed api + // Sakura start - lava flow speed api
+ @Override + @Override
+ public final int getTickDelay(Level world, BlockPos pos) { + 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); + return flowSpeed >= 0 ? flowSpeed : this.getTickDelay(world);
+ } + }
+ // Sakura end - lava flow speed api + // Sakura end - lava flow speed api

View File

@@ -4,12 +4,12 @@
).isValid(); ).isValid();
} }
// Paper end // Paper end
+ // Sakura start - local config and property storage + // Sakura start - local config api
+ @Override + @Override
+ public final me.samsuik.sakura.local.storage.LocalStorageHandler getStorageHandler() { + public final me.samsuik.sakura.configuration.local.LocalConfigurationAccessor localConfig() {
+ return this.getHandle().localConfig(); + return this.getHandle().localConfig();
+ } + }
+ // Sakura end - local config and property storage + // Sakura end - local config api
private static final Random rand = new Random(); private static final Random rand = new Random();

View File

@@ -1,7 +1,7 @@
package me.samsuik.sakura.command; package me.samsuik.sakura.command;
import me.samsuik.sakura.command.subcommands.*; 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.command.subcommands.debug.DebugRedstoneCache;
import me.samsuik.sakura.player.visibility.VisibilityTypes; import me.samsuik.sakura.player.visibility.VisibilityTypes;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
@@ -30,7 +30,7 @@ public final class SakuraCommands {
// "sakura" isn't a subcommand // "sakura" isn't a subcommand
COMMANDS.put("sakura", new SakuraCommand("sakura")); COMMANDS.put("sakura", new SakuraCommand("sakura"));
DEBUG_COMMANDS.add(new DebugRedstoneCache("redstone-cache")); 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) { public static void registerCommands(MinecraftServer server) {

View File

@@ -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"));
}
}

View File

@@ -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"));
}
}

View File

@@ -4,6 +4,9 @@ import com.mojang.logging.LogUtils;
import io.papermc.paper.configuration.Configuration; import io.papermc.paper.configuration.Configuration;
import io.papermc.paper.configuration.ConfigurationPart; import io.papermc.paper.configuration.ConfigurationPart;
import io.papermc.paper.configuration.type.number.IntOr; 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.bukkit.Material;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.spongepowered.configurate.objectmapping.meta.Comment; 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 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 String fpsSettingChange = "<dark_gray>(<light_purple>S</light_purple>) <gray><state> <yellow><name>";
public boolean tpsShowEntityAndChunkCount = true; 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; public Fps fps;

View File

@@ -14,7 +14,9 @@ import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2LongMap; import it.unimi.dsi.fastutil.objects.Reference2LongMap;
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap; 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.configuration.transformation.ConfigurationTransformations;
import me.samsuik.sakura.mechanics.MinecraftMechanicsTarget;
import net.minecraft.core.RegistryAccess; import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@@ -141,6 +143,7 @@ public final class SakuraConfigurations extends Configurations<GlobalConfigurati
.defaultOptions(options -> options .defaultOptions(options -> options
.header(contextMap.require(WORLD_NAME).equals(WORLD_DEFAULTS) ? WORLD_DEFAULTS_HEADER : WORLD_HEADER.apply(contextMap)) .header(contextMap.require(WORLD_NAME).equals(WORLD_DEFAULTS) ? WORLD_DEFAULTS_HEADER : WORLD_HEADER.apply(contextMap))
.serializers(serializers -> serializers .serializers(serializers -> serializers
.register(new TypeToken<MinecraftMechanicsTarget>() {}, new MinecraftMechanicsTargetSerializer())
.register(new TypeToken<Reference2IntMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2IntMap<?>>(Reference2IntOpenHashMap::new, Integer.TYPE)) .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<Reference2LongMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2LongMap<?>>(Reference2LongOpenHashMap::new, Long.TYPE))
.register(new TypeToken<Table<?, ?, ?>>() {}, new TableSerializer()) .register(new TypeToken<Table<?, ?, ?>>() {}, new TableSerializer())

View File

@@ -10,7 +10,7 @@ import io.papermc.paper.configuration.type.number.IntOr;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import me.samsuik.sakura.entity.merge.MergeLevel; import me.samsuik.sakura.entity.merge.MergeLevel;
import me.samsuik.sakura.explosion.durable.DurableMaterial; 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.Util;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
@@ -28,7 +28,7 @@ import java.util.Set;
public final class WorldConfiguration extends ConfigurationPart { public final class WorldConfiguration extends ConfigurationPart {
private static final Logger LOGGER = LogUtils.getClassLogger(); 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; private transient final ResourceLocation worldKey;
WorldConfiguration(ResourceLocation worldKey) { WorldConfiguration(ResourceLocation worldKey) {
@@ -115,7 +115,7 @@ public final class WorldConfiguration extends ConfigurationPart {
public TNTSpread tntSpread = TNTSpread.ALL; public TNTSpread tntSpread = TNTSpread.ALL;
public boolean tntFlowsInWater = true; public boolean tntFlowsInWater = true;
public boolean fallingBlockParity = false; public boolean fallingBlockParity = false;
public PhysicsVersion physicsVersion = PhysicsVersion.LATEST; public MinecraftMechanicsTarget mechanicsTarget = MinecraftMechanicsTarget.latest();
public enum TNTSpread { public enum TNTSpread {
ALL, Y, NONE; ALL, Y, NONE;

View File

@@ -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);
}
}

View File

@@ -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));
}
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
});
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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()));
}
}

View File

@@ -29,6 +29,7 @@ public final class ConfigurationTransformations {
V8_RenameExplosionResistantItems.apply(versionedBuilder); V8_RenameExplosionResistantItems.apply(versionedBuilder);
V9_RenameAllowNonTntBreakingDurableBlocks.apply(versionedBuilder); V9_RenameAllowNonTntBreakingDurableBlocks.apply(versionedBuilder);
V10_DurableMaterialOnlyDamagedByTnt.apply(versionedBuilder); V10_DurableMaterialOnlyDamagedByTnt.apply(versionedBuilder);
V11_RemovePhysicsVersion.apply(versionedBuilder);
// ADD FUTURE VERSIONED TRANSFORMS TO versionedBuilder HERE // ADD FUTURE VERSIONED TRANSFORMS TO versionedBuilder HERE
versionedBuilder.build().apply(node); versionedBuilder.build().apply(node);
} }

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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.ChunkEntitySlices;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.samsuik.sakura.mechanics.MechanicVersion;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth; 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 z = entity.getZ() - pos.z;
double distance = Math.sqrt(x * x + y * y + z * z); double distance = Math.sqrt(x * x + y * y + z * z);
// Sakura start - configure cannon physics // Sakura start - configure cannon physics
if (this.physics.before(1_17_0)) { if (this.mechanicsTarget.before(MechanicVersion.v1_17)) {
distanceFromBottom = (float) distanceFromBottom; distanceFromBottom = (float) distanceFromBottom;
distance = (float) distance; distance = (float) distance;
} }

View File

@@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.samsuik.sakura.entity.EntityState; import me.samsuik.sakura.entity.EntityState;
import me.samsuik.sakura.entity.merge.MergeLevel; import me.samsuik.sakura.entity.merge.MergeLevel;
import me.samsuik.sakura.entity.merge.MergeableEntity; import me.samsuik.sakura.entity.merge.MergeableEntity;
import me.samsuik.sakura.mechanics.MechanicVersion;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@@ -38,7 +39,7 @@ public final class TntExplosion extends SpecialisedExplosion<PrimedTnt> {
// Sakura start - configure cannon physics // Sakura start - configure cannon physics
@Override @Override
protected double getExplosionOffset() { 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 // Sakura end - configure cannon physics

View File

@@ -3,9 +3,11 @@ package me.samsuik.sakura.explosion.durable;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import org.jspecify.annotations.NullMarked;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@NullMarked
public final class DurableBlockManager { public final class DurableBlockManager {
private final Cache<BlockPos, DurableBlock> durableBlocks = CacheBuilder.newBuilder() private final Cache<BlockPos, DurableBlock> durableBlocks = CacheBuilder.newBuilder()
.expireAfterAccess(1, TimeUnit.MINUTES) .expireAfterAccess(1, TimeUnit.MINUTES)

View File

@@ -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) {
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,7 +1,7 @@
package me.samsuik.sakura.redstone; package me.samsuik.sakura.redstone;
import io.papermc.paper.configuration.WorldConfiguration; 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.core.BlockPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.redstone.Orientation; import net.minecraft.world.level.redstone.Orientation;
@@ -13,10 +13,9 @@ public record RedstoneNetworkSource(WorldConfiguration.Misc.RedstoneImplementati
BlockPos position, @Nullable Orientation orientation, BlockPos position, @Nullable Orientation orientation,
int updateDepth, int newPower, int oldPower) { 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) { @Nullable Orientation orientation, int newPower, int oldPower) {
WorldConfiguration.Misc.RedstoneImplementation redstoneImplementation = localConfig.redstoneImplementation;
int updateDepth = level.neighborUpdater.getUpdateDepth(); int updateDepth = level.neighborUpdater.getUpdateDepth();
return new RedstoneNetworkSource(redstoneImplementation, pos, orientation, updateDepth, newPower, oldPower); return new RedstoneNetworkSource(localConfiguration.paperRedstoneImplementation(), pos, orientation, updateDepth, newPower, oldPower);
} }
} }

View File

@@ -1,7 +1,7 @@
package me.samsuik.sakura.redstone; package me.samsuik.sakura.redstone;
import it.unimi.dsi.fastutil.objects.*; 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 me.samsuik.sakura.utils.TickExpiry;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; 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) { public boolean tryApplyFromCache(BlockPos pos, @Nullable Orientation orientation, int newPower, int oldPower) {
LocalValueConfig localConfig = this.level.localConfig().config(pos); final CachedLocalConfiguration localConfiguration = this.level.localConfig().at(pos);
if (!localConfig.redstoneCache || this.isTrackingWireUpdates()) { if (!localConfiguration.redstoneBehaviour.cache() || this.isTrackingWireUpdates()) {
return false; return false;
} }
@@ -51,7 +51,7 @@ public final class RedstoneWireCache {
return true; 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); RedstoneNetwork network = this.networkCache.get(networkSource);
if (network != null) { if (network != null) {
try { try {