diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java index 882a0014..7cd8f318 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/DimensionalTearMarkerBlockEntity.java @@ -22,8 +22,16 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { private static final int PARTNER_ORIGIN_ATTEMPTS = 12; private static final int PARTNER_SEARCH_ATTEMPTS = 48; private static final int LOCAL_SEARCH_ATTEMPTS = 18; + private static final int SEARCH_ATTEMPTS_PER_TICK = 2; private int attemptTicks; + private int localAttempts; + private int partnerOriginAttempts; + private int partnerSearchAttempts; + @Nullable + private BlockPos firstPocket; + @Nullable + private BlockPos partnerOrigin; public DimensionalTearMarkerBlockEntity( BlockPos pos, @@ -43,18 +51,40 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { private boolean trySpawnLinkedPair(ServerLevel level, BlockPos origin) { RandomSource random = level.getRandom(); - BlockPos first = findOpenPocket(level, origin, random, LOCAL_SEARCH_ATTEMPTS); - if (first == null) { - return false; + for (int i = 0; i < SEARCH_ATTEMPTS_PER_TICK; i++) { + if (firstPocket == null) { + if (localAttempts >= LOCAL_SEARCH_ATTEMPTS) { + return false; + } + localAttempts++; + BlockPos candidate = randomCandidate(origin, random); + if (isUsablePocket(level, candidate)) { + firstPocket = candidate; + } + continue; + } + + if (partnerOrigin == null || partnerSearchAttempts >= PARTNER_SEARCH_ATTEMPTS) { + if (partnerOriginAttempts >= PARTNER_ORIGIN_ATTEMPTS) { + return false; + } + partnerOrigin = linkedOrigin(firstPocket, random); + partnerOriginAttempts++; + partnerSearchAttempts = 0; + } + + partnerSearchAttempts++; + BlockPos candidate = randomCandidate(partnerOrigin, random); + if (!isUsablePocket(level, candidate)) { + continue; + } + return spawnPair(level, random, firstPocket, candidate); } - BlockPos second = null; - for (int i = 0; i < PARTNER_ORIGIN_ATTEMPTS && second == null; i++) { - second = findOpenPocket(level, linkedOrigin(first, random), random, PARTNER_SEARCH_ATTEMPTS); - } - if (second == null) { - return false; - } + return false; + } + + private boolean spawnPair(ServerLevel level, RandomSource random, BlockPos first, BlockPos second) { int lifetime = Math.max(1200, AntarchySettings.dimensionalTearLifetimeTicks()); float yawA = random.nextFloat() * 360.0F; @@ -68,6 +98,14 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { return true; } + private static BlockPos randomCandidate(BlockPos origin, RandomSource random) { + return origin.offset( + Mth.nextInt(random, -16, 16), + Mth.nextInt(random, -24, 24), + Mth.nextInt(random, -16, 16) + ); + } + private static BlockPos linkedOrigin(BlockPos first, RandomSource random) { double angle = random.nextDouble() * Math.PI * 2.0D; int distance = Mth.nextInt(random, MIN_LINK_RANGE, MAX_LINK_RANGE); @@ -77,25 +115,12 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { return first.offset(dx, dy, dz); } - @Nullable - private static BlockPos findOpenPocket(ServerLevel level, BlockPos origin, RandomSource random, int attempts) { + private static boolean isUsablePocket(ServerLevel level, BlockPos pos) { int minY = level.getMinBuildHeight() + 8; int maxY = level.getMaxBuildHeight() - 8; - for (int i = 0; i < attempts; i++) { - BlockPos candidate = origin.offset( - Mth.nextInt(random, -16, 16), - Mth.nextInt(random, -24, 24), - Mth.nextInt(random, -16, 16) - ); - candidate = new BlockPos(candidate.getX(), Mth.clamp(candidate.getY(), minY, maxY), candidate.getZ()); - if (isUsablePocket(level, candidate)) { - return candidate; - } + if (pos.getY() < minY || pos.getY() > maxY) { + return false; } - return null; - } - - private static boolean isUsablePocket(ServerLevel level, BlockPos pos) { for (int dx = -2; dx <= 2; dx++) { for (int dy = -2; dy <= 3; dy++) { for (int dz = -4; dz <= 4; dz++) { @@ -120,11 +145,25 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity { protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.saveAdditional(tag, registries); tag.putInt("attempt_ticks", this.attemptTicks); + tag.putInt("local_attempts", this.localAttempts); + tag.putInt("partner_origin_attempts", this.partnerOriginAttempts); + tag.putInt("partner_search_attempts", this.partnerSearchAttempts); + if (this.firstPocket != null) { + tag.putLong("first_pocket", this.firstPocket.asLong()); + } + if (this.partnerOrigin != null) { + tag.putLong("partner_origin", this.partnerOrigin.asLong()); + } } @Override protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.loadAdditional(tag, registries); this.attemptTicks = tag.getInt("attempt_ticks"); + this.localAttempts = tag.getInt("local_attempts"); + this.partnerOriginAttempts = tag.getInt("partner_origin_attempts"); + this.partnerSearchAttempts = tag.getInt("partner_search_attempts"); + this.firstPocket = tag.contains("first_pocket") ? BlockPos.of(tag.getLong("first_pocket")) : null; + this.partnerOrigin = tag.contains("partner_origin") ? BlockPos.of(tag.getLong("partner_origin")) : null; } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/DimensionalTearCommand.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/DimensionalTearCommand.java index 7cfdbfcc..a04c5d93 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/DimensionalTearCommand.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/command/DimensionalTearCommand.java @@ -37,7 +37,8 @@ public final class DimensionalTearCommand { } private static Vec3 anchorPos(ServerPlayer player) { - return player.position().add(0.0D, VERTICAL_OFFSET, 0.0D); + Vec3 forward = player.getLookAngle().normalize(); + return player.position().add(forward.scale(4.0D)).add(0.0D, VERTICAL_OFFSET, 0.0D); } private static int spawnSingle(CommandContext context) throws com.mojang.brigadier.exceptions.CommandSyntaxException { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java index 49095aa7..fd122329 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/entity/portal/DimensionalTearEntity.java @@ -6,8 +6,10 @@ import com.craisinlord.antarchy.content.AntarchyTags; import com.craisinlord.antarchy.content.entity.ManticoreEntity; import com.craisinlord.antarchy.content.entity.royal.QueenEntity; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -67,9 +69,12 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { private static final double PORTAL_RADIUS = 1.35D; private static final double EXIT_OFFSET = 2.4D; private static final Map TELEPORT_COOLDOWNS = new HashMap<>(); + private static final Map> QUEEN_TEAR_IDS = new HashMap<>(); private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private UUID linkedTearId; + @Nullable + private DimensionalTearEntity linkedTear; private Vec3 linkedFallbackPos = Vec3.ZERO; private int ageTicks; private int lifetimeTicks = DEFAULT_LIFETIME_TICKS; @@ -105,6 +110,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { DimensionalTearEntity tear = create(level, pos, yaw, lifetimeTicks); tear.queenOwnerId = queenId; tear.queenManticoreCount = Math.max(1, count); + QUEEN_TEAR_IDS.computeIfAbsent(queenId, ignored -> new HashSet<>()).add(tear.getUUID()); return tear; } @@ -119,6 +125,7 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { public void linkTo(DimensionalTearEntity other) { this.linkedTearId = other.getUUID(); this.linkedFallbackPos = other.position(); + this.linkedTear = other; } @Override @@ -282,10 +289,15 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { } public static void discardQueenOwnedTears(ServerLevel level, UUID queenId) { - for (DimensionalTearEntity tear : level.getEntitiesOfClass(DimensionalTearEntity.class, - new AABB(-3.0E7D, level.getMinBuildHeight(), -3.0E7D, 3.0E7D, level.getMaxBuildHeight(), 3.0E7D), - tear -> queenId.equals(tear.queenOwnerId))) { - tear.discard(); + Set tearIds = QUEEN_TEAR_IDS.remove(queenId); + if (tearIds == null) { + return; + } + for (UUID tearId : tearIds) { + Entity entity = level.getEntity(tearId); + if (entity instanceof DimensionalTearEntity tear) { + tear.discard(); + } } } @@ -351,11 +363,15 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { @Nullable private DimensionalTearEntity findLinkedTear() { + if (this.linkedTear != null && this.linkedTear.isAlive()) { + return this.linkedTear; + } if (!(this.level() instanceof ServerLevel serverLevel) || this.linkedTearId == null) { return null; } Entity entity = serverLevel.getEntity(this.linkedTearId); - return entity instanceof DimensionalTearEntity tear ? tear : null; + this.linkedTear = entity instanceof DimensionalTearEntity tear ? tear : null; + return this.linkedTear; } private void removeLinkedPair() { @@ -366,6 +382,20 @@ public class DimensionalTearEntity extends Entity implements GeoEntity { this.discard(); } + @Override + public void remove(RemovalReason reason) { + if (this.queenOwnerId != null) { + Set tearIds = QUEEN_TEAR_IDS.get(this.queenOwnerId); + if (tearIds != null) { + tearIds.remove(this.getUUID()); + if (tearIds.isEmpty()) { + QUEEN_TEAR_IDS.remove(this.queenOwnerId); + } + } + } + super.remove(reason); + } + private int randomEventDelay() { int min = Math.max(20, AntarchySettings.dimensionalTearEmergenceMinIntervalTicks()); int max = Math.max(min, AntarchySettings.dimensionalTearEmergenceMaxIntervalTicks()); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisAntiwaterPoolFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisAntiwaterPoolFeature.java deleted file mode 100644 index 84d06dda..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisAntiwaterPoolFeature.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.craisinlord.antarchy.content.worldgen.thoraxis; - -import com.craisinlord.antarchy.content.AntarchyObjects; -import com.mojang.serialization.Codec; -import net.minecraft.core.BlockPos; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.LiquidBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; -import net.minecraft.world.level.material.FluidState; - -public final class ThoraxisAntiwaterPoolFeature extends Feature { - private static final int MIN_CAVITY_HEIGHT = 8; - private static final int MAX_CAVITY_HEIGHT = 40; - - public ThoraxisAntiwaterPoolFeature(Codec codec) { - super(codec); - } - - @Override - public boolean place(FeaturePlaceContext context) { - WorldGenLevel level = context.level(); - BlockPos origin = context.origin(); - RandomSource random = context.random(); - ThoraxisAntiwaterPoolConfiguration config = context.config(); - - int radius = randomBetween(random, config.minRadius(), config.maxRadius()); - int depth = randomBetween(random, config.minDepth(), config.maxDepth()); - BlockState antiwaterSourceState = config.state().getState(random, origin); - BlockState nyxiteState = AntarchyObjects.NYXITE.get().defaultBlockState(); - - int pools = randomBetween(random, 1, Math.max(1, 1 + depth / 8)); - boolean placedAny = false; - - for (int i = 0; i < pools; i++) { - int offsetX = random.nextInt(radius * 2 + 1) - radius; - int offsetZ = random.nextInt(radius * 2 + 1) - radius; - int centerX = origin.getX() + offsetX; - int centerZ = origin.getZ() + offsetZ; - - int floorY = findFloorY(level, centerX, centerZ); - if (floorY <= level.getMinBuildHeight()) { - continue; - } - - int ceilingY = findCeilingY(level, centerX, centerZ, floorY); - if (ceilingY <= floorY + MIN_CAVITY_HEIGHT || ceilingY - floorY > MAX_CAVITY_HEIGHT) { - continue; - } - - int poolRadius = randomBetween(random, Math.max(2, radius / 6), Math.max(3, radius / 4 + 1)); - int ventCount = randomBetween(random, 0, Math.max(1, depth / 4)); - placedAny |= placeSparsePool(level, random, centerX, centerZ, floorY, ceilingY, poolRadius, ventCount, antiwaterSourceState, nyxiteState); - } - - return placedAny; - } - - private boolean placeSparsePool( - WorldGenLevel level, - RandomSource random, - int centerX, - int centerZ, - int floorY, - int ceilingY, - int poolRadius, - int ventCount, - BlockState antiwaterSourceState, - BlockState nyxiteState - ) { - boolean placedAny = false; - int ceilingBandY = ceilingY - 1; - int sourceY = floorY + 1; - - placedAny |= placeNyxiteBase(level, random, centerX, floorY, centerZ, poolRadius, nyxiteState); - placedAny |= seedAntiwaterVent(level, sourceY, centerX, centerZ, antiwaterSourceState); - - for (int i = 0; i < ventCount; i++) { - int ventX = centerX + random.nextInt(poolRadius * 2 + 1) - poolRadius; - int ventZ = centerZ + random.nextInt(poolRadius * 2 + 1) - poolRadius; - int distance = Math.abs(ventX - centerX) + Math.abs(ventZ - centerZ); - if (distance > poolRadius + random.nextInt(2)) { - continue; - } - - placedAny |= placeNyxiteBase(level, random, ventX, floorY, ventZ, Math.max(1, poolRadius - 1), nyxiteState); - placedAny |= seedAntiwaterVent(level, sourceY, ventX, ventZ, antiwaterSourceState); - placedAny |= seedCeilingPocket(level, random, ventX, ventZ, ceilingBandY, Math.max(1, poolRadius - 1), antiwaterSourceState); - placedAny |= seedStem(level, ventX, ventZ, floorY, ceilingBandY, antiwaterSourceState); - } - - return placedAny; - } - - private static boolean placeNyxiteBase(WorldGenLevel level, RandomSource random, int centerX, int floorY, int centerZ, int radius, BlockState nyxiteState) { - boolean placedAny = false; - BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); - - for (int dx = -radius; dx <= radius; dx++) { - for (int dz = -radius; dz <= radius; dz++) { - double distance = Math.sqrt(dx * dx + dz * dz); - if (distance > radius + random.nextDouble()) { - continue; - } - - double density = 1.0D - (distance / Math.max(1.0D, radius)); - double chance = Mth.clamp(0.18D + density * 0.45D, 0.0D, 0.78D); - if (random.nextDouble() > chance) { - continue; - } - - mutable.set(centerX + dx, floorY, centerZ + dz); - BlockState existing = level.getBlockState(mutable); - if (existing.is(Blocks.BEDROCK) || existing.blocksMotion()) { - continue; - } - - level.setBlock(mutable, nyxiteState, 2); - placedAny = true; - } - } - - return placedAny; - } - - private static boolean seedAntiwaterVent(WorldGenLevel level, int y, int x, int z, BlockState antiwaterSourceState) { - BlockPos pos = new BlockPos(x, y, z); - BlockState state = level.getBlockState(pos); - FluidState fluidState = state.getFluidState(); - if (state.is(Blocks.BEDROCK) || state.blocksMotion() || !fluidState.isEmpty()) { - return false; - } - - level.setBlock(pos, antiwaterSourceState, 2); - scheduleFluidTick(level, pos, antiwaterSourceState); - return true; - } - - private static boolean seedCeilingPocket(WorldGenLevel level, RandomSource random, int centerX, int centerZ, int ceilingBandY, int radius, BlockState antiwaterSourceState) { - boolean placedAny = false; - BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); - BlockState flowingAntiwater = flowingAntiwaterState(antiwaterSourceState, Math.max(1, Math.min(7, 6 + random.nextInt(2)))); - - for (int dx = -radius; dx <= radius; dx++) { - for (int dz = -radius; dz <= radius; dz++) { - double distance = Math.sqrt(dx * dx + dz * dz); - if (distance > radius + random.nextDouble() * 0.5D) { - continue; - } - - double density = 1.0D - (distance / Math.max(1.0D, radius)); - double chance = Mth.clamp(0.22D + density * 0.52D, 0.0D, 0.84D); - if (random.nextDouble() > chance) { - continue; - } - - mutable.set(centerX + dx, ceilingBandY, centerZ + dz); - BlockState existing = level.getBlockState(mutable); - FluidState fluidState = existing.getFluidState(); - if (existing.is(Blocks.BEDROCK) || existing.blocksMotion() || !fluidState.isEmpty()) { - continue; - } - - level.setBlock(mutable, flowingAntiwater, 2); - scheduleFluidTick(level, mutable, flowingAntiwater); - placedAny = true; - } - } - - return placedAny; - } - - private static boolean seedStem(WorldGenLevel level, int x, int z, int floorY, int ceilingBandY, BlockState antiwaterSourceState) { - boolean placedAny = false; - int stemHeight = Math.max(2, Math.min(ceilingBandY - floorY - 1, 5)); - for (int i = 1; i <= stemHeight; i++) { - int y = floorY + i; - if (y >= ceilingBandY) { - break; - } - - BlockPos pos = new BlockPos(x, y, z); - BlockState state = level.getBlockState(pos); - if (state.is(Blocks.BEDROCK) || state.blocksMotion()) { - break; - } - - BlockState stemState = i == 1 ? antiwaterSourceState : flowingAntiwaterState(antiwaterSourceState, Math.max(1, 8 - i)); - level.setBlock(pos, stemState, 2); - scheduleFluidTick(level, pos, stemState); - placedAny = true; - } - - return placedAny; - } - - private static BlockState flowingAntiwaterState(BlockState sourceState, int level) { - if (!sourceState.hasProperty(LiquidBlock.LEVEL)) { - return sourceState; - } - - int clamped = Mth.clamp(level, 1, 7); - return sourceState.setValue(LiquidBlock.LEVEL, clamped); - } - - private static void scheduleFluidTick(WorldGenLevel level, BlockPos pos, BlockState state) { - FluidState fluidState = state.getFluidState(); - if (!fluidState.isEmpty()) { - level.scheduleTick(pos, fluidState.getType(), fluidState.getType().getTickDelay(level)); - } - } - - private static int findFloorY(WorldGenLevel level, int x, int z) { - BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); - int floorY = Integer.MIN_VALUE; - - for (int y = level.getMinBuildHeight() + 1; y < level.getMaxBuildHeight() - 1; y++) { - mutable.set(x, y, z); - BlockState state = level.getBlockState(mutable); - if (!isSolidSurface(state)) { - continue; - } - - mutable.set(x, y + 1, z); - if (isOpenBlock(level.getBlockState(mutable))) { - floorY = y; - } - } - - return floorY; - } - - private static int findCeilingY(WorldGenLevel level, int x, int z, int floorY) { - BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); - for (int y = floorY + 2; y < level.getMaxBuildHeight() - 1; y++) { - mutable.set(x, y, z); - BlockState state = level.getBlockState(mutable); - if (!isSolidSurface(state)) { - continue; - } - - mutable.set(x, y - 1, z); - if (isOpenBlock(level.getBlockState(mutable))) { - return y; - } - } - - return Integer.MIN_VALUE; - } - - private static boolean isSolidSurface(BlockState state) { - return !state.isAir() && state.blocksMotion() && state.getFluidState().isEmpty(); - } - - private static boolean isOpenBlock(BlockState state) { - return state.isAir(); - } - - private static int randomBetween(RandomSource random, int minInclusive, int maxInclusive) { - int min = Math.min(minInclusive, maxInclusive); - int max = Math.max(minInclusive, maxInclusive); - return min >= max ? min : min + random.nextInt(max - min + 1); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBloodCrystalFeature.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBloodCrystalFeature.java index c90352b6..7ff837a5 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBloodCrystalFeature.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBloodCrystalFeature.java @@ -18,6 +18,8 @@ import java.util.ArrayList; import java.util.List; public final class ThoraxisBloodCrystalFeature extends Feature { + private static final int SURFACE_SEARCH_RANGE = 32; + public ThoraxisBloodCrystalFeature(Codec codec) { super(codec); } @@ -199,7 +201,8 @@ public final class ThoraxisBloodCrystalFeature extends Feature level.getMinBuildHeight(); y--) { + int minY = Math.max(level.getMinBuildHeight() + 1, startY - SURFACE_SEARCH_RANGE); + for (int y = startY; y > minY; y--) { BlockPos pos = new BlockPos(x, y, z); BlockState state = level.getBlockState(pos); if (state.isAir() || state.canBeReplaced() || !state.getFluidState().isEmpty()) { @@ -219,7 +222,8 @@ public final class ThoraxisBloodCrystalFeature extends Feature= max ? min : min + random.nextInt(max - min + 1); } } - diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java index ca9a72b3..f27b3ba5 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisUndersideManager.java @@ -6,15 +6,17 @@ import com.craisinlord.antarchy.content.block.DreamSandBlock; import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; -import java.util.HashMap; -import java.util.HashSet; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; @@ -22,6 +24,7 @@ import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; public final class ThoraxisUndersideManager { public static final int GRAVITY_FLIP_Y = 0; @@ -29,27 +32,36 @@ public final class ThoraxisUndersideManager { private static final AntarchyGravityTransition TRANSITION = new AntarchyGravityTransition(12); private static final int EFFECT_DURATION_TICKS = 50; private static final int EFFECT_REFRESH_THRESHOLD_TICKS = 10; + private static final int DISCOVERY_INTERVAL_TICKS = 5; + private static final int SIMULATION_DISTANCE_MARGIN_CHUNKS = 1; private static final int MAX_FLIPS_PER_TICK = 8; private static final int ENTER_UNDERSIDE_Y = GRAVITY_FLIP_Y - 4; private static final int EXIT_UNDERSIDE_Y = GRAVITY_FLIP_Y + 4; private static final int FLIP_COOLDOWN_TICKS = 40; - private static final Set UNDERSIDE_FORCED_GRAVITY = new HashSet<>(); - private static final Set ACTIVE_ITEMS_SCRATCH = new HashSet<>(); - private static final Map LAST_FLIP_TICK = new HashMap<>(); + private static final Map STATES = new WeakHashMap<>(); private ThoraxisUndersideManager() { } public static void tick(ServerLevel level) { - if (!isThoraxis(level) || level.players().isEmpty()) { + if (!isThoraxis(level)) { + return; + } + + TrackingState tracking = STATES.computeIfAbsent(level, ignored -> new TrackingState()); + if (level.players().isEmpty()) { + tracking.clear(); + STATES.remove(level); return; } long now = level.getGameTime(); - Set activeThisTick = ACTIVE_ITEMS_SCRATCH; - activeThisTick.clear(); + if (now % DISCOVERY_INTERVAL_TICKS == 0L) { + tracking.discover(level); + } + int flipsRemaining = MAX_FLIPS_PER_TICK; - for (Entity entity : level.getAllEntities()) { + for (Entity entity : tracking.entities) { if (!entity.isAlive() || entity.isSpectator()) { continue; } @@ -68,20 +80,19 @@ public final class ThoraxisUndersideManager { } UUID uuid = entity.getUUID(); - boolean tracked = UNDERSIDE_FORCED_GRAVITY.contains(uuid); + boolean tracked = tracking.forcedItems.contains(entity); boolean shouldInvert = tracked ? entity.getY() < EXIT_UNDERSIDE_Y : entity.getY() < ENTER_UNDERSIDE_Y; if (shouldInvert) { boolean needsFlip = AntarchyGravityApi.getGravityDirection(entity) != AntarchyGravityDirection.UP || !AntarchyGravityApi.isGravityForced(entity); - activeThisTick.add(uuid); - UNDERSIDE_FORCED_GRAVITY.add(uuid); - if (needsFlip && (flipsRemaining <= 0 || !flipReady(uuid, now))) { + tracking.forcedItems.add(entity); + if (needsFlip && (flipsRemaining <= 0 || !flipReady(tracking, uuid, now))) { continue; } if (needsFlip) { flipsRemaining--; - LAST_FLIP_TICK.put(uuid, now); + tracking.lastFlipTick.put(uuid, now); AntarchyGravityApi.setForcedGravityDirection(entity, AntarchyGravityDirection.UP, TRANSITION); } continue; @@ -90,24 +101,51 @@ public final class ThoraxisUndersideManager { if (tracked) { boolean isForcedUp = AntarchyGravityApi.getGravityDirection(entity) == AntarchyGravityDirection.UP && AntarchyGravityApi.isGravityForced(entity); - if (isForcedUp && !flipReady(uuid, now)) { - activeThisTick.add(uuid); + if (isForcedUp && !flipReady(tracking, uuid, now)) { continue; } - UNDERSIDE_FORCED_GRAVITY.remove(uuid); + tracking.forcedItems.remove(entity); if (isForcedUp) { - LAST_FLIP_TICK.put(uuid, now); + tracking.lastFlipTick.put(uuid, now); AntarchyGravityApi.setGravityDirection(entity, AntarchyGravityDirection.DOWN, false, TRANSITION); } } } - UNDERSIDE_FORCED_GRAVITY.retainAll(activeThisTick); - LAST_FLIP_TICK.values().removeIf(t -> now - t > FLIP_COOLDOWN_TICKS * 4L); + tracking.entities.removeIf(entity -> !entity.isAlive() || entity.isRemoved()); + tracking.forcedItems.removeIf(entity -> !entity.isAlive() || entity.isRemoved()); + tracking.lastFlipTick.entrySet().removeIf(entry -> now - entry.getValue() > FLIP_COOLDOWN_TICKS * 4L); } - private static boolean flipReady(UUID uuid, long now) { - Long last = LAST_FLIP_TICK.get(uuid); + private static final class TrackingState { + private final Set entities = Collections.newSetFromMap(new IdentityHashMap<>()); + private final Set forcedItems = Collections.newSetFromMap(new IdentityHashMap<>()); + private final Map lastFlipTick = new java.util.HashMap<>(); + + private void discover(ServerLevel level) { + int radius = (level.getServer().getPlayerList().getSimulationDistance() + SIMULATION_DISTANCE_MARGIN_CHUNKS) * 16; + for (ServerPlayer player : level.players()) { + AABB area = new AABB( + player.getX() - radius, level.getMinBuildHeight(), player.getZ() - radius, + player.getX() + radius, level.getMaxBuildHeight(), player.getZ() + radius + ); + entities.add(player); + entities.addAll(level.getEntitiesOfClass(LivingEntity.class, area, + entity -> !entity.isSpectator() && entity.getY() < EXIT_UNDERSIDE_Y)); + entities.addAll(level.getEntitiesOfClass(ItemEntity.class, area, + entity -> !entity.isSpectator() && entity.getY() < EXIT_UNDERSIDE_Y)); + } + } + + private void clear() { + entities.clear(); + forcedItems.clear(); + lastFlipTick.clear(); + } + } + + private static boolean flipReady(TrackingState tracking, UUID uuid, long now) { + Long last = tracking.lastFlipTick.get(uuid); return last == null || now - last >= FLIP_COOLDOWN_TICKS; } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java index 44b12f82..992a9e6b 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeMisc.java @@ -360,8 +360,6 @@ public final class AntarchyNeoforgeMisc { () -> new com.craisinlord.antarchy.content.worldgen.mushroom.GlowcapHugeMushroomFeature(NoneFeatureConfiguration.CODEC)); public static final DeferredHolder, com.craisinlord.antarchy.content.worldgen.mushroom.GlowcapHugeMushroomWorldgenFeature> HUGE_GLOWCAP_MUSHROOM_WORLDGEN = FEATURES.register("huge_glowcap_mushroom_worldgen", () -> new com.craisinlord.antarchy.content.worldgen.mushroom.GlowcapHugeMushroomWorldgenFeature(NoneFeatureConfiguration.CODEC)); - public static final DeferredHolder, ThoraxisAntiwaterPoolFeature> THORAXIS_ANTIWATER_POOL = FEATURES.register("thoraxis_antiwater_pool", - () -> new ThoraxisAntiwaterPoolFeature(ThoraxisAntiwaterPoolConfiguration.CODEC)); public static final DeferredHolder, LucidAntiwaterPoolFeature> LUCID_ANTIWATER_POOL = FEATURES.register("lucid_antiwater_pool", () -> new LucidAntiwaterPoolFeature(ThoraxisAntiwaterPoolConfiguration.CODEC)); public static final DeferredHolder, ThoraxisDuneFeature> THORAXIS_DUNE = FEATURES.register("thoraxis_dune",