huge perf improvements to underside manager and dimensional tears
This commit is contained in:
1 parent
e173ae66f5
commit
4ecd7906d6
7 files changed
+166
-325
No files matched your search
+64
-25
@@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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<CommandSourceStack> context) throws com.mojang.brigadier.exceptions.CommandSyntaxException {
|
||||
|
||||
+34
-4
@@ -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<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
|
||||
private static final Map<UUID, Set<UUID>> 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,12 +289,17 @@ 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))) {
|
||||
Set<UUID> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Mob createTaggedMob(ServerLevel level, TagKey<EntityType<?>> tag) {
|
||||
@@ -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<UUID> 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());
|
||||
|
||||
-268
@@ -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<ThoraxisAntiwaterPoolConfiguration> {
|
||||
private static final int MIN_CAVITY_HEIGHT = 8;
|
||||
private static final int MAX_CAVITY_HEIGHT = 40;
|
||||
|
||||
public ThoraxisAntiwaterPoolFeature(Codec<ThoraxisAntiwaterPoolConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<ThoraxisAntiwaterPoolConfiguration> 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);
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -18,6 +18,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class ThoraxisBloodCrystalFeature extends Feature<ThoraxisBloodCrystalConfiguration> {
|
||||
private static final int SURFACE_SEARCH_RANGE = 32;
|
||||
|
||||
public ThoraxisBloodCrystalFeature(Codec<ThoraxisBloodCrystalConfiguration> codec) {
|
||||
super(codec);
|
||||
}
|
||||
@@ -199,7 +201,8 @@ public final class ThoraxisBloodCrystalFeature extends Feature<ThoraxisBloodCrys
|
||||
int z = origin.getZ();
|
||||
int startY = Mth.clamp(origin.getY(), level.getMinBuildHeight() + 1, level.getMaxBuildHeight() - 2);
|
||||
|
||||
for (int y = startY; y > 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<ThoraxisBloodCrys
|
||||
int z = origin.getZ();
|
||||
int startY = Mth.clamp(origin.getY(), level.getMinBuildHeight() + 1, level.getMaxBuildHeight() - 2);
|
||||
|
||||
for (int y = startY; y < level.getMaxBuildHeight() - 1; y++) {
|
||||
int maxY = Math.min(level.getMaxBuildHeight() - 2, startY + SURFACE_SEARCH_RANGE);
|
||||
for (int y = startY; y < maxY; y++) {
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (state.isAir() || state.canBeReplaced() || !state.getFluidState().isEmpty()) {
|
||||
@@ -240,4 +244,3 @@ public final class ThoraxisBloodCrystalFeature extends Feature<ThoraxisBloodCrys
|
||||
return min >= max ? min : min + random.nextInt(max - min + 1);
|
||||
}
|
||||
}
|
||||
|
||||
+60
-22
@@ -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<UUID> UNDERSIDE_FORCED_GRAVITY = new HashSet<>();
|
||||
private static final Set<UUID> ACTIVE_ITEMS_SCRATCH = new HashSet<>();
|
||||
private static final Map<UUID, Long> LAST_FLIP_TICK = new HashMap<>();
|
||||
private static final Map<ServerLevel, TrackingState> 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<UUID> 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<Entity> entities = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
private final Set<Entity> forcedItems = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
private final Map<UUID, Long> 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;
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -360,8 +360,6 @@ public final class AntarchyNeoforgeMisc {
|
||||
() -> new com.craisinlord.antarchy.content.worldgen.mushroom.GlowcapHugeMushroomFeature(NoneFeatureConfiguration.CODEC));
|
||||
public static final DeferredHolder<Feature<?>, 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<Feature<?>, ThoraxisAntiwaterPoolFeature> THORAXIS_ANTIWATER_POOL = FEATURES.register("thoraxis_antiwater_pool",
|
||||
() -> new ThoraxisAntiwaterPoolFeature(ThoraxisAntiwaterPoolConfiguration.CODEC));
|
||||
public static final DeferredHolder<Feature<?>, LucidAntiwaterPoolFeature> LUCID_ANTIWATER_POOL = FEATURES.register("lucid_antiwater_pool",
|
||||
() -> new LucidAntiwaterPoolFeature(ThoraxisAntiwaterPoolConfiguration.CODEC));
|
||||
public static final DeferredHolder<Feature<?>, ThoraxisDuneFeature> THORAXIS_DUNE = FEATURES.register("thoraxis_dune",
|
||||
|
||||
Reference in new issue
Block a user