From cc4901f242560bd889e6489f0f1d5fc6699476b4 Mon Sep 17 00:00:00 2001 From: ishland Date: Sat, 4 Feb 2023 17:12:07 +0800 Subject: [PATCH] Fixes Valkyrien Skies 2 incompatibility with `use_optimized_entity_tracking` --- build.gradle | 3 ++ src/main/java/com/ishland/vmp/VMPMod.java | 6 +++ .../playerwatching/NearbyEntityTracking.java | 48 ++++++++++++++++--- .../compat/EntityPositionTransformer.java | 27 +++++++++++ ...alkyrienSkies2ShipPositionTransformer.java | 47 ++++++++++++++++++ 5 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/ishland/vmp/common/playerwatching/compat/EntityPositionTransformer.java create mode 100644 src/main/java/com/ishland/vmp/common/playerwatching/compat/ValkyrienSkies2ShipPositionTransformer.java diff --git a/build.gradle b/build.gradle index a592556..9c18241 100644 --- a/build.gradle +++ b/build.gradle @@ -55,6 +55,9 @@ dependencies { shadowInclude implementation("com.ibm.async:asyncutil:${async_util_version}") + // https://mvnrepository.com/artifact/org.joml/joml-primitives + compileOnly 'org.joml:joml-primitives:1.10.0' + // Fabric API. This is technically optional, but you probably want it anyway. // modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" diff --git a/src/main/java/com/ishland/vmp/VMPMod.java b/src/main/java/com/ishland/vmp/VMPMod.java index 54c03c5..859e8d5 100644 --- a/src/main/java/com/ishland/vmp/VMPMod.java +++ b/src/main/java/com/ishland/vmp/VMPMod.java @@ -1,6 +1,8 @@ package com.ishland.vmp; import com.ishland.raknetify.fabric.common.connection.RakNetMultiChannel; +import com.ishland.vmp.common.config.Config; +import com.ishland.vmp.common.playerwatching.NearbyEntityTracking; import com.ishland.vmp.mixins.access.INetworkState; import com.ishland.vmp.mixins.access.INetworkStatePacketHandler; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -21,5 +23,9 @@ public void onInitialize() { RakNetMultiChannel.getPacketChannelOverride(type.getKey()); } } + + if (Config.USE_OPTIMIZED_ENTITY_TRACKING) { + NearbyEntityTracking.init(); + } } } diff --git a/src/main/java/com/ishland/vmp/common/playerwatching/NearbyEntityTracking.java b/src/main/java/com/ishland/vmp/common/playerwatching/NearbyEntityTracking.java index f3e48df..ccd1ede 100644 --- a/src/main/java/com/ishland/vmp/common/playerwatching/NearbyEntityTracking.java +++ b/src/main/java/com/ishland/vmp/common/playerwatching/NearbyEntityTracking.java @@ -1,6 +1,7 @@ package com.ishland.vmp.common.playerwatching; import com.ishland.vmp.common.maps.AreaMap; +import com.ishland.vmp.common.playerwatching.compat.EntityPositionTransformer; import com.ishland.vmp.common.util.SimpleObjectPool; import com.ishland.vmp.mixins.access.IThreadedAnvilChunkStorageEntityTracker; import it.unimi.dsi.fastutil.longs.Long2ObjectFunction; @@ -13,15 +14,42 @@ import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.entity.Entity; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ThreadedAnvilChunkStorage; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.util.math.Vec3d; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; import java.util.Set; public class NearbyEntityTracking { + private static final EntityPositionTransformer[] transformers; + + static { + List list = new ArrayList<>(); + if (FabricLoader.getInstance().isModLoaded("valkyrienskies")) { + System.out.println("ValkyrienSkies detected, applying compatibility patch"); + try { + list.add((EntityPositionTransformer) + Class.forName("com.ishland.vmp.common.playerwatching.compat.ValkyrienSkies2ShipPositionTransformer") + .getDeclaredConstructor().newInstance()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + transformers = list.toArray(EntityPositionTransformer[]::new); + } + + public static void init() { + // intentionally empty + } + private final SimpleObjectPool> pooledHashSets = new SimpleObjectPool<>(unused -> new ReferenceLinkedOpenHashSet<>(), ReferenceLinkedOpenHashSet::clear, @@ -44,14 +72,14 @@ public class NearbyEntityTracking { private final Reference2LongOpenHashMap tracker2ChunkPos = new Reference2LongOpenHashMap<>(); public void addEntityTracker(ThreadedAnvilChunkStorage.EntityTracker tracker) { - final ChunkSectionPos trackedSection = ((IThreadedAnvilChunkStorageEntityTracker) tracker).getTrackedSection(); + final ChunkPos pos = getEntityChunkPos(((IThreadedAnvilChunkStorageEntityTracker) tracker).getEntity()); this.areaMap.add( tracker, - trackedSection.getX(), - trackedSection.getZ(), + pos.x, + pos.z, getChunkViewDistance(tracker) ); - this.tracker2ChunkPos.put(tracker, ((IThreadedAnvilChunkStorageEntityTracker) tracker).getEntity().getChunkPos().toLong()); + this.tracker2ChunkPos.put(tracker, pos.toLong()); } public void removeEntityTracker(ThreadedAnvilChunkStorage.EntityTracker tracker) { @@ -82,9 +110,17 @@ protected void rehash(int newN) { } }; + private static ChunkPos getEntityChunkPos(Entity entity) { + Vec3d pos = entity.getPos(); + for (EntityPositionTransformer transformer : transformers) { + pos = transformer.transform(entity, pos); + } + return new ChunkPos(ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)); + } + public void tick() { for (Reference2LongMap.Entry entry : this.tracker2ChunkPos.reference2LongEntrySet()) { - final ChunkPos pos = ((IThreadedAnvilChunkStorageEntityTracker) entry.getKey()).getEntity().getChunkPos(); + final ChunkPos pos = getEntityChunkPos(((IThreadedAnvilChunkStorageEntityTracker) entry.getKey()).getEntity()); if (pos.toLong() != entry.getLongValue()) { this.areaMap.update(entry.getKey(), pos.x, pos.z, getChunkViewDistance(entry.getKey())); entry.setValue(pos.toLong()); @@ -94,7 +130,7 @@ public void tick() { trackerTickList.clear(); for (var entry : this.playerTrackers.entrySet()) { - final Set currentTrackers = this.areaMap.getObjectsInRange(entry.getKey().getChunkPos().toLong()); + final Set currentTrackers = this.areaMap.getObjectsInRange(getEntityChunkPos(entry.getKey()).toLong()); boolean isPlayerPositionUpdated = ((ServerPlayerEntityExtension) entry.getKey()).vmpTracking$isPositionUpdated(); ((ServerPlayerEntityExtension) entry.getKey()).vmpTracking$updatePosition(); diff --git a/src/main/java/com/ishland/vmp/common/playerwatching/compat/EntityPositionTransformer.java b/src/main/java/com/ishland/vmp/common/playerwatching/compat/EntityPositionTransformer.java new file mode 100644 index 0000000..c655c62 --- /dev/null +++ b/src/main/java/com/ishland/vmp/common/playerwatching/compat/EntityPositionTransformer.java @@ -0,0 +1,27 @@ +package com.ishland.vmp.common.playerwatching.compat; + +import net.minecraft.entity.Entity; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.Vec3d; + +public abstract class EntityPositionTransformer { + + protected abstract Vec3d transform0(Entity entity, Vec3d pos); + + public final Vec3d transform(Entity entity, Vec3d pos) { + final Vec3d pos1; + try { + pos1 = transform0(entity, pos); + } catch (Throwable t) { + System.err.println("EntityPositionTransformer %s threw an exception for %s at %s".formatted(getClass().getName(), entity, pos)); + t.printStackTrace(); + return pos; + } + if (pos1 == null) { + System.err.println("EntityPositionTransformer %s returned null for %s at %s".formatted(getClass().getName(), entity, pos)); + return pos; + } + return pos1; + } + +} diff --git a/src/main/java/com/ishland/vmp/common/playerwatching/compat/ValkyrienSkies2ShipPositionTransformer.java b/src/main/java/com/ishland/vmp/common/playerwatching/compat/ValkyrienSkies2ShipPositionTransformer.java new file mode 100644 index 0000000..2ce44dc --- /dev/null +++ b/src/main/java/com/ishland/vmp/common/playerwatching/compat/ValkyrienSkies2ShipPositionTransformer.java @@ -0,0 +1,47 @@ +package com.ishland.vmp.common.playerwatching.compat; + +import net.minecraft.entity.Entity; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import org.joml.Matrix4dc; +import org.joml.Vector3d; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class ValkyrienSkies2ShipPositionTransformer extends EntityPositionTransformer { + + private static final MethodHandle methodVSGameUtilsKt$getShipManagingPos; + private static final MethodHandle methodShip$getShipToWorld; + + static { + try { + final Class clazzVSGameUtilsKt = Class.forName("org.valkyrienskies.mod.common.VSGameUtilsKt"); + final Class clazzShip = Class.forName("org.valkyrienskies.core.api.ships.Ship"); + methodVSGameUtilsKt$getShipManagingPos = MethodHandles.lookup().findStatic(clazzVSGameUtilsKt, + "getShipManagingPos", MethodType.methodType(clazzShip, World.class, int.class, int.class)); + methodShip$getShipToWorld = MethodHandles.lookup().findVirtual(clazzShip, + "getShipToWorld", MethodType.methodType(Matrix4dc.class)); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + @Override + public Vec3d transform0(Entity entity, Vec3d pos) { + try { + final Object ship = methodVSGameUtilsKt$getShipManagingPos.invoke(entity.world, ChunkSectionPos.getSectionCoord(pos.x), ChunkSectionPos.getSectionCoord(pos.z)); + if (ship != null) { + final Matrix4dc shipToWorld = (Matrix4dc) methodShip$getShipToWorld.invoke(ship); + final Vector3d transformedPosition = shipToWorld.transformPosition(new Vector3d(pos.x, pos.y, pos.z)); + return new Vec3d(transformedPosition.x, transformedPosition.y, transformedPosition.z); + } + } catch (Throwable t) { + throw new RuntimeException(t); + } + return pos; + } +}