Skip to content
This repository was archived by the owner on Dec 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
package dev.spiritstudios.specter.mixin.client.entity;


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 net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;

import dev.spiritstudios.specter.api.entity.EntityPart;
import dev.spiritstudios.specter.api.entity.PartHolder;
import dev.spiritstudios.specter.impl.entity.EntityPartWorld;

@Mixin(targets = "net/minecraft/client/world/ClientWorld$ClientEntityHandler")
public abstract class ClientWorld$ClientEntityHandlerMixin {
@Inject(method = "startTracking(Lnet/minecraft/entity/Entity;)V", at = @At("TAIL"))

@Shadow
@Final
ClientWorld field_27735;

@Inject(method = "startTracking(Lnet/minecraft/entity/Entity;)V", at = @At("RETURN"))
private void startTracking(Entity entity, CallbackInfo ci) {
if (entity instanceof PartHolder<?> partHolder) {
for (EntityPart<?> part : partHolder.parts()) {
((EntityPartWorld) entity.getWorld()).specter$parts().put(part.getId(), part);
for (EntityPart<?> part : partHolder.getEntityParts()) {
this.field_27735.specter$getParts().put(part.getId(), part);
}
}
}

@Inject(method = "stopTracking(Lnet/minecraft/entity/Entity;)V", at = @At("TAIL"))
@Inject(method = "stopTracking(Lnet/minecraft/entity/Entity;)V", at = @At("RETURN"))
private void stopTracking(Entity entity, CallbackInfo ci) {
if (entity instanceof PartHolder<?> partHolder) {
for (EntityPart<?> part : partHolder.parts()) {
((EntityPartWorld) entity.getWorld()).specter$parts().remove(part.getId(), part);
for (EntityPart<?> part : partHolder.getEntityParts()) {
this.field_27735.specter$getParts().remove(part.getId(), part);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@

@Mixin(EntityRenderer.class)
public abstract class EntityRendererMixin {

@Inject(method = "appendHitboxes", at = @At("HEAD"))
private void appendHitboxes(Entity entity, ImmutableList.Builder<EntityHitbox> builder, float tickProgress, CallbackInfo ci) {
if (entity instanceof PartHolder<?> partHolder) {
for (EntityPart<?> part : partHolder.parts()) {
for (EntityPart<?> part : partHolder.getEntityParts()) {
Box box = part.getBoundingBox();
builder.add(new EntityHitbox(
box.minX - entity.getX(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@

@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin {

@Inject(method = "appendHitboxes(Lnet/minecraft/entity/LivingEntity;Lcom/google/common/collect/ImmutableList$Builder;F)V", at = @At("HEAD"))
private void appendHitboxes(LivingEntity entity, ImmutableList.Builder<EntityHitbox> builder, float tickProgress, CallbackInfo ci) {
if (entity instanceof PartHolder<?> partHolder) {
for (EntityPart<?> part : partHolder.parts()) {
for (EntityPart<?> part : partHolder.getEntityParts()) {
Box box = part.getBoundingBox();
builder.add(new EntityHitbox(
box.minX - entity.getX(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package dev.spiritstudios.specter.api.entity;

import net.minecraft.entity.EntityPose;

import org.jetbrains.annotations.Nullable;

import net.minecraft.entity.Entity;
Expand All @@ -16,14 +18,15 @@
import net.minecraft.util.math.Vec3d;

public abstract class EntityPart<T extends Entity> extends Entity {
private final T owner;
private final EntityDimensions dims;
protected final T owner;
protected final EntityDimensions dimensions;
private Vec3d relativePos = new Vec3d(0, 0, 0);

public EntityPart(T owner, EntityDimensions dimensions) {
super(owner.getType(), owner.getWorld());
this.owner = owner;
this.dims = dimensions;
this.dimensions = dimensions;
this.calculateDimensions();
}

@Override
Expand All @@ -32,18 +35,17 @@ protected void initDataTracker(DataTracker.Builder builder) {

@Override
protected void readCustomDataFromNbt(NbtCompound nbt) {

}

@Override
protected void writeCustomDataToNbt(NbtCompound nbt) {

}

public Vec3d getRelativePos() {
return relativePos;
}

@SuppressWarnings("unused")
public void setRelativePos(Vec3d relativePos) {
this.relativePos = relativePos;
}
Expand All @@ -55,7 +57,7 @@ public boolean canHit() {

@Override
protected Box calculateDefaultBoundingBox(Vec3d pos) {
return this.dims == null ? super.calculateDefaultBoundingBox(pos) : this.dims.getBoxAt(pos);
return this.dimensions == null ? super.calculateDefaultBoundingBox(pos) : this.dimensions.getBoxAt(pos);
}

@Override
Expand Down Expand Up @@ -86,4 +88,9 @@ public Packet<ClientPlayPacketListener> createSpawnPacket(EntityTrackerEntry ent
public T getOwner() {
return owner;
}

@Override
public EntityDimensions getDimensions(EntityPose pose) {
return this.dimensions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
import net.minecraft.entity.Entity;

public interface PartHolder<T extends Entity> {
List<EntityPart<T>> parts();
List<EntityPart<T>> getEntityParts();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
import dev.spiritstudios.specter.api.entity.EntityPart;

public interface EntityPartWorld {
Int2ObjectMap<EntityPart<?>> specter$parts();
default Int2ObjectMap<EntityPart<?>> specter$getParts() {
throw new UnsupportedOperationException("Injected interface should be implemented by mixin!");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

@Mixin(PlayerEntity.class)
public abstract class PlayerEntityMixin {

@ModifyVariable(method = "attack", at = @At("STORE"), ordinal = 1)
private Entity attack(Entity value) {
if (value instanceof EntityPart<?> part) return part.getOwner();
return value;
return value instanceof EntityPart<?> part ? part.getOwner() : value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

@Mixin(ServerChunkLoadingManager.class)
public abstract class ServerChunkLoadingManagerMixin {

@WrapMethod(method = "loadEntity")
private void loadEntity(Entity entity, Operation<Void> original) {
if (entity instanceof EntityPart<?>) return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
package dev.spiritstudios.specter.mixin.entity;

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 net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerWorld;

import dev.spiritstudios.specter.api.entity.EntityPart;
import dev.spiritstudios.specter.api.entity.PartHolder;
import dev.spiritstudios.specter.impl.entity.EntityPartWorld;

@Mixin(targets = "net/minecraft/server/world/ServerWorld$ServerEntityHandler")
public abstract class ServerWorld$ServerEntityHandlerMixin {

@Shadow
@Final
ServerWorld field_26936;

@Inject(method = "startTracking(Lnet/minecraft/entity/Entity;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;updateEventHandler(Ljava/util/function/BiConsumer;)V"))
private void startTracking(Entity entity, CallbackInfo ci) {
if (entity instanceof PartHolder<?> partHolder) {
for (EntityPart<?> part : partHolder.parts()) {
((EntityPartWorld) entity.getWorld()).specter$parts().put(part.getId(), part);
for (EntityPart<?> part : partHolder.getEntityParts()) {
this.field_26936.specter$getParts().put(part.getId(), part);
}
}
}

@Inject(method = "stopTracking(Lnet/minecraft/entity/Entity;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;updateEventHandler(Ljava/util/function/BiConsumer;)V"))
private void stopTracking(Entity entity, CallbackInfo ci) {
if (entity instanceof PartHolder<?> partHolder) {
for (EntityPart<?> part : partHolder.parts()) {
((EntityPartWorld) entity.getWorld()).specter$parts().remove(part.getId(), part);
for (EntityPart<?> part : partHolder.getEntityParts()) {
this.field_26936.specter$getParts().remove(part.getId(), part);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.sugar.Local;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

import net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerWorld;

import dev.spiritstudios.specter.impl.entity.EntityPartWorld;

@Mixin(ServerWorld.class)
public abstract class ServerWorldMixin implements EntityPartWorld {
public abstract class ServerWorldMixin extends WorldMixin {

@ModifyReturnValue(method = "getEntityOrDragonPart", at = @At("RETURN"))
private Entity getEntityOrDragonPart(Entity original, @Local(argsOnly = true) int id) {
return original != null ? original : this.specter$parts().get(id);
return original != null ? original : this.specter$parts.get(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
Expand All @@ -25,10 +26,11 @@

@Mixin(World.class)
public abstract class WorldMixin implements EntityPartWorld {

@Unique
private final Int2ObjectMap<EntityPart<?>> parts = new Int2ObjectOpenHashMap<>();
protected final Int2ObjectMap<EntityPart<?>> specter$parts = new Int2ObjectOpenHashMap<>();

@Inject(method = "method_47576", at = @At("TAIL"), cancellable = true)
@Inject(method = "method_47576", at = @At("RETURN"), cancellable = true)
private static <T extends Entity> void collectEntitiesByTypeLambda(
Predicate<? super T> predicate,
List<? super T> result,
Expand All @@ -38,7 +40,7 @@ private static <T extends Entity> void collectEntitiesByTypeLambda(
CallbackInfoReturnable<LazyIterationConsumer.NextIteration> cir
) {
if (entity instanceof PartHolder<?> partHolder) {
for (EntityPart<?> part : partHolder.parts()) {
for (EntityPart<?> part : partHolder.getEntityParts()) {
T partCasted = filter.downcast(part);

if (partCasted != null && predicate.test(partCasted)) {
Expand All @@ -55,7 +57,7 @@ private static <T extends Entity> void collectEntitiesByTypeLambda(
private List<Entity> getOtherEntities(Entity except, Box box, Predicate<? super Entity> predicate, Operation<List<Entity>> original) {
List<Entity> list = original.call(except, box, predicate);

for (EntityPart<?> part : this.parts.values()) {
for (EntityPart<?> part : this.specter$parts.values()) {
if (part != except && part.getOwner() != except && predicate.test(part) && box.intersects(part.getBoundingBox())) {
list.add(part);
}
Expand All @@ -65,7 +67,7 @@ private List<Entity> getOtherEntities(Entity except, Box box, Predicate<? super
}

@Override
public Int2ObjectMap<EntityPart<?>> specter$parts() {
return parts;
public Int2ObjectMap<EntityPart<?>> specter$getParts() {
return specter$parts;
}
}
3 changes: 3 additions & 0 deletions specter-entity/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
"badges": [
"library"
]
},
"loom:injected_interfaces": {
"net/minecraft/class_1937": ["dev/spiritstudios/specter/impl/entity/EntityPartWorld"]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protected SilverfishEntityMixin(EntityType<? extends HostileEntity> entityType,
}

@Override
public List<EntityPart<SilverfishEntity>> parts() {
public List<EntityPart<SilverfishEntity>> getEntityParts() {
return parts;
}

Expand Down
Loading