From cf1ab0c6f21d8d6e145a08cc86c10dd2bf4058b7 Mon Sep 17 00:00:00 2001
From: ThePlasticPotato <77798144+ThePlasticPotato@users.noreply.github.com>
Date: Mon, 27 May 2024 10:36:27 -0400
Subject: [PATCH] Unloaded chunk flighting wip

---
 .../mod/mixin/entity/MixinEntity.java         | 14 +++
 ...ersistentEntitySectionManagerCallback.java | 85 +++++++++++++++++++
 ...ersistentEntitySectionManagerAccessor.java | 19 +++++
 .../valkyrienskies-common.mixins.json         |  4 +-
 4 files changed, 121 insertions(+), 1 deletion(-)
 create mode 100644 common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/MixinPersistentEntitySectionManagerCallback.java
 create mode 100644 common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/PersistentEntitySectionManagerAccessor.java

diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/entity/MixinEntity.java b/common/src/main/java/org/valkyrienskies/mod/mixin/entity/MixinEntity.java
index 94b1c5eb3..c23ef380c 100644
--- a/common/src/main/java/org/valkyrienskies/mod/mixin/entity/MixinEntity.java
+++ b/common/src/main/java/org/valkyrienskies/mod/mixin/entity/MixinEntity.java
@@ -6,6 +6,7 @@
 import net.minecraft.CrashReportCategory;
 import net.minecraft.ReportedException;
 import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerLevel;
 import net.minecraft.util.Mth;
 import net.minecraft.world.entity.Entity;
 import net.minecraft.world.entity.EntityType;
@@ -154,6 +155,16 @@ private void preCalculateViewVector(final float xRot, final float yRot, final Ca
         cir.setReturnValue(newViewVector);
     }
 
+    @Inject(method = "touchingUnloadedChunk", at = @At("HEAD"), cancellable = true, remap = false)
+    private void isTouchingUnloadedChunkOrShip(CallbackInfoReturnable<Boolean> cir) {
+        if (!level.isClientSide) {
+            ServerLevel slevel = (ServerLevel) level;
+            if (VSGameUtilsKt.getShipsIntersecting(slevel, getBoundingBox()).iterator().hasNext()) {
+                cir.setReturnValue(true);
+            }
+        }
+    }
+
     // region shadow functions and fields
     @Shadow
     public Level level;
@@ -184,6 +195,9 @@ private void preCalculateViewVector(final float xRot, final float yRot, final Ca
     @Shadow
     public abstract EntityType<?> getType();
 
+    @Shadow
+    public abstract Vec3 getPosition(float f);
+
     @Override
     @NotNull
     public EntityDraggingInformation getDraggingInformation() {
diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/MixinPersistentEntitySectionManagerCallback.java b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/MixinPersistentEntitySectionManagerCallback.java
new file mode 100644
index 000000000..7b4ab9453
--- /dev/null
+++ b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/MixinPersistentEntitySectionManagerCallback.java
@@ -0,0 +1,85 @@
+package org.valkyrienskies.mod.mixin.feature.unloaded_chunk_flight;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.SectionPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.entity.EntityAccess;
+import net.minecraft.world.level.entity.EntitySection;
+import net.minecraft.world.level.entity.PersistentEntitySectionManager;
+import net.minecraft.world.level.entity.PersistentEntitySectionManager.Callback;
+import net.minecraft.world.level.entity.Visibility;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.valkyrienskies.core.api.ships.LoadedServerShip;
+import org.valkyrienskies.mod.common.VSGameUtilsKt;
+import org.valkyrienskies.mod.common.util.EntityDraggingInformation;
+import org.valkyrienskies.mod.common.util.IEntityDraggingInformationProvider;
+import org.valkyrienskies.mod.common.util.VectorConversionsMCKt;
+
+@Mixin(Callback.class)
+public class MixinPersistentEntitySectionManagerCallback {
+
+    @Shadow
+    @Final
+    private EntityAccess entity;
+
+    @Shadow
+    private long currentSectionKey;
+
+    @Shadow
+    private EntitySection<Entity> currentSection;
+
+    @Shadow
+    @Final
+    PersistentEntitySectionManager field_27271;
+
+    @Shadow
+    private void updateStatus(Visibility visibility, Visibility visibility2) {
+        throw new IllegalStateException("Mixin failed to apply");
+    }
+
+    @Inject(method = "onMove", at = @At("HEAD"), cancellable = true, remap = false)
+    private void onMoveInclShips(CallbackInfo ci) {
+        // Do nothing
+        Entity realEntity = (Entity) this.entity;
+        BlockPos blockPos = this.entity.blockPosition();
+        if (realEntity.level != null && !realEntity.level.isClientSide) {
+            ServerLevel slevel = (ServerLevel) realEntity.level;
+
+            EntityDraggingInformation draggingInformation = ((IEntityDraggingInformationProvider) realEntity).getDraggingInformation();
+
+            LoadedServerShip loadedShip = null;
+
+            if (draggingInformation.isEntityBeingDraggedByAShip() && draggingInformation.getLastShipStoodOn() != null) {
+                loadedShip = VSGameUtilsKt.getShipObjectWorld(slevel).getLoadedShips().getById(draggingInformation.getLastShipStoodOn());
+            } else if (VSGameUtilsKt.getShipMountedTo(realEntity) != null) {
+                loadedShip = VSGameUtilsKt.getShipObjectWorld(slevel).getLoadedShips().getById(VSGameUtilsKt.getShipMountedTo(realEntity).getId());
+            } else if (VSGameUtilsKt.getShipsIntersecting(slevel, realEntity.getBoundingBox()).iterator().hasNext()) {
+                loadedShip = VSGameUtilsKt.getShipObjectWorld(slevel).getLoadedShips().getById(VSGameUtilsKt.getShipsIntersecting(slevel, realEntity.getBoundingBox()).iterator().next().getId());
+            }
+
+            if (loadedShip != null) {
+                long l = SectionPos.asLong(new BlockPos(VectorConversionsMCKt.toMinecraft(loadedShip.getTransform().getWorldToShip().transformPosition(VectorConversionsMCKt.toJOMLD(blockPos)))));
+                if (l != this.currentSectionKey) {
+                    Visibility visibility = this.currentSection.getStatus();
+                    if (!this.currentSection.remove(realEntity)) {
+                        //LOGGER.warn("Entity {} wasn't found in section {} (moving to {})", this.entity, SectionPos.of(this.currentSectionKey), l);
+                    }
+                    ((PersistentEntitySectionManagerAccessor) this.field_27271).invokeRemoveSectionIfEmpty(this.currentSectionKey, this.currentSection);
+                    EntitySection entitySection = ((PersistentEntitySectionManagerAccessor) this.field_27271).getSectionStorage().getOrCreateSection(l);
+                    entitySection.add(realEntity);
+                    this.currentSection = entitySection;
+                    this.currentSectionKey = l;
+                    this.updateStatus(visibility, entitySection.getStatus());
+                }
+                ci.cancel();
+                return;
+            }
+        }
+    }
+}
diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/PersistentEntitySectionManagerAccessor.java b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/PersistentEntitySectionManagerAccessor.java
new file mode 100644
index 000000000..67cdc8396
--- /dev/null
+++ b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/unloaded_chunk_flight/PersistentEntitySectionManagerAccessor.java
@@ -0,0 +1,19 @@
+package org.valkyrienskies.mod.mixin.feature.unloaded_chunk_flight;
+
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.entity.EntitySection;
+import net.minecraft.world.level.entity.EntitySectionStorage;
+import net.minecraft.world.level.entity.PersistentEntitySectionManager;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+@Mixin(PersistentEntitySectionManager.class)
+public interface PersistentEntitySectionManagerAccessor {
+
+    @Accessor
+    EntitySectionStorage<Entity> getSectionStorage();
+
+    @Invoker
+    void invokeRemoveSectionIfEmpty(long l, EntitySection<Entity> entitySection);
+}
diff --git a/common/src/main/resources/valkyrienskies-common.mixins.json b/common/src/main/resources/valkyrienskies-common.mixins.json
index 569f9e31a..ecd1f63be 100644
--- a/common/src/main/resources/valkyrienskies-common.mixins.json
+++ b/common/src/main/resources/valkyrienskies-common.mixins.json
@@ -49,6 +49,8 @@
     "feature.shipyard_entities.MixinTransientEntitySectionManager",
     "feature.spawn_player_on_ship.MixinServerGamePacketListenerImpl",
     "feature.tick_ship_chunks.MixinChunkMap",
+    "feature.unloaded_chunk_flight.MixinPersistentEntitySectionManagerCallback",
+    "feature.unloaded_chunk_flight.PersistentEntitySectionManagerAccessor",
     "feature.water_in_ships_entity.MixinEntity",
     "feature.world_border.MixinLevel",
     "feature.world_border.MixinWorldBorder",
@@ -168,9 +170,9 @@
     "mod_compat.create.client.MixinSoundScapes",
     "mod_compat.create.client.MixinTileEntityRenderHelper",
     "mod_compat.create.client.MixinTrainRelocator",
+    "mod_compat.create.client.MixinValueBox",
     "mod_compat.create.client.trackOutlines.MixinBigOutlines",
     "mod_compat.create.client.trackOutlines.MixinTrackBlockOutline",
-    "mod_compat.create.client.MixinValueBox",
     "mod_compat.flywheel.InstancingEngineAccessor",
     "mod_compat.flywheel.MixinBlockEntityInstanceManager",
     "mod_compat.flywheel.MixinInstanceManager",