mirror of
https://github.com/architectury/architectury-api.git
synced 2026-03-28 03:56:59 -05:00
@@ -21,12 +21,16 @@ package dev.architectury.hooks.item.tool;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import dev.architectury.injectables.annotations.ExpectPlatform;
|
||||
import net.minecraft.world.item.HoeItem;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class HoeItemHooks {
|
||||
@@ -48,10 +52,25 @@ public final class HoeItemHooks {
|
||||
* @param predicate context predicate
|
||||
* @param action action to run
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@ApiStatus.ScheduledForRemoval
|
||||
public static void addTillable(Block input, Predicate<UseOnContext> predicate, Consumer<UseOnContext> action) {
|
||||
if (HoeItem.TILLABLES instanceof ImmutableMap) {
|
||||
HoeItem.TILLABLES = new HashMap<>(HoeItem.TILLABLES);
|
||||
}
|
||||
HoeItem.TILLABLES.put(input, new Pair<>(predicate, action));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new tilling action.
|
||||
*
|
||||
* @param input input block
|
||||
* @param predicate context predicate
|
||||
* @param action action to run
|
||||
* @param newState the new block state
|
||||
*/
|
||||
@ExpectPlatform
|
||||
public static void addTillable(Block input, Predicate<UseOnContext> predicate, Consumer<UseOnContext> action, Function<UseOnContext, BlockState> newState) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.architectury.hooks.item.tool.fabric;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.world.item.HoeItem;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class HoeItemHooksImpl {
|
||||
public static void addTillable(Block input, Predicate<UseOnContext> predicate, Consumer<UseOnContext> action, Function<UseOnContext, BlockState> newState) {
|
||||
if (HoeItem.TILLABLES instanceof ImmutableMap) {
|
||||
HoeItem.TILLABLES = new HashMap<>(HoeItem.TILLABLES);
|
||||
}
|
||||
HoeItem.TILLABLES.put(input, new Pair<>(predicate, useOnContext -> {
|
||||
action.accept(useOnContext);
|
||||
BlockState state = newState.apply(useOnContext);
|
||||
useOnContext.getLevel().setBlock(useOnContext.getClickedPos(), state, 11);
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
package dev.architectury.registry.registries.fabric;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
@@ -34,6 +35,7 @@ import net.fabricmc.fabric.api.event.registry.RegistryAttribute;
|
||||
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -48,7 +50,8 @@ public class RegistriesImpl {
|
||||
|
||||
private static void listen(ResourceKey<?> resourceKey, ResourceLocation id, Consumer<?> listener) {
|
||||
if (LISTENED_REGISTRIES.add(resourceKey)) {
|
||||
RegistryEntryAddedCallback.event(Registry.REGISTRY.get(resourceKey.location())).register((rawId, entryId, object) -> {
|
||||
Registry<?> registry = MoreObjects.firstNonNull(Registry.REGISTRY.get(resourceKey.location()), BuiltinRegistries.REGISTRY.get(resourceKey.location()));
|
||||
RegistryEntryAddedCallback.event(registry).register((rawId, entryId, object) -> {
|
||||
RegistryEntryId<?> registryEntryId = new RegistryEntryId<>(resourceKey, entryId);
|
||||
for (Consumer<?> consumer : LISTENERS.get(registryEntryId)) {
|
||||
((Consumer<Object>) consumer).accept(object);
|
||||
@@ -67,7 +70,7 @@ public class RegistriesImpl {
|
||||
public static <T> ResourceLocation getId(T object, ResourceKey<Registry<T>> fallback) {
|
||||
if (fallback == null)
|
||||
return null;
|
||||
return getId(object, (Registry<T>) Registry.REGISTRY.get(fallback.location()));
|
||||
return getId(object, (Registry<T>) MoreObjects.firstNonNull(Registry.REGISTRY.get(fallback.location()), BuiltinRegistries.REGISTRY.get(fallback.location())));
|
||||
}
|
||||
|
||||
public static <T> ResourceLocation getId(T object, Registry<T> fallback) {
|
||||
@@ -85,7 +88,7 @@ public class RegistriesImpl {
|
||||
|
||||
@Override
|
||||
public <T> Registrar<T> get(ResourceKey<Registry<T>> key) {
|
||||
return new RegistrarImpl<>(modId, (Registry<T>) Registry.REGISTRY.get(key.location()));
|
||||
return new RegistrarImpl<>(modId, (Registry<T>) MoreObjects.firstNonNull(Registry.REGISTRY.get(key.location()), BuiltinRegistries.REGISTRY.get(key.location())));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package dev.architectury.hooks.item.tool.forge;
|
||||
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.ToolActions;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class HoeItemHooksImpl {
|
||||
public static void addTillable(Block input, Predicate<UseOnContext> predicate, Consumer<UseOnContext> action, Function<UseOnContext, BlockState> function) {
|
||||
MinecraftForge.EVENT_BUS.<BlockEvent.BlockToolModificationEvent>addListener(event -> {
|
||||
UseOnContext context = event.getContext();
|
||||
if (ToolActions.HOE_TILL == event.getToolAction() && context.getItemInHand().canPerformAction(ToolActions.HOE_TILL)
|
||||
&& event.getState().is(input) && predicate.test(context)) {
|
||||
if (!event.isSimulated()) {
|
||||
action.accept(context);
|
||||
}
|
||||
|
||||
event.setFinalState(function.apply(context));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import dev.architectury.registry.registries.RegistrySupplier;
|
||||
import dev.architectury.registry.registries.options.RegistrarOption;
|
||||
import dev.architectury.registry.registries.options.StandardRegistrarOption;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
@@ -42,8 +43,9 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.registries.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
@@ -139,6 +141,12 @@ public class RegistriesImpl {
|
||||
private final Map<ResourceKey<? extends Registry<?>>, Data> registry = new HashMap<>();
|
||||
private final Multimap<ResourceKey<Registry<?>>, Consumer<Registrar<?>>> listeners = HashMultimap.create();
|
||||
|
||||
record RegistryBuilderEntry(RegistryBuilder<?> builder, Consumer<IForgeRegistry<?>> forgeRegistry) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<RegistryBuilderEntry> builders = new ArrayList<>();
|
||||
|
||||
public RegistryProviderImpl(String modId) {
|
||||
this.modId = modId;
|
||||
this.eventBus = Suppliers.memoize(() -> {
|
||||
@@ -161,6 +169,7 @@ public class RegistriesImpl {
|
||||
ForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(registryKey.location());
|
||||
if (registry == null) {
|
||||
Registry<T> ts = (Registry<T>) Registry.REGISTRY.get(registryKey.location());
|
||||
if (ts == null) ts = (Registry<T>) BuiltinRegistries.REGISTRY.get(registryKey.location());
|
||||
if (ts == null) {
|
||||
throw new IllegalArgumentException("Registry " + registryKey + " does not exist!");
|
||||
} else {
|
||||
@@ -191,7 +200,7 @@ public class RegistriesImpl {
|
||||
public <T> RegistrarBuilder<T> builder(Class<T> type, ResourceLocation registryId) {
|
||||
return new RegistryBuilderWrapper<>(this, new net.minecraftforge.registries.RegistryBuilder<>()
|
||||
.setName(registryId)
|
||||
.setType((Class) type));
|
||||
.setType((Class) type), registryId);
|
||||
}
|
||||
|
||||
public class EventListener {
|
||||
@@ -325,25 +334,47 @@ public class RegistriesImpl {
|
||||
LISTENERS.removeAll(id);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void handleEvent(NewRegistryEvent event) {
|
||||
if (builders != null) {
|
||||
for (RegistryBuilderEntry builder : builders) {
|
||||
event.create((RegistryBuilder) builder.builder(), (Consumer) builder.forgeRegistry());
|
||||
}
|
||||
builders = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class RegistryBuilderWrapper<T> implements RegistrarBuilder<T> {
|
||||
private final RegistryProviderImpl provider;
|
||||
private final net.minecraftforge.registries.RegistryBuilder<?> builder;
|
||||
private final ResourceLocation registryId;
|
||||
private boolean saveToDisk = false;
|
||||
private boolean syncToClients = false;
|
||||
|
||||
public RegistryBuilderWrapper(RegistryProviderImpl provider, net.minecraftforge.registries.RegistryBuilder<?> builder) {
|
||||
public RegistryBuilderWrapper(RegistryProviderImpl provider, RegistryBuilder<?> builder, ResourceLocation registryId) {
|
||||
this.provider = provider;
|
||||
this.builder = builder;
|
||||
this.registryId = registryId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registrar<T> build() {
|
||||
if (!syncToClients) builder.disableSync();
|
||||
if (!saveToDisk) builder.disableSaving();
|
||||
return provider.get(builder.create());
|
||||
if (provider.builders == null) {
|
||||
throw new IllegalStateException("Cannot create registries when registries are already aggregated!");
|
||||
}
|
||||
final var registrarRef = new Registrar<?>[1];
|
||||
var registrar = new DelegatedRegistrar(provider.modId, () -> java.util.Objects.requireNonNull(registrarRef[0], "Registry not yet initialized!"), registryId);
|
||||
var entry = new RegistryProviderImpl.RegistryBuilderEntry(builder, forgeRegistry -> {
|
||||
registrarRef[0] = provider.get(forgeRegistry);
|
||||
registrar.onRegister();
|
||||
});
|
||||
provider.builders.add(entry);
|
||||
return registrar;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -565,7 +596,7 @@ public class RegistriesImpl {
|
||||
|
||||
@Override
|
||||
public <E extends T> RegistrySupplier<E> register(ResourceLocation id, Supplier<E> supplier) {
|
||||
RegistryObject registryObject = RegistryObject.of(id, delegate);
|
||||
RegistryObject registryObject = RegistryObject.create(id, delegate);
|
||||
registry.computeIfAbsent(key(), type -> new Data())
|
||||
.register(delegate, registryObject, () -> supplier.get().setRegistryName(id));
|
||||
Registrar<T> registrar = this;
|
||||
@@ -686,4 +717,141 @@ public class RegistriesImpl {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DelegatedRegistrar<T> implements Registrar<T> {
|
||||
private final String modId;
|
||||
private final Supplier<Registrar<T>> delegate;
|
||||
private final ResourceLocation registryId;
|
||||
private List<Runnable> onRegister = new ArrayList<>();
|
||||
|
||||
public DelegatedRegistrar(String modId, Supplier<Registrar<T>> delegate, ResourceLocation registryId) {
|
||||
this.modId = modId;
|
||||
this.delegate = delegate;
|
||||
this.registryId = registryId;
|
||||
}
|
||||
|
||||
public void onRegister() {
|
||||
if (onRegister != null) {
|
||||
for (Runnable runnable : onRegister) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
onRegister = null;
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return onRegister == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistrySupplier<T> delegate(ResourceLocation id) {
|
||||
if (isReady()) return delegate.get().delegate(id);
|
||||
return new RegistrySupplier<T>() {
|
||||
@Override
|
||||
public Registries getRegistries() {
|
||||
return Registries.get(modId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registrar<T> getRegistrar() {
|
||||
return DelegatedRegistrar.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getRegistryId() {
|
||||
return DelegatedRegistrar.this.key().location();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return isReady() && delegate.get().contains(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return isReady() ? delegate.get().get(id) : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends T> RegistrySupplier<E> register(ResourceLocation id, Supplier<E> supplier) {
|
||||
if (isReady()) return delegate.get().register(id, supplier);
|
||||
onRegister.add(() -> delegate.get().register(id, supplier));
|
||||
return (RegistrySupplier<E>) delegate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ResourceLocation getId(T obj) {
|
||||
return !isReady() ? null : delegate.get().getId(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawId(T obj) {
|
||||
return !isReady() ? -1 : delegate.get().getRawId(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ResourceKey<T>> getKey(T obj) {
|
||||
return !isReady() ? Optional.empty() : delegate.get().getKey(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public T get(ResourceLocation id) {
|
||||
return !isReady() ? null : delegate.get().get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public T byRawId(int rawId) {
|
||||
return !isReady() ? null : delegate.get().byRawId(rawId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(ResourceLocation id) {
|
||||
return isReady() && delegate.get().contains(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(T obj) {
|
||||
return isReady() && delegate.get().containsValue(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ResourceLocation> getIds() {
|
||||
return isReady() ? delegate.get().getIds() : Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<ResourceKey<T>, T>> entrySet() {
|
||||
return isReady() ? delegate.get().entrySet() : Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceKey<? extends Registry<T>> key() {
|
||||
return isReady() ? delegate.get().key() : ResourceKey.createRegistryKey(registryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listen(ResourceLocation id, Consumer<T> callback) {
|
||||
if (isReady()) {
|
||||
delegate.get().listen(id, callback);
|
||||
} else {
|
||||
onRegister.add(() -> delegate.get().listen(id, callback));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return isReady() ? delegate.get().iterator() : Collections.emptyIterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ fabric_loader_version=0.13.3
|
||||
fabric_api_version=0.50.0+1.18.2
|
||||
mod_menu_version=3.0.0
|
||||
|
||||
forge_version=40.0.1
|
||||
forge_version=40.1.14
|
||||
|
||||
@@ -31,28 +31,21 @@ import net.minecraft.world.level.block.Blocks;
|
||||
public final class TestBlockInteractions {
|
||||
private TestBlockInteractions() {
|
||||
}
|
||||
|
||||
|
||||
public static void init() {
|
||||
AxeItemHooks.addStrippable(Blocks.QUARTZ_PILLAR, Blocks.OAK_LOG);
|
||||
ShovelItemHooks.addFlattenable(Blocks.IRON_ORE, Blocks.DIAMOND_BLOCK.defaultBlockState());
|
||||
HoeItemHooks.addTillable(Blocks.COAL_BLOCK, ctx -> {
|
||||
if (!ctx.getLevel().isNight()) {
|
||||
if (!ctx.getLevel().isClientSide) {
|
||||
Player player = ctx.getPlayer();
|
||||
if (player != null)
|
||||
player.sendMessage(new TextComponent("These dark arts can only be done at night!"), Util.NIL_UUID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return ctx.getLevel().isNight();
|
||||
}, ctx -> {
|
||||
BlockPos pos = ctx.getClickedPos();
|
||||
ctx.getLevel().setBlock(pos, Blocks.DIAMOND_BLOCK.defaultBlockState(), 3);
|
||||
if (!ctx.getLevel().isClientSide) {
|
||||
Player player = ctx.getPlayer();
|
||||
if (player != null)
|
||||
player.sendMessage(new TextComponent("Thou has successfully committed the dark arts of alchemy!!"), Util.NIL_UUID);
|
||||
}
|
||||
}, ctx -> {
|
||||
return Blocks.DIAMOND_BLOCK.defaultBlockState();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,15 @@
|
||||
|
||||
package dev.architectury.test.registry;
|
||||
|
||||
import dev.architectury.core.RegistryEntry;
|
||||
import dev.architectury.core.item.ArchitecturySpawnEggItem;
|
||||
import dev.architectury.hooks.item.food.FoodPropertiesHooks;
|
||||
import dev.architectury.hooks.level.entity.EntityHooks;
|
||||
import dev.architectury.registry.block.BlockProperties;
|
||||
import dev.architectury.registry.level.entity.EntityAttributeRegistry;
|
||||
import dev.architectury.registry.registries.DeferredRegister;
|
||||
import dev.architectury.registry.registries.Registrar;
|
||||
import dev.architectury.registry.registries.Registries;
|
||||
import dev.architectury.registry.registries.RegistrySupplier;
|
||||
import dev.architectury.test.TestMod;
|
||||
import dev.architectury.test.entity.TestEntity;
|
||||
@@ -33,6 +36,7 @@ import dev.architectury.test.registry.objects.EquippableTickingItem;
|
||||
import dev.architectury.test.tab.TestCreativeTabs;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectCategory;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
@@ -53,6 +57,17 @@ import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import static dev.architectury.test.TestMod.SINK;
|
||||
|
||||
public class TestRegistries {
|
||||
public static final class TestInt extends RegistryEntry<TestInt> {
|
||||
public final int value;
|
||||
|
||||
public TestInt(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Registrar<TestInt> INTS = Registries.get(TestMod.MOD_ID).<TestInt>builder(new ResourceLocation(TestMod.MOD_ID, "ints"))
|
||||
.syncToClients()
|
||||
.build();
|
||||
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(TestMod.MOD_ID, Registry.ITEM_REGISTRY);
|
||||
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(TestMod.MOD_ID, Registry.BLOCK_REGISTRY);
|
||||
public static final DeferredRegister<EntityType<?>> ENTITY_TYPES = DeferredRegister.create(TestMod.MOD_ID, Registry.ENTITY_TYPE_REGISTRY);
|
||||
@@ -60,6 +75,8 @@ public class TestRegistries {
|
||||
public static final DeferredRegister<RecipeSerializer<?>> RECIPE_SERIALIZERS = DeferredRegister.create(TestMod.MOD_ID, Registry.RECIPE_SERIALIZER_REGISTRY);
|
||||
public static final DeferredRegister<RecipeType<?>> RECIPE_TYPES = DeferredRegister.create(TestMod.MOD_ID, Registry.RECIPE_TYPE_REGISTRY);
|
||||
|
||||
public static final RegistrySupplier<TestInt> TEST_INT = INTS.register(new ResourceLocation(TestMod.MOD_ID, "test_int"), () -> new TestInt(1));
|
||||
public static final RegistrySupplier<TestInt> TEST_INT_2 = INTS.register(new ResourceLocation(TestMod.MOD_ID, "test_int_2"), () -> new TestInt(2));
|
||||
public static final RegistrySupplier<MobEffect> TEST_EFFECT = MOB_EFFECTS.register("test_effect", () ->
|
||||
new MobEffect(MobEffectCategory.NEUTRAL, 0x123456) {
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user