From b17c3194a4ca2fb3154826fde7d4ffb46103289e Mon Sep 17 00:00:00 2001 From: Naz Date: Thu, 2 Jan 2025 18:11:42 +0800 Subject: [PATCH] Improve multipart entity support --- forge | 2 +- .../entity/EntityRenderDispatcherInject.java | 39 ++++ .../server/level/ChunkMapInject.java | 15 ++ .../server/level/ServerLevelInject.java | 175 +++++++++++++++++- .../world/entity/player/PlayerInject.java | 23 +++ .../forgeinjects/world/level/LevelInject.java | 30 ++- .../server/level/ServerLevelInjection.java | 8 + .../resources/kilt_forge_injects.mixins.json | 1 + 8 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 src/main/java/xyz/bluspring/kilt/injections/server/level/ServerLevelInjection.java diff --git a/forge b/forge index ea0934da..0808c204 160000 --- a/forge +++ b/forge @@ -1 +1 @@ -Subproject commit ea0934da12e1a6ba6d2100f343b75621ccbe9520 +Subproject commit 0808c20414a7d42eefad398df3b5860ac7ecb06a diff --git a/src/main/java/xyz/bluspring/kilt/forgeinjects/client/renderer/entity/EntityRenderDispatcherInject.java b/src/main/java/xyz/bluspring/kilt/forgeinjects/client/renderer/entity/EntityRenderDispatcherInject.java index 553d9a4f..ac296bb2 100644 --- a/src/main/java/xyz/bluspring/kilt/forgeinjects/client/renderer/entity/EntityRenderDispatcherInject.java +++ b/src/main/java/xyz/bluspring/kilt/forgeinjects/client/renderer/entity/EntityRenderDispatcherInject.java @@ -1,14 +1,26 @@ // TRACKED HASH: 7806cbd7ecf0842aa5db2c08ecd295f2b0b0f3ed package xyz.bluspring.kilt.forgeinjects.client.renderer.entity; +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import io.github.fabricators_of_create.porting_lib.entity.MultiPartEntity; +import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.entity.EntityRenderDispatcher; import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.player.Player; import net.minecraftforge.client.event.EntityRenderersEvent; +import net.minecraftforge.entity.PartEntity; import net.minecraftforge.fml.ModLoader; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -36,4 +48,31 @@ public Map> getSkinMap() { private void kilt$addEntityRenderLayers(ResourceManager resourceManager, CallbackInfo ci, @Local EntityRendererProvider.Context context) { ModLoader.get().postEvent(new EntityRenderersEvent.AddLayers(this.renderers, this.playerRenderers, context)); } + + @Definition(id = "entity", local = @Local(type = Entity.class, argsOnly = true)) + @Definition(id = "EnderDragon", type = EnderDragon.class) + @Expression("entity instanceof EnderDragon") + @WrapOperation(method = "renderHitbox", at = @At("MIXINEXTRAS:EXPRESSION")) + private static boolean kilt$renderHitboxIfMultipart(Object object, Operation original, @Local(argsOnly = true) PoseStack poseStack, @Local(argsOnly = true) VertexConsumer buffer, @Local(argsOnly = true) float partialTicks, @Local(argsOnly = true) Entity entity) { + if (original.call(object)) + return true; + + if (((Entity) object).isMultipartEntity() && !(object instanceof MultiPartEntity)) { + double currentX = -Mth.lerp(partialTicks, entity.xOld, entity.getX()); + double currentY = -Mth.lerp(partialTicks, entity.yOld, entity.getY()); + double currentZ = -Mth.lerp(partialTicks, entity.zOld, entity.getZ()); + + for (PartEntity partEntity : entity.getParts()) { + poseStack.pushPose(); + double partX = currentX + Mth.lerp(partialTicks, partEntity.xOld, partEntity.getX()); + double partY = currentY + Mth.lerp(partialTicks, partEntity.yOld, partEntity.getY()); + double partZ = currentZ + Mth.lerp(partialTicks, partEntity.zOld, partEntity.getZ()); + poseStack.translate(partX, partY, partZ); + LevelRenderer.renderLineBox(poseStack, buffer, partEntity.getBoundingBox().move(-partEntity.getX(), -partEntity.getY(), -partEntity.getZ()), 0.25F, 1.0F, 0.0F, 1.0F); + poseStack.popPose(); + } + } + + return false; + } } \ No newline at end of file diff --git a/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ChunkMapInject.java b/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ChunkMapInject.java index 30b22dc1..72f8f977 100644 --- a/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ChunkMapInject.java +++ b/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ChunkMapInject.java @@ -1,6 +1,10 @@ // TRACKED HASH: cebcc0747792b8bfe53d24573c9609f5c22b61d1 package xyz.bluspring.kilt.forgeinjects.server.level; +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; @@ -8,12 +12,15 @@ import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ImposterProtoChunk; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.entity.PartEntity; import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.event.level.ChunkDataEvent; import net.minecraftforge.event.level.ChunkEvent; @@ -30,6 +37,14 @@ public abstract class ChunkMapInject { @Shadow @Final private ServerLevel level; + @Definition(id = "entity", local = @Local(type = Entity.class, argsOnly = true)) + @Definition(id = "EnderDragonPart", type = EnderDragonPart.class) + @Expression("entity instanceof EnderDragonPart") + @WrapOperation(method = "addEntity", at = @At("MIXINEXTRAS:EXPRESSION")) + private boolean kilt$checkIfEntityMultipart(Object object, Operation original) { + return original.call(object) || object instanceof PartEntity; + } + @Inject(method = "updateChunkScheduling", at = @At(value = "RETURN", ordinal = 1)) private void kilt$fireTicketUpdatedEvent(long chunkPos, int newLevel, ChunkHolder holder, int oldLevel, CallbackInfoReturnable cir) { ForgeEventFactory.fireChunkTicketLevelUpdated(this.level, chunkPos, oldLevel, newLevel, holder); diff --git a/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ServerLevelInject.java b/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ServerLevelInject.java index b3c6a0ec..93c5e679 100644 --- a/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ServerLevelInject.java +++ b/src/main/java/xyz/bluspring/kilt/forgeinjects/server/level/ServerLevelInject.java @@ -1,23 +1,54 @@ // TRACKED HASH: 853e9e4639ba453c25a048d0905ec758c41a51dd package xyz.bluspring.kilt.forgeinjects.server.level; +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalFloatRef; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.RandomSequences; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.entity.EntityAccess; +import net.minecraft.world.level.entity.LevelCallback; +import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.storage.DimensionDataStorage; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.level.storage.WritableLevelData; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.extensions.IForgeLevel; import net.minecraftforge.common.util.LevelCapabilityData; +import net.minecraftforge.entity.PartEntity; import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.entity.EntityLeaveLevelEvent; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -26,21 +57,26 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import xyz.bluspring.kilt.injections.server.level.ServerLevelInjection; +import java.util.Collection; +import java.util.EnumSet; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Consumer; import java.util.function.Supplier; @Mixin(ServerLevel.class) -public abstract class ServerLevelInject extends Level { +public abstract class ServerLevelInject extends Level implements ServerLevelInjection, IForgeLevel { protected ServerLevelInject(WritableLevelData levelData, ResourceKey dimension, RegistryAccess registryAccess, Holder dimensionTypeRegistration, Supplier profiler, boolean isClientSide, boolean isDebug, long biomeZoomSeed, int maxChainedNeighborUpdates) { super(levelData, dimension, registryAccess, dimensionTypeRegistration, profiler, isClientSide, isDebug, biomeZoomSeed, maxChainedNeighborUpdates); } @Shadow public abstract DimensionDataStorage getDataStorage(); - @Unique - private LevelCapabilityData capabilityData; + @Shadow @Final private List players; + @Unique private LevelCapabilityData capabilityData; + @Unique final Int2ObjectMap> kilt$entityParts = new Int2ObjectOpenHashMap<>(); @Inject(method = "", at = @At("TAIL")) private void kilt$addInitCapabilities(MinecraftServer server, Executor dispatcher, LevelStorageSource.LevelStorageAccess levelStorageAccess, ServerLevelData serverLevelData, ResourceKey dimension, LevelStem levelStem, ChunkProgressListener progressListener, boolean isDebug, long biomeZoomSeed, List customSpawners, boolean tickTime, RandomSequences randomSequences, CallbackInfo ci) { @@ -57,9 +93,142 @@ protected ServerLevelInject(WritableLevelData levelData, ResourceKey dime return ForgeEventFactory.onSleepFinished((ServerLevel) (Object) this, l, ((ServerLevel) (Object) this).getDayTime()); } + @WrapWithCondition(method = "method_31420", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;guardEntityTick(Ljava/util/function/Consumer;Lnet/minecraft/world/entity/Entity;)V")) + private boolean kilt$checkValidTickingEntity(ServerLevel instance, Consumer consumer, Entity entity) { + return !entity.isRemoved() && !(entity instanceof PartEntity); + } + + @WrapOperation(method = "tickChunk", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/biome/Biome;shouldFreeze(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z")) + private boolean kilt$checkAreaLoaded(Biome instance, LevelReader level, BlockPos pos, Operation original) { + return this.isAreaLoaded(pos, 1) && original.call(instance, level, pos); + } + + @WrapWithCondition(method = "tickPassenger", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;rideTick()V")) + private boolean kilt$checkIfEntityCanUpdate(Entity instance) { + return instance.canUpdate(); + } + + @WrapOperation(method = "addEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;addNewEntity(Lnet/minecraft/world/level/entity/EntityAccess;)Z")) + private boolean kilt$checkEntityAddToWorld(PersistentEntitySectionManager instance, T entity, Operation original) { + //noinspection MixinExtrasOperationParameters + if (original.call(instance, entity)) { + ((Entity) entity).onAddedToWorld(); + return true; + } else { + return false; + } + } + + @Inject(method = "playSeededSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V", at = @At("HEAD"), cancellable = true) + private void kilt$modifyPositionedSound(CallbackInfo ci, @Local(argsOnly = true, ordinal = 0) double x, @Local(argsOnly = true, ordinal = 1) double y, @Local(argsOnly = true, ordinal = 2) double z, @Local(argsOnly = true) LocalRef> sound, @Local(argsOnly = true) LocalRef source, @Local(argsOnly = true, ordinal = 0) LocalFloatRef volume, @Local(argsOnly = true, ordinal = 1) LocalFloatRef pitch, @Local(argsOnly = true) long seed) { + var event = ForgeEventFactory.onPlaySoundAtPosition(this, x, y, z, sound.get(), source.get(), volume.get(), pitch.get()); + + if (event.isCanceled() || event.getSound() == null) { + ci.cancel(); + return; + } + + sound.set(event.getSound()); + source.set(event.getSource()); + volume.set(event.getNewVolume()); + pitch.set(event.getNewPitch()); + } + + @Inject(method = "playSeededSound(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V", at = @At("HEAD"), cancellable = true) + private void kilt$modifyEntitySound(CallbackInfo ci, @Local(argsOnly = true) Entity entity, @Local(argsOnly = true) LocalRef> sound, @Local(argsOnly = true) LocalRef source, @Local(argsOnly = true, ordinal = 0) LocalFloatRef volume, @Local(argsOnly = true, ordinal = 1) LocalFloatRef pitch, @Local(argsOnly = true) long seed) { + var event = ForgeEventFactory.onPlaySoundAtEntity(entity, sound.get(), source.get(), volume.get(), pitch.get()); + + if (event.isCanceled() || event.getSound() == null) { + ci.cancel(); + return; + } + + sound.set(event.getSound()); + source.set(event.getSource()); + volume.set(event.getNewVolume()); + pitch.set(event.getNewPitch()); + } + + @Inject(method = "gameEvent", at = @At("HEAD"), cancellable = true) + private void kilt$callVanillaGameEvent(GameEvent event, Vec3 position, GameEvent.Context context, CallbackInfo ci) { + if (!ForgeHooks.onVanillaGameEvent(this, event, position, context)) + ci.cancel(); + } + + @Inject(method = "updateNeighborsAt", at = @At("HEAD")) + private void kilt$notifyNeighborsEvent(BlockPos pos, Block block, CallbackInfo ci) { + ForgeEventFactory.onNeighborNotify(this, pos, this.getBlockState(pos), EnumSet.allOf(Direction.class), false) + .isCanceled(); // TODO: what's this for? why is this in Forge's patch? + } + + @Inject(method = "updateNeighborsAtExceptFromFacing", at = @At("HEAD"), cancellable = true) + private void kilt$notifyNeighborsEventWithoutFacing(BlockPos pos, Block blockType, Direction skipSide, CallbackInfo ci) { + var directions = EnumSet.allOf(Direction.class); + directions.remove(skipSide); + + if (ForgeEventFactory.onNeighborNotify(this, pos, this.getBlockState(pos), directions, false).isCanceled()) + ci.cancel(); + } + protected void initCapabilities() { this.gatherCapabilities(); capabilityData = this.getDataStorage().computeIfAbsent(e -> LevelCapabilityData.load(e, this.getCapabilities()), () -> new LevelCapabilityData(getCapabilities()), LevelCapabilityData.ID); capabilityData.setCapabilities(getCapabilities()); } + + @Override + public Int2ObjectMap> kilt$getEntityParts() { + return this.kilt$entityParts; + } + + @Override + public Collection> kilt$getPartEntities() { + return this.kilt$entityParts.values(); + } + + @Mixin(targets = "net.minecraft.server.level.ServerLevel.EntityCallbacks") + public static abstract class EntityCallbacksInject implements LevelCallback { + @Shadow @Final + ServerLevel field_26936; + + @Definition(id = "entity", local = @Local(type = Entity.class, argsOnly = true)) + @Definition(id = "EnderDragon", type = EnderDragon.class) + @Expression("entity instanceof EnderDragon") + @WrapOperation(method = "onTrackingStart(Lnet/minecraft/world/entity/Entity;)V", at = @At("MIXINEXTRAS:EXPRESSION")) + private boolean kilt$startTrackingMultipart(Object object, Operation original) { + if (original.call(object)) + return true; + + if (((Entity) object).isMultipartEntity()) { + for (PartEntity part : ((Entity) object).getParts()) { + ((ServerLevelInjection) field_26936).kilt$getEntityParts().put(part.getId(), part); + } + } + + return false; + } + + @Definition(id = "entity", local = @Local(type = Entity.class, argsOnly = true)) + @Definition(id = "EnderDragon", type = EnderDragon.class) + @Expression("entity instanceof EnderDragon") + @WrapOperation(method = "onTrackingEnd(Lnet/minecraft/world/entity/Entity;)V", at = @At("MIXINEXTRAS:EXPRESSION")) + private boolean kilt$stopTrackingMultipart(Object object, Operation original) { + if (original.call(object)) + return true; + + if (((Entity) object).isMultipartEntity()) { + for (PartEntity part : ((Entity) object).getParts()) { + ((ServerLevelInjection) field_26936).kilt$getEntityParts().remove(part.getId()); + } + } + + return false; + } + + @Inject(method = "onTrackingEnd(Lnet/minecraft/world/entity/Entity;)V", at = @At("TAIL")) + private void kilt$callEntityLevelRemoveEvent(Entity entity, CallbackInfo ci) { + entity.onRemovedFromWorld(); + MinecraftForge.EVENT_BUS.post(new EntityLeaveLevelEvent(entity, field_26936)); + } + } } \ No newline at end of file diff --git a/src/main/java/xyz/bluspring/kilt/forgeinjects/world/entity/player/PlayerInject.java b/src/main/java/xyz/bluspring/kilt/forgeinjects/world/entity/player/PlayerInject.java index 187904d9..cf28d04e 100644 --- a/src/main/java/xyz/bluspring/kilt/forgeinjects/world/entity/player/PlayerInject.java +++ b/src/main/java/xyz/bluspring/kilt/forgeinjects/world/entity/player/PlayerInject.java @@ -1,16 +1,24 @@ // TRACKED HASH: da42f0fcd542552388a5aff060abf470c54f9f10 package xyz.bluspring.kilt.forgeinjects.world.entity.player; +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; import net.minecraft.core.BlockPos; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.extensions.IForgePlayer; +import net.minecraftforge.entity.PartEntity; import net.minecraftforge.event.ForgeEventFactory; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -73,6 +81,21 @@ public float getDigSpeed(BlockState blockState, @Nullable BlockPos blockPos) { ci.cancel(); } + @Definition(id = "entity", local = @Local(type = Entity.class, argsOnly = true)) + @Definition(id = "EnderDragonPart", type = EnderDragonPart.class) + @Expression("entity instanceof EnderDragonPart") + @WrapOperation(method = "attack", at = @At("MIXINEXTRAS:EXPRESSION")) + private boolean kilt$trySetAttackedMultipartEntity(Object object, Operation original, @Local(ordinal = 1) LocalRef entity) { + if (original.call(object)) + return true; + + if (object instanceof PartEntity partEntity) { + entity.set(partEntity.getParent()); + } + + return false; + } + /*@ModifyReturnValue(method = "createAttributes", at = @At("RETURN")) private static AttributeSupplier.Builder kilt$addForgeAttributes(AttributeSupplier.Builder original) { return original diff --git a/src/main/java/xyz/bluspring/kilt/forgeinjects/world/level/LevelInject.java b/src/main/java/xyz/bluspring/kilt/forgeinjects/world/level/LevelInject.java index 4d787f60..33b71441 100644 --- a/src/main/java/xyz/bluspring/kilt/forgeinjects/world/level/LevelInject.java +++ b/src/main/java/xyz/bluspring/kilt/forgeinjects/world/level/LevelInject.java @@ -8,15 +8,19 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.phys.AABB; import net.minecraftforge.common.capabilities.CapabilityProvider; import net.minecraftforge.common.capabilities.ICapabilityProviderImpl; import net.minecraftforge.common.extensions.IForgeBlockState; import net.minecraftforge.common.extensions.IForgeLevel; import net.minecraftforge.common.util.BlockSnapshot; +import net.minecraftforge.entity.PartEntity; import net.minecraftforge.event.ForgeEventFactory; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -33,7 +37,9 @@ import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; @Mixin(value = Level.class, priority = 1111) // higher priority to mixin to Porting Lib @Extends(CapabilityProvider.class) @@ -82,7 +88,7 @@ public ArrayList getCapturedBlockSnapshots() { this.capturedBlockSnapshots.add(blockSnapshot.get()); } - // TODO: what are these used for? + // TODO: Kilt: what are these used for? BlockState old = this.getBlockState(posRef.get()); int oldLight = old.getLightEmission((Level) (Object) this, posRef.get()); int oldOpacity = old.getLightBlock((Level) (Object) this, posRef.get()); @@ -146,4 +152,26 @@ public ArrayList getCapturedBlockSnapshots() { private void kilt$loadBlockEntitiesForge(ArrayList instance, Consumer consumer) { instance.forEach(BlockEntity::onLoad); } + + @Inject(method = "getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;", at = @At("TAIL")) + private void kilt$addPartEntitiesToList(Entity entity, AABB area, Predicate predicate, CallbackInfoReturnable> cir, @Local List list) { + for (PartEntity partEntity : this.kilt$getPartEntities()) { + if (partEntity != entity && partEntity.getBoundingBox().intersects(area) && predicate.test(partEntity)) { + list.add(partEntity); + } + } + } + + @Inject(method = "getEntities(Lnet/minecraft/world/level/entity/EntityTypeTest;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;Ljava/util/List;I)V", at = @At("TAIL")) + private void kilt$addPartEntitiesToList(EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate, List output, int maxResults, CallbackInfo ci) { + for (PartEntity partEntity : this.kilt$getPartEntities()) { + var castEntity = entityTypeTest.tryCast(partEntity); + if (castEntity != null && partEntity.getBoundingBox().intersects(bounds) && predicate.test(castEntity)) { + // TODO: Kilt: doesn't this technically overflow the maxResults? + output.add(castEntity); + if (output.size() >= maxResults) + break; + } + } + } } \ No newline at end of file diff --git a/src/main/java/xyz/bluspring/kilt/injections/server/level/ServerLevelInjection.java b/src/main/java/xyz/bluspring/kilt/injections/server/level/ServerLevelInjection.java new file mode 100644 index 00000000..bff2f281 --- /dev/null +++ b/src/main/java/xyz/bluspring/kilt/injections/server/level/ServerLevelInjection.java @@ -0,0 +1,8 @@ +package xyz.bluspring.kilt.injections.server.level; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraftforge.entity.PartEntity; + +public interface ServerLevelInjection { + Int2ObjectMap> kilt$getEntityParts(); +} diff --git a/src/main/resources/kilt_forge_injects.mixins.json b/src/main/resources/kilt_forge_injects.mixins.json index d908d38d..53d4c8c6 100644 --- a/src/main/resources/kilt_forge_injects.mixins.json +++ b/src/main/resources/kilt_forge_injects.mixins.json @@ -65,6 +65,7 @@ "server.level.ChunkMapInject", "server.level.ServerChunkCacheInject", "server.level.ServerLevelInject", + "server.level.ServerLevelInject$EntityCallbacksInject", "server.level.ServerPlayerGameModeInject", "server.level.ServerPlayerInject", "server.network.MemoryServerHandshakePacketListenerImplInject",