
Allow Registration-Based Usage of BlockWithElementHolder
Hanatte opened this issue ยท 1 comments
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);
}
}
}
}