MINIONS... TONIGHT WE STEAL THE MOON

This commit is contained in:
craisinlord committed 2026-10-09 23:56:18 -05:00
1 parent e3b4e50f2b
commit 759f511b00
28 files changed
+450 -103

No files matched your search

@@ -0,0 +1,52 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.PortalGunMoonAimPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunMoonMath;
import java.util.function.Consumer;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
public final class MoonSkyTracker {
private static final long MAX_AGE_MILLIS = 1500L;
private static final double AIM_COSINE = Math.cos(Math.toRadians(PortalGunMoonMath.AIM_TOLERANCE_DEGREES));
private static Vec3 direction;
private static ClientLevel level;
private static long recordedAt;
private static Consumer<PortalGunMoonAimPayload> sender = payload -> {};
private MoonSkyTracker() {
}
public static void setSender(Consumer<PortalGunMoonAimPayload> payloadSender) {
sender = payloadSender;
}
public static void recordSphericalMoon(ClientLevel renderedLevel, Camera camera, Matrix4f skyMatrix, double theta, double phi) {
Vector3d local = new Quaterniond().rotateY(theta).mul(new Quaterniond().rotateX(phi)).transform(new Vector3d(0.0D, 1.0D, 0.0D));
Vector3f view = skyMatrix.transformDirection(new Vector3f((float) local.x, (float) local.y, (float) local.z));
if (view.lengthSquared() < 1.0E-8F) {
return;
}
Vector3f world = camera.rotation().transform(view.normalize());
direction = new Vec3(world.x, world.y, world.z).normalize();
level = renderedLevel;
recordedAt = System.currentTimeMillis();
}
public static void report() {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null) {
return;
}
boolean tracked = direction != null && level == minecraft.level && System.currentTimeMillis() - recordedAt <= MAX_AGE_MILLIS;
boolean aiming = tracked && minecraft.gameRenderer.getMainCamera().getLookVector().dot(
new Vector3f((float) direction.x, (float) direction.y, (float) direction.z)) >= AIM_COSINE;
sender.accept(new PortalGunMoonAimPayload(tracked, aiming));
}
}
@@ -222,33 +222,18 @@ public class HoverboardEntity extends PathfinderMob implements GeoEntity {
private double resolveVerticalMotion(boolean holdingJump) {
AntarchyGravityDirection gravityDirection = AntarchyGravityApi.getGravityDirection(this);
double supportHeight = this.findSupportHeight(gravityDirection);
if (!gravityDirection.isInverted()) {
double maxY = supportHeight + MAX_HOVER_HEIGHT;
if (holdingJump) {
return this.getY() >= maxY ? 0.0D : ASCEND_SPEED;
}
if (this.getY() <= supportHeight) {
return 0.0D;
}
double heightAboveBand = this.getY() - maxY;
if (heightAboveBand > 0.0D) {
return -Mth.clamp(SETTLE_DESCEND_SPEED + heightAboveBand * FAST_DESCEND_RAMP, SETTLE_DESCEND_SPEED, FAST_DESCEND_SPEED);
}
return -SETTLE_DESCEND_SPEED;
}
double minY = supportHeight - MAX_HOVER_HEIGHT;
double height = gravityDirection.isInverted() ? supportHeight - this.getY() : this.getY() - supportHeight;
if (holdingJump) {
return this.getY() <= minY ? 0.0D : -ASCEND_SPEED;
return height >= MAX_HOVER_HEIGHT ? 0.0D : ASCEND_SPEED;
}
if (this.getY() >= supportHeight) {
if (height <= 0.0D) {
return 0.0D;
}
double depthBelowBand = minY - this.getY();
if (depthBelowBand > 0.0D) {
return Mth.clamp(SETTLE_DESCEND_SPEED + depthBelowBand * FAST_DESCEND_RAMP, SETTLE_DESCEND_SPEED, FAST_DESCEND_SPEED);
double heightAboveBand = height - MAX_HOVER_HEIGHT;
if (heightAboveBand > 0.0D) {
return -Mth.clamp(SETTLE_DESCEND_SPEED + heightAboveBand * FAST_DESCEND_RAMP, SETTLE_DESCEND_SPEED, FAST_DESCEND_SPEED);
}
return SETTLE_DESCEND_SPEED;
return -SETTLE_DESCEND_SPEED;
}
private double findSupportHeight(AntarchyGravityDirection gravityDirection) {
@@ -0,0 +1,108 @@
package com.craisinlord.antarchy.content.entity.glimmer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.AntarchyObjects;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.phys.AABB;
public final class GlimmerNightSpawner {
private static final ResourceKey<Level> ELYTHIA = ResourceKey.create(
Registries.DIMENSION,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "elythia")
);
private static final int CHECK_INTERVAL_TICKS = 20 * 10;
private static final float SPAWN_CHANCE = 0.35F;
private static final double NEARBY_RADIUS = 64.0D;
private static final int MAX_NEARBY_WILD = 2;
private static final double MIN_SPAWN_DISTANCE = 24.0D;
private static final double MAX_SPAWN_DISTANCE = 56.0D;
private static final int COLUMN_ATTEMPTS = 8;
private GlimmerNightSpawner() {
}
public static void tick(ServerLevel level) {
if (!level.dimension().equals(ELYTHIA)) {
return;
}
if (level.getGameTime() % CHECK_INTERVAL_TICKS != 0L || !level.isNight()) {
return;
}
if (!level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
return;
}
EntityType<GlimmerEntity> type = AntarchyObjects.GLIMMER.get();
for (ServerPlayer player : level.players()) {
if (player.isSpectator() || !player.isAlive()) {
continue;
}
if (level.random.nextFloat() >= SPAWN_CHANCE) {
continue;
}
if (countNearbyWild(level, player, type) >= MAX_NEARBY_WILD) {
continue;
}
attemptSpawnNear(level, player, type);
}
}
private static int countNearbyWild(ServerLevel level, ServerPlayer player, EntityType<GlimmerEntity> type) {
AABB box = player.getBoundingBox().inflate(NEARBY_RADIUS);
return level.getEntities(type, box, glimmer -> !glimmer.isTame()).size();
}
private static void attemptSpawnNear(ServerLevel level, ServerPlayer player, EntityType<GlimmerEntity> type) {
RandomSource random = level.random;
BlockPos origin = player.blockPosition();
for (int attempt = 0; attempt < COLUMN_ATTEMPTS; attempt++) {
double angle = random.nextDouble() * Math.PI * 2.0D;
double distance = MIN_SPAWN_DISTANCE + random.nextDouble() * (MAX_SPAWN_DISTANCE - MIN_SPAWN_DISTANCE);
int x = origin.getX() + Mth.floor(Math.cos(angle) * distance);
int z = origin.getZ() + Mth.floor(Math.sin(angle) * distance);
if (!level.hasChunkAt(new BlockPos(x, origin.getY(), z))) {
continue;
}
BlockPos pos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, new BlockPos(x, 0, z));
if (!biomeSpawnsGlimmers(level.getBiome(pos), type)) {
continue;
}
if (!SpawnPlacements.checkSpawnRules(type, level, MobSpawnType.NATURAL, pos, random)) {
continue;
}
if (!level.noCollision(type.getSpawnAABB(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D))) {
continue;
}
GlimmerEntity glimmer = type.create(level);
if (glimmer == null) {
return;
}
glimmer.moveTo(pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D, random.nextFloat() * 360.0F, 0.0F);
glimmer.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.NATURAL, null);
level.addFreshEntity(glimmer);
return;
}
}
private static boolean biomeSpawnsGlimmers(Holder<Biome> biome, EntityType<GlimmerEntity> type) {
return biome.value().getMobSettings().getMobs(MobCategory.CREATURE).unwrap().stream()
.anyMatch(data -> data.type == type);
}
}
@@ -113,6 +113,9 @@ public class PortalGunItem extends Item implements GeoItem {
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
ItemStack stack = player.getItemInHand(usedHand);
if (level.isClientSide) {
if (!player.isShiftKeyDown()) {
com.craisinlord.antarchy.content.portalgun.PortalGunMoonAimSync.reportFromClient();
}
return InteractionResultHolder.consume(stack);
}
ServerLevel serverLevel = (ServerLevel) level;
@@ -312,6 +315,10 @@ public class PortalGunItem extends Item implements GeoItem {
if (!level.dimensionType().hasSkyLight() || level.dimensionType().hasCeiling()) {
return false;
}
Boolean clientAim = player instanceof ServerPlayer serverPlayer ? com.craisinlord.antarchy.content.portalgun.PortalGunMoonAimSync.clientAim(serverPlayer) : null;
if (clientAim != null) {
return clientAim;
}
return PortalGunMoonMath.isAimedAtMoon(this.worldLook(player), level.getTimeOfDay(1.0F));
}
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record PortalGunMoonAimPayload(boolean tracked, boolean aiming) implements CustomPacketPayload {
public static final Type<PortalGunMoonAimPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_moon_aim"));
public static final StreamCodec<ByteBuf, PortalGunMoonAimPayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL, PortalGunMoonAimPayload::tracked,
ByteBufCodecs.BOOL, PortalGunMoonAimPayload::aiming,
PortalGunMoonAimPayload::new
);
@Override
public Type<PortalGunMoonAimPayload> type() {
return TYPE;
}
}
@@ -0,0 +1,48 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.network.PortalGunMoonAimPayload;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.server.level.ServerPlayer;
public final class PortalGunMoonAimSync {
private static final long MAX_REPORT_AGE_TICKS = 10L;
private static final Map<UUID, Report> REPORTS = new ConcurrentHashMap<>();
private static Runnable clientReporter = () -> {};
private PortalGunMoonAimSync() {
}
public static void setClientReporter(Runnable reporter) {
clientReporter = reporter;
}
public static void reportFromClient() {
clientReporter.run();
}
public static void handle(ServerPlayer player, PortalGunMoonAimPayload payload) {
REPORTS.put(player.getUUID(), new Report(payload.tracked(), payload.aiming(), player.serverLevel().getGameTime()));
}
public static Boolean clientAim(ServerPlayer player) {
Report report = REPORTS.get(player.getUUID());
if (report == null || !report.tracked()) {
return null;
}
long age = player.serverLevel().getGameTime() - report.gameTime();
return age < 0L || age > MAX_REPORT_AGE_TICKS ? null : report.aiming();
}
public static void forget(UUID playerId) {
REPORTS.remove(playerId);
}
public static void clear() {
REPORTS.clear();
}
private record Report(boolean tracked, boolean aiming, long gameTime) {
}
}
@@ -30,6 +30,7 @@ import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
@@ -99,7 +100,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private static final double MAX_CLIENT_TRANSIT_MOVEMENT_SQR = 100.0D;
private static final double PROJECTILE_PLANE_TOLERANCE = 0.1D;
private static final double MOON_RANGE = 14.0D;
private static final double MOON_SPREAD = 0.9D;
private static final double MOON_RAY_MIN_OUTWARD = -0.2D;
private static final double MOON_PULL_STRENGTH = 0.15D;
private static final double MOON_MAX_SPEED = 1.8D;
private static final double MOON_MOUTH_DEPTH = 0.9D;
@@ -112,6 +113,8 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private static final int MOON_VACUUM_INTERVAL = 10;
private static final float MOON_VACUUM_DAMAGE = 2.0F;
private static final int MOON_WIND_INTERVAL = 40;
private static final int MOON_BLOCK_MAX_FLIGHT_TICKS = 80;
private static final double MOON_BLOCK_SWALLOW_RADIUS = 2.0D;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private final Map<UUID, Integer> teleportCooldowns = new HashMap<>();
private UUID ownerId;
@@ -122,6 +125,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private int pairTime;
private int moonTicks;
private int moonSuckedBlocks;
private final Map<UUID, Integer> moonLiftedBlocks = new HashMap<>();
private BlockPos supportOrigin = BlockPos.ZERO;
private BlockPos masterPos = BlockPos.ZERO;
private BlockPos basePos = BlockPos.ZERO;
@@ -663,26 +667,23 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
PortalGunWorldPortalShape shape = this.getWorldPortalShape();
Player owner = this.getOwnerId() == null ? null : level.getPlayerByUUID(this.getOwnerId());
this.pullMoonEntities(level, shape, owner);
this.tickMoonLiftedBlocks(level, shape);
if (AntarchySettings.portalGunMoonPullsBlocks() && this.moonTicks % MOON_BLOCK_INTERVAL == 0) {
this.pullMoonBlocks(level, shape, owner);
}
}
private static double moonLateralLimit(PortalGunWorldPortalShape shape, double depth) {
return Math.max(shape.halfWidth(), shape.halfHeight()) + 0.5D + Math.max(0.0D, depth) * MOON_SPREAD;
}
private void pullMoonEntities(ServerLevel level, PortalGunWorldPortalShape shape, Player owner) {
Vec3 center = shape.center();
AABB area = new AABB(center, center.add(shape.normal().scale(MOON_RANGE))).inflate(moonLateralLimit(shape, MOON_RANGE));
AABB area = new AABB(center, center).inflate(MOON_RANGE);
Vec3 target = center.add(shape.normal().scale(0.3D));
boolean vacuumTick = this.moonTicks % MOON_VACUUM_INTERVAL == 0;
for (Entity entity : level.getEntities(this, area, this::canMoonAffect)) {
Vec3 position = entity.getBoundingBox().getCenter();
PortalGunWorldPortalShape.PortalLocalCoords local = shape.localCoords(position);
double depth = local.depth();
double lateral = Math.sqrt(local.horizontal() * local.horizontal() + local.vertical() * local.vertical());
if (depth < -0.5D || depth > MOON_RANGE || lateral > moonLateralLimit(shape, depth)) {
double distance = position.distanceTo(center);
if (depth < -0.5D || distance > MOON_RANGE) {
continue;
}
boolean atMouth = depth <= MOON_MOUTH_DEPTH + entity.getBbWidth() * 0.5D
@@ -694,7 +695,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
}
continue;
}
double falloff = 1.0D - depth / MOON_RANGE;
double falloff = 1.0D - distance / MOON_RANGE;
double strength = MOON_PULL_STRENGTH * (0.35D + falloff * falloff * 1.6D);
Vec3 worldMotion = PortalGunTransitMath.entityVelocityToWorld(entity, entity.getDeltaMovement())
.scale(0.92D)
@@ -752,20 +753,20 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
int cap = AntarchySettings.portalGunMoonBlockCap();
int pulled = 0;
for (int ray = 0; ray < MOON_BLOCK_RAYS && pulled < MOON_BLOCKS_PER_PULL && this.moonSuckedBlocks < cap; ray++) {
double limit = moonLateralLimit(shape, MOON_RANGE);
double horizontal = (this.random.nextDouble() * 2.0D - 1.0D) * limit;
double vertical = (this.random.nextDouble() * 2.0D - 1.0D) * limit;
if (horizontal * horizontal + vertical * vertical > limit * limit) {
Vec3 direction = new Vec3(this.random.nextGaussian(), this.random.nextGaussian(), this.random.nextGaussian());
if (direction.lengthSqr() < 1.0E-6D) {
continue;
}
direction = direction.normalize();
double outward = direction.dot(shape.normal());
if (outward < MOON_RAY_MIN_OUTWARD) {
direction = direction.subtract(shape.normal().scale(outward - MOON_RAY_MIN_OUTWARD - this.random.nextDouble() * 0.3D)).normalize();
}
Vec3 start = shape.center()
.add(shape.right().scale((this.random.nextDouble() * 2.0D - 1.0D) * shape.halfWidth()))
.add(shape.up().scale((this.random.nextDouble() * 2.0D - 1.0D) * shape.halfHeight()))
.add(shape.normal().scale(0.5D));
Vec3 end = shape.center()
.add(shape.right().scale(horizontal))
.add(shape.up().scale(vertical))
.add(shape.normal().scale(MOON_RANGE));
.add(shape.normal().scale(0.3D));
Vec3 end = start.add(direction.scale(MOON_RANGE));
BlockPos hit = this.firstBlockAlong(level, shape, start, end);
if (hit == null) {
continue;
@@ -775,16 +776,44 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
|| owner != null && !level.mayInteract(owner, hit)) {
continue;
}
FallingBlockEntity falling = BlockSuction.lift(level, hit, state, shape.center(), MOON_BLOCK_SPEED);
FallingBlockEntity falling = BlockSuction.lift(level, hit, state, shape.center().add(shape.normal().scale(1.2D)), MOON_BLOCK_SPEED);
if (falling == null) {
continue;
}
falling.setNoGravity(true);
this.moonLiftedBlocks.put(falling.getUUID(), this.moonTicks);
pulled++;
this.moonSuckedBlocks++;
}
}
private void tickMoonLiftedBlocks(ServerLevel level, PortalGunWorldPortalShape shape) {
Iterator<Map.Entry<UUID, Integer>> iterator = this.moonLiftedBlocks.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<UUID, Integer> entry = iterator.next();
if (!(level.getEntity(entry.getKey()) instanceof FallingBlockEntity falling) || !falling.isAlive()) {
iterator.remove();
continue;
}
if (this.moonTicks - entry.getValue() > MOON_BLOCK_MAX_FLIGHT_TICKS
|| falling.getBoundingBox().getCenter().distanceTo(shape.center()) <= MOON_BLOCK_SWALLOW_RADIUS) {
falling.discard();
iterator.remove();
}
}
}
private void releaseMoonLiftedBlocks(ServerLevel level) {
for (UUID id : this.moonLiftedBlocks.keySet()) {
if (level.getEntity(id) instanceof FallingBlockEntity falling && falling.isAlive()) {
falling.setNoGravity(false);
falling.setDeltaMovement(falling.getDeltaMovement().scale(0.2D));
falling.hurtMarked = true;
}
}
this.moonLiftedBlocks.clear();
}
private BlockPos firstBlockAlong(ServerLevel level, PortalGunWorldPortalShape shape, Vec3 start, Vec3 end) {
Vec3 delta = end.subtract(start);
int steps = Math.max(1, (int) Math.ceil(delta.length() / MOON_RAY_STEP));
@@ -795,9 +824,6 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
continue;
}
previous = pos;
if (shape.localCoords(Vec3.atCenterOf(pos)).depth() < 0.5D) {
continue;
}
if (!level.isInWorldBounds(pos) || !level.getWorldBorder().isWithinBounds(pos)) {
return null;
}
@@ -805,9 +831,19 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (state == null) {
return null;
}
if (!state.isAir()) {
return pos;
if (state.isAir() || this.containsPortalSpot(pos)) {
continue;
}
PortalGunWorldPortalShape.PortalLocalCoords local = shape.localCoords(Vec3.atCenterOf(pos));
if (local.depth() < -1.0D) {
return null;
}
if (local.depth() < 0.5D
&& Math.abs(local.horizontal()) <= shape.halfWidth() + 1.0D
&& Math.abs(local.vertical()) <= shape.halfHeight() + 1.0D) {
return null;
}
return pos;
}
return null;
}
@@ -821,13 +857,16 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private void spawnMoonParticles() {
PortalGunWorldPortalShape shape = this.getWorldPortalShape();
Vec3 center = shape.center();
for (int i = 0; i < 4; i++) {
double depth = 1.0D + this.random.nextDouble() * MOON_RANGE * 0.6D;
double limit = moonLateralLimit(shape, depth);
Vec3 start = center
.add(shape.right().scale((this.random.nextDouble() * 2.0D - 1.0D) * limit))
.add(shape.up().scale((this.random.nextDouble() * 2.0D - 1.0D) * limit))
.add(shape.normal().scale(depth));
for (int i = 0; i < 6; i++) {
Vec3 direction = new Vec3(this.random.nextGaussian(), this.random.nextGaussian(), this.random.nextGaussian());
if (direction.lengthSqr() < 1.0E-6D) {
continue;
}
direction = direction.normalize();
if (direction.dot(shape.normal()) < 0.1D) {
direction = direction.add(shape.normal().scale(0.1D - direction.dot(shape.normal()))).normalize();
}
Vec3 start = center.add(direction.scale(1.5D + this.random.nextDouble() * MOON_RANGE * 0.6D));
Vec3 velocity = center.subtract(start).normalize().scale(0.35D + this.random.nextDouble() * 0.3D);
this.level().addParticle(ParticleTypes.CLOUD, start.x, start.y, start.z, velocity.x, velocity.y, velocity.z);
}
@@ -1072,6 +1111,11 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
tag.putBoolean("Moon", true);
tag.putInt("MoonTicks", this.moonTicks);
tag.putInt("MoonSuckedBlocks", this.moonSuckedBlocks);
ListTag lifted = new ListTag();
for (UUID id : this.moonLiftedBlocks.keySet()) {
lifted.add(NbtUtils.createUUID(id));
}
tag.put("MoonLiftedBlocks", lifted);
}
tag.putInt("Side", this.getPortalSide().ordinal());
tag.putInt("Facing", this.getFacingDirection().get3DDataValue());
@@ -1125,6 +1169,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
this.entityData.set(MOON, tag.getBoolean("Moon"));
this.moonTicks = tag.getInt("MoonTicks");
this.moonSuckedBlocks = tag.getInt("MoonSuckedBlocks");
this.moonLiftedBlocks.clear();
for (Tag lifted : tag.getList("MoonLiftedBlocks", Tag.TAG_INT_ARRAY)) {
this.moonLiftedBlocks.put(NbtUtils.loadUUID(lifted), this.moonTicks);
}
this.entityData.set(SIDE, tag.getInt("Side"));
this.entityData.set(FACING, tag.getInt("Facing"));
this.entityData.set(UP_AXIS, tag.getInt("UpAxis"));
@@ -1153,6 +1201,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
@Override
public void remove(Entity.RemovalReason reason) {
if (this.isMoonPortal() && this.level() instanceof ServerLevel moonLevel) {
this.releaseMoonLiftedBlocks(moonLevel);
}
Level level = this.level();
UUID ownerId = this.ownerId;
boolean destroyPortal = reason.shouldDestroy();
@@ -5,11 +5,14 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
@@ -49,6 +52,7 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
private static final TagKey<Block> GLASS_PANES = TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("c", "glass_panes"));
private static final int MAX_TICKETED_PROJECTILES = 20;
private static final int PROJECTILE_TICKET_LEVEL = 31;
private static final int MAX_CHUNK_WAIT_TICKS = 200;
private static final TicketType<UUID> PROJECTILE_TICKET = TicketType.create("antarchy_portal_projectile", Comparator.comparing(UUID::toString));
private static final LinkedHashMap<UUID, ProjectileTicket> PROJECTILE_TICKETS = new LinkedHashMap<>();
private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
@@ -74,6 +78,7 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
private UUID gunId;
private boolean spawnSynced;
private boolean ticketRegistered;
private int chunkWaitTicks;
public int portalWidth = 1;
public int portalHeight = 2;
@@ -207,8 +212,8 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
this.setDeltaMovement(this.getSyncedVelocity());
this.spawnSynced = true;
}
if (!this.level().isClientSide && !this.isRemoved()) {
this.updateProjectileTicket();
if (this.level() instanceof ServerLevel serverLevel && !this.isRemoved() && !this.prepareServerStep(serverLevel)) {
return;
}
this.baseTick();
if (this.isRemoved()) {
@@ -260,9 +265,6 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
if (this.isRemoved()) {
return;
}
if (!this.level().isClientSide && !this.isRemoved()) {
this.updateProjectileTicket();
}
if (this.isInWater()) {
for (int i = 0; i < 4; i++) {
float backtrack = 0.25F;
@@ -293,34 +295,78 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
super.remove(reason);
}
private void updateProjectileTicket() {
if (!(this.level() instanceof ServerLevel serverLevel)) {
return;
private boolean prepareServerStep(ServerLevel level) {
Vec3 position = this.position();
Vec3 motion = this.getDeltaMovement();
if (position.y > level.getMaxBuildHeight() + 2.0D && motion.y >= 0.0D
|| position.y < level.getMinBuildHeight() - 2.0D && motion.y <= 0.0D) {
this.discard();
return false;
}
if (!AntarchySettings.portalGunCanPortalProjectilesChunkload()) {
List<ChunkPos> path = chunksAlong(position, position.add(motion));
boolean chunkload = AntarchySettings.portalGunCanPortalProjectilesChunkload();
if (chunkload) {
this.updateProjectileTicket(level, path);
} else {
this.releaseProjectileTicket();
return;
}
ChunkPos chunkPos = this.chunkPosition();
for (ChunkPos chunk : path) {
if (level.getChunkSource().getChunkNow(chunk.x, chunk.z) == null) {
if (!chunkload || ++this.chunkWaitTicks > MAX_CHUNK_WAIT_TICKS) {
this.discard();
}
return false;
}
}
this.chunkWaitTicks = 0;
return true;
}
private static List<ChunkPos> chunksAlong(Vec3 start, Vec3 end) {
int minX = SectionPos.blockToSectionCoord(Math.min(start.x, end.x));
int maxX = SectionPos.blockToSectionCoord(Math.max(start.x, end.x));
int minZ = SectionPos.blockToSectionCoord(Math.min(start.z, end.z));
int maxZ = SectionPos.blockToSectionCoord(Math.max(start.z, end.z));
if (maxX - minX > 1 || maxZ - minZ > 1) {
return List.of(new ChunkPos(minX, minZ), new ChunkPos(maxX, maxZ));
}
List<ChunkPos> chunks = new ArrayList<>(4);
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
chunks.add(new ChunkPos(x, z));
}
}
return chunks;
}
private void updateProjectileTicket(ServerLevel serverLevel, List<ChunkPos> chunks) {
UUID ticketId = this.getUUID();
ProjectileTicket current = PROJECTILE_TICKETS.get(ticketId);
if (current != null && current.level == serverLevel && current.chunkPos.equals(chunkPos)) {
if (current != null && current.level == serverLevel && current.chunks.equals(chunks)) {
return;
}
if (current != null) {
current.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, current.chunkPos, PROJECTILE_TICKET_LEVEL, ticketId);
removeTicket(ticketId, current);
PROJECTILE_TICKETS.remove(ticketId);
}
while (PROJECTILE_TICKETS.size() >= MAX_TICKETED_PROJECTILES) {
Map.Entry<UUID, ProjectileTicket> oldest = PROJECTILE_TICKETS.entrySet().iterator().next();
oldest.getValue().level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, oldest.getValue().chunkPos, PROJECTILE_TICKET_LEVEL, oldest.getKey());
removeTicket(oldest.getKey(), oldest.getValue());
PROJECTILE_TICKETS.remove(oldest.getKey());
}
serverLevel.getChunkSource().addRegionTicket(PROJECTILE_TICKET, chunkPos, PROJECTILE_TICKET_LEVEL, ticketId);
PROJECTILE_TICKETS.put(ticketId, new ProjectileTicket(serverLevel, chunkPos));
for (ChunkPos chunk : chunks) {
serverLevel.getChunkSource().addRegionTicket(PROJECTILE_TICKET, chunk, PROJECTILE_TICKET_LEVEL, ticketId);
}
PROJECTILE_TICKETS.put(ticketId, new ProjectileTicket(serverLevel, List.copyOf(chunks)));
this.ticketRegistered = true;
}
private static void removeTicket(UUID ticketId, ProjectileTicket ticket) {
for (ChunkPos chunk : ticket.chunks()) {
ticket.level().getChunkSource().removeRegionTicket(PROJECTILE_TICKET, chunk, PROJECTILE_TICKET_LEVEL, ticketId);
}
}
private void releaseProjectileTicket() {
if (!this.ticketRegistered) {
return;
@@ -328,20 +374,19 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
UUID ticketId = this.getUUID();
ProjectileTicket ticket = PROJECTILE_TICKETS.remove(ticketId);
if (ticket != null) {
ticket.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, ticket.chunkPos, PROJECTILE_TICKET_LEVEL, ticketId);
removeTicket(ticketId, ticket);
}
this.ticketRegistered = false;
}
public static void releaseAllProjectileTickets() {
for (Map.Entry<UUID, ProjectileTicket> entry : PROJECTILE_TICKETS.entrySet()) {
ProjectileTicket ticket = entry.getValue();
ticket.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, ticket.chunkPos, PROJECTILE_TICKET_LEVEL, entry.getKey());
removeTicket(entry.getKey(), entry.getValue());
}
PROJECTILE_TICKETS.clear();
}
private record ProjectileTicket(ServerLevel level, ChunkPos chunkPos) {
private record ProjectileTicket(ServerLevel level, List<ChunkPos> chunks) {
}
@Override
@@ -0,0 +1,29 @@
package com.craisinlord.antarchy.mixins.compat.stellarview;
import com.craisinlord.antarchy.content.client.MoonSkyTracker;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalSceneRenderer;
import com.mojang.blaze3d.vertex.Tesselator;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.povstalec.stellarview.client.resourcepack.ViewCenter;
import net.povstalec.stellarview.common.util.SphericalCoords;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Pseudo
@Mixin(targets = "net.povstalec.stellarview.client.render.space_objects.distinct.LunaRenderer", remap = false)
public abstract class StellarViewLunaDirectionMixin {
@Inject(method = "renderTextureLayers", at = @At("HEAD"), remap = false)
private void antarchy$recordMoonDirection(ViewCenter viewCenter, ClientLevel level, Camera camera, Tesselator tesselator,
Matrix4f lastMatrix, SphericalCoords sphericalCoords, long ticks, double distance,
float partialTicks, CallbackInfo ci) {
if (PortalGunPortalSceneRenderer.isRenderingScene() || sphericalCoords == null || camera == null || lastMatrix == null) {
return;
}
MoonSkyTracker.recordSphericalMoon(level, camera, lastMatrix, sphericalCoords.theta, sphericalCoords.phi);
}
}
@@ -147,6 +147,7 @@
],
"client": [
"client.CameraBasisAccessor",
"compat.stellarview.StellarViewLunaDirectionMixin",
"client.LevelRendererPortalViewAreaAccessor",
"client.LevelRendererPortalViewAreaMixin",
"client.LevelRendererPortalLifecycleMixin",
@@ -74,7 +74,7 @@
"humidity": [0.84, 1.0],
"continentalness": [0.25, 0.72],
"erosion": [-0.5, 0.55],
"weirdness": [-0.55, 0.62],
"weirdness": [-0.35, 0.62],
"depth": [0.0, 1.0],
"offset": 0.0
}
@@ -94,11 +94,11 @@
{
"biome": "antarchy:glimmering_pools",
"parameters": {
"temperature": [0.58, 0.76],
"humidity": [0.88, 1.0],
"temperature": [0.52, 0.78],
"humidity": [0.84, 1.0],
"continentalness": [0.3, 0.65],
"erosion": [-0.05, 0.45],
"weirdness": [-0.7, -0.25],
"erosion": [-0.3, 0.45],
"weirdness": [-0.8, -0.35],
"depth": [0.0, 0.25],
"offset": 0.0
}
@@ -31,10 +31,6 @@
"antarchy:mantis": {
"energy_budget": 0.1,
"charge": 15.0
},
"antarchy:glimmer": {
"energy_budget": 0.1,
"charge": 9
}
},
"carvers": {
@@ -37,10 +37,6 @@
"antarchy:basilisk": {
"energy_budget": 0.1,
"charge": 14.0
},
"antarchy:glimmer": {
"energy_budget": 0.1,
"charge": 9
}
},
"carvers": {
@@ -37,10 +37,6 @@
"antarchy:basilisk": {
"energy_budget": 0.1,
"charge": 14.0
},
"antarchy:glimmer": {
"energy_budget": 0.1,
"charge": 9
}
},
"carvers": {
@@ -24,10 +24,6 @@
}
},
"spawn_costs": {
"antarchy:glimmer": {
"energy_budget": 0.1,
"charge": 9
}
},
"carvers": {
"air": [],
@@ -34,10 +34,6 @@
}
},
"spawn_costs": {
"antarchy:glimmer": {
"energy_budget": 0.1,
"charge": 9
}
},
"carvers": {
"air": [],
@@ -31,10 +31,6 @@
"antarchy:mantis": {
"energy_budget": 0.1,
"charge": 15.0
},
"antarchy:glimmer": {
"energy_budget": 0.1,
"charge": 9
}
},
"carvers": {