mirror of
https://github.com/Samsuik/Sakura.git
synced 2025-12-19 14:59:30 +00:00
some refractoring
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user