Polymer

Polymer

763k Downloads

Allow Registration-Based Usage of BlockWithElementHolder

Hanatte opened this issue ยท 1 comments

commented

Allow Registration-Based Usage of BlockWithElementHolder

The BlockWithElementHolder interface is useful, but it has a limitation: the Block class must directly implement the interface. This design makes it difficult to inject such functionality externally. To address this, I propose introducing a registration-based approach for using BlockWithElementHolder.

For example, the usage could look like this:

public final class FooBlockWithElementHolder implements BlockWithElementHolder {
    @Override
    public ElementHolder createElementHolder(ServerWorld world, BlockPos pos, BlockState initialBlockState) {
        // ...
    }
}

// Register the instance in a registry
BlockWithElementHolders.register(ModBlocks.FOO, new FooBlockWithElementHolder());

EntityDecorator API

I propose an API similar to BlockWithElementHolder and EntityRenderer. This class attaches an ElementHolder to the entity when the entity is added to the world by the ServerEntityManagerMixin. For example, it could be used as follows:

public final class FooEntityDecorator extends EntityDecorator<FooEntity> {
    @Override
    public ElementHolder getElementHolder() {
        FooEntity entity = this.getEntity();
        // ...
    }
}

// Register the decorator in a registry
EntityDecorators.INSTANCE.register(ModEntityTypes.FOO, FooEntityDecorator::new);

API Implementation

Here is an example implementation:

// EntityDecorator.java
import eu.pb4.polymer.virtualentity.api.ElementHolder;
import net.minecraft.entity.Entity;

public abstract class EntityDecorator<T extends Entity> {
    private final T entity;

    protected EntityDecorator(T entity) {
        this.entity = entity;
    }

    public T getEntity() {
        return entity;
    }

    public abstract ElementHolder getElementHolder();
}

// EntityDecoratorFactory.java
import net.minecraft.entity.Entity;

public interface EntityDecoratorFactory<T extends Entity> {
    EntityDecorator<T> create(T entity);
}

// EntityDecorators.java
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

public enum EntityDecorators {
    INSTANCE;

    private final Map<EntityType<?>, Set<EntityDecoratorFactory<?>>> type2Factories = new ConcurrentHashMap<>();

    public <T extends Entity> void register(EntityType<? extends T> type, EntityDecoratorFactory<T> factory) {
        type2Factories.computeIfAbsent(type, key -> new CopyOnWriteArraySet<>()).add(factory);
    }

    @SuppressWarnings("unchecked")
    public <T extends Entity> Set<EntityDecoratorFactory<T>> getFactories(EntityType<? extends T> type) {
        return (Set<EntityDecoratorFactory<T>>) (Set<?>) type2Factories.getOrDefault(type, Set.of());
    }
}

// ServerEntityManagerMixin.java
import eu.pb4.polymer.virtualentity.api.attachment.EntityAttachment;
import net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerEntityManager;
import net.minecraft.world.entity.EntityLike;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(value = ServerEntityManager.class)
public abstract class ServerEntityManagerMixin<T extends EntityLike> {
    @SuppressWarnings("unchecked")
    @Inject(method = "addEntity(Lnet/minecraft/world/entity/EntityLike;Z)Z", at = @At(value = "RETURN"))
    private void moire$injectAddEntity(T entityLike, boolean existing, CallbackInfoReturnable<Boolean> info) {
        if (info.getReturnValue() && entityLike instanceof Entity entity) {
            for (var factory : EntityDecorators.INSTANCE.getFactories(entity.getType())) {
                var decorator = ((EntityDecoratorFactory<Entity>) factory).create(entity);
                EntityAttachment.ofTicking(decorator.getElementHolder(), entity);
            }
        }
    }
}
commented

For entities you could use ServerEntityEvents.ENTITY_LOAD from fabric api.