9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2025-12-19 14:59:30 +00:00

some refractoring

This commit is contained in:
Samsuik
2025-06-21 13:55:45 +01:00
parent 4c9f00050e
commit 5b8cf57b90
13 changed files with 212 additions and 172 deletions

View File

@@ -1,9 +1,11 @@
package me.samsuik.sakura.entity.merge;
import me.samsuik.sakura.entity.merge.strategy.MergeStrategy;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public final class EntityMergeHandler {
private final TrackedMergeHistory trackedHistory = new TrackedMergeHistory();
@@ -17,7 +19,7 @@ public final class EntityMergeHandler {
public boolean tryMerge(@Nullable Entity entity, @Nullable Entity previous) {
if (entity instanceof MergeableEntity mergeEntity && previous instanceof MergeableEntity) {
MergeEntityData mergeEntityData = mergeEntity.getMergeEntityData();
MergeStrategy strategy = MergeStrategy.from(mergeEntityData.getMergeLevel());
MergeStrategy strategy = MergeStrategy.from(mergeEntityData.mergeLevel);
Entity into = strategy.mergeEntity(entity, previous, this.trackedHistory);
if (into instanceof MergeableEntity intoEntity && !into.isRemoved() && mergeEntity.isSafeToMergeInto(intoEntity, strategy.trackHistory())) {
return this.mergeEntity(mergeEntity, intoEntity);
@@ -35,7 +37,7 @@ public final class EntityMergeHandler {
public void removeEntity(@Nullable Entity entity) {
if (entity instanceof MergeableEntity mergeEntity) {
MergeEntityData mergeEntityData = mergeEntity.getMergeEntityData();
MergeStrategy strategy = MergeStrategy.from(mergeEntityData.getMergeLevel());
MergeStrategy strategy = MergeStrategy.from(mergeEntityData.mergeLevel);
if (mergeEntityData.hasMerged() && strategy.trackHistory()) {
this.trackedHistory.trackHistory(entity, mergeEntityData);
}
@@ -60,7 +62,7 @@ public final class EntityMergeHandler {
* @param into the entity to merge into
* @return if successful
*/
public boolean mergeEntity(@NotNull MergeableEntity mergeEntity, @NotNull MergeableEntity into) {
public boolean mergeEntity(MergeableEntity mergeEntity, MergeableEntity into) {
MergeEntityData entities = mergeEntity.getMergeEntityData();
MergeEntityData mergeInto = into.getMergeEntityData();
mergeInto.mergeWith(entities); // merge entities together

View File

@@ -1,16 +1,17 @@
package me.samsuik.sakura.entity.merge;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface MergeCondition {
default MergeCondition and(@NotNull MergeCondition condition) {
default MergeCondition and(MergeCondition condition) {
return (e,c,t) -> this.accept(e,c,t) && condition.accept(e,c,t);
}
default MergeCondition or(@NotNull MergeCondition condition) {
default MergeCondition or(MergeCondition condition) {
return (e,c,t) -> this.accept(e,c,t) || condition.accept(e,c,t);
}
boolean accept(@NotNull Entity entity, int attempts, long sinceCreation);
boolean accept(Entity entity, int attempts, long sinceCreation);
}

View File

@@ -3,15 +3,16 @@ package me.samsuik.sakura.entity.merge;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
import java.util.List;
@NullMarked
public final class MergeEntityData {
private final Entity entity;
private final List<MergeEntityData> connected = new ObjectArrayList<>();
private int count = 1;
private MergeLevel mergeLevel = MergeLevel.NONE;
public int count = 1;
public MergeLevel mergeLevel = MergeLevel.NONE;
public MergeEntityData(Entity entity) {
this.entity = entity;
@@ -23,12 +24,17 @@ public final class MergeEntityData {
}
}
public void mergeWith(@NotNull MergeEntityData mergeEntityData) {
public void mergeWith(MergeEntityData mergeEntityData) {
this.connected.add(mergeEntityData);
this.connected.addAll(mergeEntityData.connected);
this.count += mergeEntityData.getCount();
this.count += mergeEntityData.count;
mergeEntityData.updateEntityHandles(this.entity);
mergeEntityData.setCount(0);
mergeEntityData.count = 0;
mergeEntityData.connected.clear();
}
public boolean hasMerged() {
return !this.connected.isEmpty() && this.count != 0;
}
public LongOpenHashSet getOriginPositions() {
@@ -36,24 +42,4 @@ public final class MergeEntityData {
this.connected.forEach(entityData -> positions.add(entityData.entity.getPackedOriginPosition()));
return positions;
}
public boolean hasMerged() {
return !this.connected.isEmpty() && this.count != 0;
}
public void setMergeLevel(MergeLevel mergeLevel) {
this.mergeLevel = mergeLevel;
}
public MergeLevel getMergeLevel() {
return mergeLevel;
}
public void setCount(int count) {
this.count = count;
}
public int getCount() {
return this.count;
}
}

View File

@@ -1,122 +0,0 @@
package me.samsuik.sakura.entity.merge;
import me.samsuik.sakura.utils.collections.FixedSizeCustomObjectTable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.entity.EntityTickList;
import org.jetbrains.annotations.NotNull;
public interface MergeStrategy {
/**
* If this merge strategy requires the merge history to be tracked.
*
* @return should track history
*/
boolean trackHistory();
/**
* Tries to merge the first entity into another entity.
* <p>
* The first entity should always be positioned right after the second entity in the
* {@link EntityTickList}. This method should only
* be called before the first entity and after the second entity has ticked.
*
* @param entity current entity
* @param previous last entity to tick
* @return success
*/
Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory);
/**
* Gets the {@link MergeStrategy} for the {@link MergeLevel}.
*
* @param level provided level
* @return strategy
*/
static MergeStrategy from(MergeLevel level) {
return switch (level) {
case NONE -> None.INSTANCE;
case STRICT -> Strict.INSTANCE;
case LENIENT -> Lenient.INSTANCE;
case SPAWN -> Spawn.INSTANCE;
};
}
final class None implements MergeStrategy {
private static final None INSTANCE = new None();
@Override
public boolean trackHistory() {
return false;
}
@Override
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
return null;
}
}
final class Strict implements MergeStrategy {
private static final Strict INSTANCE = new Strict();
@Override
public boolean trackHistory() {
return false;
}
@Override
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
return entity.compareState(previous) ? previous : null;
}
}
final class Lenient implements MergeStrategy {
private static final Lenient INSTANCE = new Lenient();
private final FixedSizeCustomObjectTable<Entity> entityTable = new FixedSizeCustomObjectTable<>(512, entity -> {
return entity.blockPosition().hashCode();
});
@Override
public boolean trackHistory() {
return true;
}
@Override
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
if (entity.compareState(previous)) {
return previous;
}
if (!mergeHistory.hasPreviousMerged(entity, previous)) {
this.entityTable.clear();
}
Entity nextEntity = this.entityTable.getAndWrite(entity);
if (nextEntity == null || entity == nextEntity || !nextEntity.level().equals(entity.level())) {
return null;
}
return entity.compareState(nextEntity) ? nextEntity : null;
}
}
final class Spawn implements MergeStrategy {
private static final Spawn INSTANCE = new Spawn();
private static final MergeCondition CONDITION = (e, shots, time) -> (shots > 16 || time >= 200);
@Override
public boolean trackHistory() {
return true;
}
@Override
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
final Entity mergeInto;
if (entity.tickCount == 1 && mergeHistory.hasPreviousMerged(entity, previous) && mergeHistory.hasMetCondition(previous, CONDITION)) {
mergeInto = previous;
} else {
mergeInto = entity.compareState(previous) ? previous : null;
}
return mergeInto;
}
}
}

View File

@@ -1,18 +1,19 @@
package me.samsuik.sakura.entity.merge;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface MergeableEntity {
@NotNull MergeEntityData getMergeEntityData();
MergeEntityData getMergeEntityData();
boolean isSafeToMergeInto(@NotNull MergeableEntity entity, boolean ticksLived);
boolean isSafeToMergeInto(MergeableEntity entity, boolean ticksLived);
default boolean respawnEntity() {
default boolean tryToRespawnEntity() {
MergeEntityData mergeData = this.getMergeEntityData();
int count = mergeData.getCount();
if (count > 1) {
mergeData.setCount(0);
this.respawnEntity(count);
int originalCount = mergeData.count;
if (originalCount > 1) {
mergeData.count = 0;
this.respawnEntity(originalCount);
return true;
}
return false;

View File

@@ -7,28 +7,36 @@ import me.samsuik.sakura.configuration.WorldConfiguration.Cannons.Mechanics.TNTS
import me.samsuik.sakura.utils.TickExpiry;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.FallingBlockEntity;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public final class TrackedMergeHistory {
private final Long2ObjectMap<PositionHistory> historyMap = new Long2ObjectOpenHashMap<>();
public boolean hasPreviousMerged(@NotNull Entity entity, @NotNull Entity into) {
public boolean hasPreviouslyMergedAndMeetsCondition(Entity entity, Entity into, MergeCondition condition) {
return this.hasPreviouslyMerged(entity, into) && this.hasMetCondition(entity, condition);
}
public boolean hasPreviouslyMerged(Entity entity, Entity into) {
PositionHistory positions = this.getHistory(into, false);
return positions != null && positions.hasPosition(entity);
}
public boolean hasMetCondition(@NotNull Entity entity, MergeCondition condition) {
public boolean hasMetCondition(Entity entity, MergeCondition condition) {
PositionHistory positions = this.getHistory(entity, false);
return positions != null && positions.hasMetConditions(entity, condition);
}
private boolean shouldTrackAllPositions(Entity entity, MergeEntityData mergeEntityData) {
return entity instanceof FallingBlockEntity
|| mergeEntityData.getMergeLevel() == MergeLevel.LENIENT
|| mergeEntityData.mergeLevel == MergeLevel.LENIENT
|| entity.level().sakuraConfig().cannons.mechanics.tntSpread == TNTSpread.ALL;
}
public void trackHistory(@NotNull Entity entity, @NotNull MergeEntityData mergeEntityData) {
public void trackHistory(Entity entity, MergeEntityData mergeEntityData) {
PositionHistory positions = this.getHistory(entity, true);
LongOpenHashSet originPositions = mergeEntityData.getOriginPositions();
long gameTime = entity.level().getGameTime();
@@ -43,9 +51,12 @@ public final class TrackedMergeHistory {
this.historyMap.values().removeIf(p -> p.expiry().isExpired(gameTime));
}
@Nullable
@Contract("_, false -> _; _, true -> !null")
private PositionHistory getHistory(Entity entity, boolean create) {
long originPosition = entity.getPackedOriginPosition();
PositionHistory history = this.historyMap.get(originPosition);
//noinspection ConstantValue
if (create && history == null) {
history = new PositionHistory(entity.level().getGameTime());
this.historyMap.put(originPosition, history);

View File

@@ -0,0 +1,40 @@
package me.samsuik.sakura.entity.merge.strategy;
import me.samsuik.sakura.entity.merge.TrackedMergeHistory;
import me.samsuik.sakura.utils.collections.FixedSizeCustomObjectTable;
import net.minecraft.world.entity.Entity;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
final class LenientStrategy implements MergeStrategy {
static final LenientStrategy INSTANCE = new LenientStrategy();
private final FixedSizeCustomObjectTable<Entity> entityTable = new FixedSizeCustomObjectTable<>(512, entity -> {
return entity.blockPosition().hashCode();
});
@Override
public boolean trackHistory() {
return true;
}
@Override
@Nullable
public Entity mergeEntity(Entity entity, Entity previous, TrackedMergeHistory mergeHistory) {
if (entity.compareState(previous)) {
return previous;
}
if (!mergeHistory.hasPreviouslyMerged(entity, previous)) {
this.entityTable.clear();
}
final Entity nextEntity = this.entityTable.getAndWrite(entity);
if (nextEntity == null || entity == nextEntity || !nextEntity.level().equals(entity.level())) {
return null;
}
return entity.compareState(nextEntity) ? nextEntity : null;
}
}

View File

@@ -0,0 +1,47 @@
package me.samsuik.sakura.entity.merge.strategy;
import me.samsuik.sakura.entity.merge.MergeLevel;
import me.samsuik.sakura.entity.merge.TrackedMergeHistory;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.entity.EntityTickList;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public interface MergeStrategy {
/**
* If this merge strategy requires the merge history to be tracked.
*
* @return should track history
*/
boolean trackHistory();
/**
* Tries to merge the first entity into another entity.
* <p>
* The first entity should always be positioned right after the second entity in the
* {@link EntityTickList}. This method should only
* be called before the first entity and after the second entity has ticked.
*
* @param entity current entity
* @param previous last entity to tick
* @return success
*/
@Nullable
Entity mergeEntity(Entity entity, Entity previous, TrackedMergeHistory mergeHistory);
/**
* Gets the {@link MergeStrategy} for the {@link MergeLevel}.
*
* @param level provided level
* @return strategy
*/
static MergeStrategy from(MergeLevel level) {
return switch (level) {
case NONE -> NoneStrategy.INSTANCE;
case STRICT -> StrictStrategy.INSTANCE;
case LENIENT -> LenientStrategy.INSTANCE;
case SPAWN -> SpawnStrategy.INSTANCE;
};
}
}

View File

@@ -0,0 +1,22 @@
package me.samsuik.sakura.entity.merge.strategy;
import me.samsuik.sakura.entity.merge.TrackedMergeHistory;
import net.minecraft.world.entity.Entity;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
final class NoneStrategy implements MergeStrategy {
static final NoneStrategy INSTANCE = new NoneStrategy();
@Override
public boolean trackHistory() {
return false;
}
@Override
@Nullable
public Entity mergeEntity(Entity entity, Entity previous, TrackedMergeHistory mergeHistory) {
return null;
}
}

View File

@@ -0,0 +1,30 @@
package me.samsuik.sakura.entity.merge.strategy;
import me.samsuik.sakura.entity.merge.MergeCondition;
import me.samsuik.sakura.entity.merge.TrackedMergeHistory;
import net.minecraft.world.entity.Entity;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
final class SpawnStrategy implements MergeStrategy {
static final SpawnStrategy INSTANCE = new SpawnStrategy();
private static final MergeCondition CONDITION = (e, shots, time) -> (shots > 16 || time >= 200);
@Override
public boolean trackHistory() {
return true;
}
@Override
@Nullable
public Entity mergeEntity(Entity entity, Entity previous, TrackedMergeHistory mergeHistory) {
final Entity mergeInto;
if (entity.tickCount == 1 && mergeHistory.hasPreviouslyMergedAndMeetsCondition(entity, previous, CONDITION)) {
mergeInto = previous;
} else {
mergeInto = entity.compareState(previous) ? previous : null;
}
return mergeInto;
}
}

View File

@@ -0,0 +1,22 @@
package me.samsuik.sakura.entity.merge.strategy;
import me.samsuik.sakura.entity.merge.TrackedMergeHistory;
import net.minecraft.world.entity.Entity;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
final class StrictStrategy implements MergeStrategy {
static final StrictStrategy INSTANCE = new StrictStrategy();
@Override
public boolean trackHistory() {
return false;
}
@Override
@Nullable
public Entity mergeEntity(Entity entity, Entity previous, TrackedMergeHistory mergeHistory) {
return entity.compareState(previous) ? previous : null;
}
}

View File

@@ -44,10 +44,10 @@ public final class TntExplosion extends SpecialisedExplosion<PrimedTnt> {
@Override
protected int getExplosionCount() {
if (this.cause.getMergeEntityData().getMergeLevel() == MergeLevel.NONE) {
if (this.cause.getMergeEntityData().mergeLevel == MergeLevel.NONE) {
this.mergeEntitiesBeforeExploding();
}
return this.cause.getMergeEntityData().getCount();
return this.cause.getMergeEntityData().count;
}
@Override

View File

@@ -6,7 +6,7 @@ import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class ItemStackUtil {
public final class ItemStackUtil {
public static ItemStack itemWithBlankName(Material material) {
return itemWithName(material, Component.empty());
}