From daa3d4d8a4c0cc229ba1ad549626d74ce4b35483 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 7 Nov 2020 03:26:05 +0800 Subject: [PATCH] More events and hooks --- .../shedaniel/architectury/event/Actor.java | 24 ++++ .../architectury/event/EventFactory.java | 47 +++++++ .../architectury/event/events/ChatEvent.java | 61 +++++++++ .../event/events/CommandPerformEvent.java | 60 +++++++++ .../events/CommandRegistrationEvent.java | 6 +- .../event/events/EntityEvent.java | 34 +++++ .../architectury/event/events/GuiEvent.java | 34 +++++ .../event/events/LifecycleEvent.java | 47 +++++-- .../architectury/event/events/TickEvent.java | 4 + .../architectury/hooks/ScreenHooks.java | 51 ++++++++ .../architectury/registry/ColorHandlers.java | 52 ++++++++ .../architectury/registry/Registries.java | 24 ++++ fabric/build.gradle | 4 + .../event/fabric/EventFactoryImpl.java | 3 +- .../hooks/fabric/ScreenHooksImpl.java | 37 ++++++ .../mixin/fabric/LivingDeathInvoker.java | 38 ++++++ .../mixin/fabric/MixinCommands.java | 48 +++++++ .../mixin/fabric/MixinMinecraftServer.java | 44 +++++++ .../mixin/fabric/MixinPlayer.java | 37 ++++++ .../MixinServerGamePacketListenerImpl.java | 67 ++++++++++ .../mixin/fabric/MixinServerLevel.java | 33 +++++ .../mixin/fabric/client/MixinClientLevel.java | 40 ++++++ .../client/MixinClientPacketListener.java | 24 +++- .../client/MixinDebugScreenOverlay.java | 39 ++++++ .../mixin/fabric/client/MixinScreen.java | 66 ++++++++++ .../registry/fabric/ColorHandlersImpl.java | 36 ++++++ .../registry/fabric/RegistriesImpl.java | 10 ++ .../main/resources/architectury.accessWidener | 3 + .../main/resources/architectury.mixins.json | 7 +- fabric/src/main/resources/fabric.mod.json | 1 + .../event/forge/EventFactoryImpl.java | 119 ++++++++++++++++-- .../hooks/forge/ScreenHooksImpl.java | 45 +++++++ .../registry/forge/ColorHandlersImpl.java | 66 ++++++++++ .../registry/forge/RegistriesImpl.java | 14 +++ 34 files changed, 1201 insertions(+), 24 deletions(-) create mode 100644 common/src/main/java/me/shedaniel/architectury/event/Actor.java create mode 100644 common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java create mode 100644 common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java create mode 100644 common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java create mode 100644 common/src/main/java/me/shedaniel/architectury/hooks/ScreenHooks.java create mode 100644 common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/ScreenHooksImpl.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/LivingDeathInvoker.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinCommands.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinMinecraftServer.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerGamePacketListenerImpl.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerLevel.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientLevel.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinDebugScreenOverlay.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinScreen.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java create mode 100644 fabric/src/main/resources/architectury.accessWidener create mode 100644 forge/src/main/java/me/shedaniel/architectury/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java create mode 100644 forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java diff --git a/common/src/main/java/me/shedaniel/architectury/event/Actor.java b/common/src/main/java/me/shedaniel/architectury/event/Actor.java new file mode 100644 index 00000000..725d20ec --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/Actor.java @@ -0,0 +1,24 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event; + +import net.minecraft.world.InteractionResult; + +@FunctionalInterface +public interface Actor { + InteractionResult act(T t); +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java b/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java index eb38cb1b..85f302a2 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java +++ b/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java @@ -24,6 +24,7 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.jodah.typetools.TypeResolver; import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.NotNull; @@ -31,6 +32,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Function; public final class EventFactory { @@ -73,6 +75,51 @@ public final class EventFactory { })); } + @SuppressWarnings("UnstableApiUsage") + public static Event createInteractionResultHolder(Class clazz) { + return create(listeners -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() { + @Override + protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable { + for (T listener : listeners) { + InteractionResultHolder result = (InteractionResultHolder) Objects.requireNonNull(method.invoke(listener, args)); + if (result.getResult() != InteractionResult.PASS) { + return result; + } + } + return InteractionResultHolder.pass(null); + } + })); + } + + @SuppressWarnings("UnstableApiUsage") + public static Event> createConsumerLoop(Class clazz) { + return create(listeners -> (Consumer) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{Consumer.class}, new AbstractInvocationHandler() { + @Override + protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable { + for (Consumer listener : listeners) { + method.invoke(listener, args); + } + return null; + } + })); + } + + @SuppressWarnings("UnstableApiUsage") + public static Event> createActorLoop(Class clazz) { + return create(listeners -> (Actor) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{Actor.class}, new AbstractInvocationHandler() { + @Override + protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable { + for (Actor listener : listeners) { + InteractionResult result = (InteractionResult) method.invoke(listener, args); + if (result != InteractionResult.PASS) { + return result; + } + } + return InteractionResult.PASS; + } + })); + } + private static class EventImpl implements Event { private final Function function; private T invoker = null; diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java new file mode 100644 index 00000000..269697c2 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java @@ -0,0 +1,61 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.network.chat.ChatType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionResultHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public interface ChatEvent { + /** + * Invoked when client tries to send a message, equivalent to forge's {@code ClientChatEvent}. + */ + @Environment(EnvType.CLIENT) Event CLIENT = EventFactory.createInteractionResultHolder(Client.class); + /** + * Invoked when client receives a message, equivalent to forge's {@code ClientChatReceivedEvent}. + */ + @Environment(EnvType.CLIENT) Event CLIENT_RECEIVED = EventFactory.createInteractionResultHolder(ClientReceived.class); + /** + * Invoked when server receives a message, equivalent to forge's {@code ServerChatEvent}. + */ + Event SERVER = EventFactory.createInteractionResultHolder(Server.class); + + @Environment(EnvType.CLIENT) + interface Client { + @NotNull + InteractionResultHolder process(String message); + } + + @Environment(EnvType.CLIENT) + interface ClientReceived { + @NotNull + InteractionResultHolder process(ChatType type, Component message, @Nullable UUID sender); + } + + interface Server { + @NotNull + InteractionResultHolder process(String message, Component component); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java new file mode 100644 index 00000000..b48af2bb --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import com.mojang.brigadier.ParseResults; +import me.shedaniel.architectury.event.Actor; +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; +import net.minecraft.commands.CommandSourceStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CommandPerformEvent { + /** + * Invoked after server parses a command but before server executes it, equivalent to forge's {@code CommandEvent}. + */ + public static final Event> EVENT = EventFactory.createActorLoop(CommandPerformEvent.class); + + @NotNull + private ParseResults results; + @Nullable + private Throwable throwable; + + public CommandPerformEvent(@NotNull ParseResults results, @Nullable Throwable throwable) { + this.results = results; + this.throwable = throwable; + } + + @NotNull + public ParseResults getResults() { + return results; + } + + public void setResults(@NotNull ParseResults results) { + this.results = results; + } + + @Nullable + public Throwable getThrowable() { + return throwable; + } + + public void setThrowable(@Nullable Throwable throwable) { + this.throwable = throwable; + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java index 99014fee..9c3407d0 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java @@ -20,9 +20,13 @@ import com.mojang.brigadier.CommandDispatcher; import me.shedaniel.architectury.event.Event; import me.shedaniel.architectury.event.EventFactory; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; public interface CommandRegistrationEvent { + /** + * Invoked after server registers its commands, equivalent to forge's {@code RegisterCommandsEvent} and fabric's {@code CommandRegistrationCallback}. + */ Event EVENT = EventFactory.createLoop(CommandRegistrationEvent.class); - void register(CommandDispatcher var1); + void register(CommandDispatcher dispatcher, Commands.CommandSelection selection); } diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java new file mode 100644 index 00000000..910dabee --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.LivingEntity; + +public interface EntityEvent { + /** + * Invoked before LivingEntity#die, equivalent to forge's {@code LivingDeathEvent}. + */ + Event LIVING_DEATH = EventFactory.createInteractionResult(LivingDeath.class); + + interface LivingDeath { + InteractionResult die(LivingEntity entity, DamageSource source); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java index c83530c5..707724fa 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java @@ -21,13 +21,47 @@ import me.shedaniel.architectury.event.Event; import me.shedaniel.architectury.event.EventFactory; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.world.InteractionResult; + +import java.util.List; @Environment(EnvType.CLIENT) public interface GuiEvent { + /** + * Invoked after in-game hud is rendered, equivalent to forge's {@code RenderGameOverlayEvent.Post@ElementType#ALL} and fabric's {@code HudRenderCallback}. + */ Event RENDER_HUD = EventFactory.createLoop(RenderHud.class); + Event DEBUG_TEXT_LEFT = EventFactory.createLoop(DebugText.class); + Event DEBUG_TEXT_RIGHT = EventFactory.createLoop(DebugText.class); + /** + * Invoked during Screen#init after previous widgets are cleared, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Pre}. + */ + Event INIT_PRE = EventFactory.createInteractionResult(ScreenInitPre.class); + /** + * Invoked after Screen#init, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Post}. + */ + Event INIT_POST = EventFactory.createLoop(ScreenInitPost.class); @Environment(EnvType.CLIENT) interface RenderHud { void renderHud(PoseStack matrices, float tickDelta); } + + @Environment(EnvType.CLIENT) + interface DebugText { + void gatherText(List strings); + } + + @Environment(EnvType.CLIENT) + interface ScreenInitPre { + InteractionResult init(Screen screen, List widgets, List children); + } + + @Environment(EnvType.CLIENT) + interface ScreenInitPost { + void init(Screen screen, List widgets, List children); + } } diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java index 6b9e9e00..cc16106f 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java @@ -21,21 +21,49 @@ import me.shedaniel.architectury.event.EventFactory; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; -import net.minecraft.resources.ResourceKey; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; -import java.util.logging.Level; public interface LifecycleEvent { + /** + * Invoked when client has been initialised, not available in forge. + */ @Deprecated @Environment(EnvType.CLIENT) Event CLIENT_STARTED = EventFactory.createLoop(ClientState.class); + /** + * Invoked when client is initialising, not available in forge. + */ @Deprecated @Environment(EnvType.CLIENT) Event CLIENT_STOPPING = EventFactory.createLoop(ClientState.class); + /** + * Invoked when server is starting, equivalent to forge's {@code FMLServerStartingEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STARTING}. + */ Event SERVER_STARTING = EventFactory.createLoop(ServerState.class); + /** + * Invoked when server has started, equivalent to forge's {@code FMLServerStartedEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STARTED}. + */ Event SERVER_STARTED = EventFactory.createLoop(ServerState.class); + /** + * Invoked when server is stopping, equivalent to forge's {@code FMLServerStoppingEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STOPPING}. + */ Event SERVER_STOPPING = EventFactory.createLoop(ServerState.class); + /** + * Invoked when server has stopped, equivalent to forge's {@code FMLServerStoppedEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STOPPED}. + */ Event SERVER_STOPPED = EventFactory.createLoop(ServerState.class); - Event WORLD_LOAD = EventFactory.createLoop(WorldLoad.class); - Event WORLD_SAVE = EventFactory.createLoop(WorldSave.class); + /** + * Invoked after a world is loaded only on server, equivalent to forge's {@code WorldEvent.Load}. + */ + Event SERVER_WORLD_LOAD = EventFactory.createLoop(ServerWorldState.class); + /** + * Invoked during a world is saved, equivalent to forge's {@code WorldEvent.Save}. + */ + Event SERVER_WORLD_SAVE = EventFactory.createLoop(ServerWorldState.class); + /** + * Invoked after a world is loaded only on client, equivalent to forge's {@code WorldEvent.Load}. + */ + @Environment(EnvType.CLIENT) Event CLIENT_WORLD_LOAD = EventFactory.createLoop(ClientWorldState.class); interface InstanceState { void stateChanged(T instance); @@ -47,11 +75,12 @@ public interface LifecycleEvent { interface ServerState extends InstanceState {} - interface WorldLoad { - void load(ResourceKey key, ServerLevel world); + interface WorldState { + void act(T world); } - interface WorldSave { - void save(ServerLevel world); - } + @Environment(EnvType.CLIENT) + interface ClientWorldState extends WorldState {} + + interface ServerWorldState extends WorldState {} } diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java index da3c087d..add64513 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java @@ -35,6 +35,8 @@ public interface TickEvent { @Environment(EnvType.CLIENT) Event CLIENT_WORLD_POST = EventFactory.createLoop(ClientWorld.class); Event SERVER_WORLD_PRE = EventFactory.createLoop(ServerWorld.class); Event SERVER_WORLD_POST = EventFactory.createLoop(ServerWorld.class); + Event PLAYER_PRE = EventFactory.createLoop(Player.class); + Event PLAYER_POST = EventFactory.createLoop(Player.class); void tick(T instance); @@ -49,4 +51,6 @@ public interface TickEvent { interface ClientWorld extends WorldTick {} interface ServerWorld extends WorldTick {} + + interface Player extends TickEvent {} } diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/ScreenHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/ScreenHooks.java new file mode 100644 index 00000000..09276d1f --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/hooks/ScreenHooks.java @@ -0,0 +1,51 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.hooks; + +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; + +@Environment(EnvType.CLIENT) +public final class ScreenHooks { + private ScreenHooks() {} + + @Populatable + private static final Impl IMPL = null; + + public static T addButton(Screen screen, T widget) { + return IMPL.addButton(screen, widget); + } + + public static T addChild(Screen screen, T listener) { + return IMPL.addChild(screen, listener); + } + + public interface Impl { + T addButton(Screen screen, T widget); + + T addChild(Screen screen, T listener); + } + + static { + ArchitecturyPopulator.populate(ScreenHooks.class); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java new file mode 100644 index 00000000..4343af1c --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.registry; + +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.color.block.BlockColor; +import net.minecraft.client.color.item.ItemColor; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; + +@Environment(EnvType.CLIENT) +public final class ColorHandlers { + private ColorHandlers() {} + + @Populatable + private static final Impl IMPL = null; + + public static void registerItemColors(ItemColor color, ItemLike... items) { + IMPL.registerItemColors(color, items); + } + + public static void registerBlockColors(BlockColor color, Block... blocks) { + IMPL.registerBlockColors(color, blocks); + } + + public interface Impl { + void registerItemColors(ItemColor color, ItemLike... items); + + void registerBlockColors(BlockColor color, Block... blocks); + } + + static { + ArchitecturyPopulator.populate(ColorHandlers.class); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/registry/Registries.java b/common/src/main/java/me/shedaniel/architectury/registry/Registries.java index de402459..42b69766 100644 --- a/common/src/main/java/me/shedaniel/architectury/registry/Registries.java +++ b/common/src/main/java/me/shedaniel/architectury/registry/Registries.java @@ -19,6 +19,8 @@ package me.shedaniel.architectury.registry; import me.shedaniel.architectury.ArchitecturyPopulator; import me.shedaniel.architectury.Populatable; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.Map; @@ -46,8 +48,30 @@ public final class Registries { return this.provider.get(registry); } + /** + * Forge: If the object is {@code IForgeRegistryEntry}, use `getRegistryName`, else null + * Fabric: Use registry + */ + @Nullable + public ResourceLocation getId(T object, ResourceKey> fallback) { + return IMPL.getId(object, fallback); + } + + /** + * Forge: If the object is {@code IForgeRegistryEntry}, use `getRegistryName`, else null + * Fabric: Use registry + */ + @Nullable + public ResourceLocation getId(T object, net.minecraft.core.Registry fallback) { + return IMPL.getId(object, fallback); + } + public interface Impl { RegistryProvider get(String modId); + + ResourceLocation getId(T object, ResourceKey> fallback); + + ResourceLocation getId(T object, net.minecraft.core.Registry fallback); } public interface RegistryProvider { diff --git a/fabric/build.gradle b/fabric/build.gradle index 102a4c3a..87376019 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -3,6 +3,10 @@ plugins { id "com.github.johnrengelman.shadow" version "5.0.0" } +loom { + accessWidener(file("src/main/resources/architectury.accessWidener")) +} + configurations { shadow } diff --git a/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java index f8756e49..f60e8d7f 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java @@ -25,6 +25,7 @@ import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.minecraft.commands.Commands; public class EventFactoryImpl implements EventFactory.Impl { @Override @@ -53,7 +54,7 @@ public class EventFactoryImpl implements EventFactory.Impl { ServerTickEvents.START_WORLD_TICK.register(TickEvent.SERVER_WORLD_PRE.invoker()::tick); ServerTickEvents.END_WORLD_TICK.register(TickEvent.SERVER_WORLD_POST.invoker()::tick); - CommandRegistrationCallback.EVENT.register((commandDispatcher, b) -> CommandRegistrationEvent.EVENT.invoker().register(commandDispatcher)); + CommandRegistrationCallback.EVENT.register((commandDispatcher, b) -> CommandRegistrationEvent.EVENT.invoker().register(commandDispatcher, b ? Commands.CommandSelection.DEDICATED : Commands.CommandSelection.INTEGRATED)); } @Override diff --git a/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/ScreenHooksImpl.java b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/ScreenHooksImpl.java new file mode 100644 index 00000000..85e6a703 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/ScreenHooksImpl.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.hooks.fabric; + +import me.shedaniel.architectury.hooks.ScreenHooks; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; + +@Environment(EnvType.CLIENT) +public class ScreenHooksImpl implements ScreenHooks.Impl { + @Override + public T addButton(Screen screen, T widget) { + return screen.addButton(widget); + } + + @Override + public T addChild(Screen screen, T listener) { + return screen.addWidget(listener); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/LivingDeathInvoker.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/LivingDeathInvoker.java new file mode 100644 index 00000000..4e394b72 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/LivingDeathInvoker.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric; + +import me.shedaniel.architectury.event.events.EntityEvent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +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.CallbackInfo; + +@Mixin(value = {LivingEntity.class, Player.class, ServerPlayer.class}) +public class LivingDeathInvoker { + @Inject(method = "die", at = @At("HEAD"), cancellable = true) + private void die(DamageSource source, CallbackInfo ci) { + if (EntityEvent.LIVING_DEATH.invoker().die((LivingEntity) (Object) this, source) == InteractionResult.FAIL) { + ci.cancel(); + } + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinCommands.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinCommands.java new file mode 100644 index 00000000..662214ac --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinCommands.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric; + +import com.google.common.base.Throwables; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import me.shedaniel.architectury.event.events.CommandPerformEvent; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.world.InteractionResult; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Commands.class) +public class MixinCommands { + @Redirect(method = "performCommand", + at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/CommandDispatcher;execute(Lcom/mojang/brigadier/StringReader;Ljava/lang/Object;)I")) + private int performCommand(CommandDispatcher dispatcher, StringReader input, Object source) throws CommandSyntaxException { + CommandSourceStack stack = (CommandSourceStack) source; + ParseResults parse = dispatcher.parse(input, stack); + CommandPerformEvent event = new CommandPerformEvent(parse, null); + if (CommandPerformEvent.EVENT.invoker().act(event) != InteractionResult.PASS) { + if (event.getThrowable() != null) { + Throwables.throwIfUnchecked(event.getThrowable()); + } + return 1; + } + return dispatcher.execute(event.getResults()); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinMinecraftServer.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinMinecraftServer.java new file mode 100644 index 00000000..13effb8b --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinMinecraftServer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric; + +import me.shedaniel.architectury.event.events.LifecycleEvent; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.world.level.Level; +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 java.util.Map; + +@Mixin(MinecraftServer.class) +public class MixinMinecraftServer { + @Shadow @Final private Map, ServerLevel> levels; + + @Inject(method = "createLevels", at = @At("RETURN")) + private void createLevels(ChunkProgressListener chunkProgressListener, CallbackInfo ci) { + for (ServerLevel level : levels.values()) { + LifecycleEvent.SERVER_WORLD_LOAD.invoker().act(level); + } + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java new file mode 100644 index 00000000..347bb123 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric; + +import me.shedaniel.architectury.event.events.TickEvent; +import net.minecraft.world.entity.player.Player; +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.CallbackInfo; + +@Mixin(Player.class) +public class MixinPlayer { + @Inject(method = "tick", at = @At("HEAD")) + private void preTick(CallbackInfo ci) { + TickEvent.PLAYER_PRE.invoker().tick((Player) (Object) this); + } + + @Inject(method = "tick", at = @At("RETURN")) + private void postTick(CallbackInfo ci) { + TickEvent.PLAYER_POST.invoker().tick((Player) (Object) this); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerGamePacketListenerImpl.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerGamePacketListenerImpl.java new file mode 100644 index 00000000..8c9869e2 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerGamePacketListenerImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric; + +import me.shedaniel.architectury.event.events.ChatEvent; +import net.minecraft.network.chat.ChatType; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.protocol.game.ServerboundChatPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import org.apache.commons.lang3.StringUtils; +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; + +@Mixin(ServerGamePacketListenerImpl.class) +public abstract class MixinServerGamePacketListenerImpl { + @Shadow public ServerPlayer player; + + @Shadow @Final private MinecraftServer server; + + @Shadow private int chatSpamTickCount; + + @Shadow + public abstract void disconnect(Component component); + + @Inject(method = "handleChat", at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/players/PlayerList;broadcastMessage(Lnet/minecraft/network/chat/Component;Lnet/minecraft/network/chat/ChatType;Ljava/util/UUID;)V"), + cancellable = true) + private void handleChat(ServerboundChatPacket packet, CallbackInfo ci) { + String string = StringUtils.normalizeSpace(packet.getMessage()); + Component component = new TranslatableComponent("chat.type.text", this.player.getDisplayName(), string); + InteractionResultHolder process = ChatEvent.SERVER.invoker().process(string, component); + if (process.getResult() == InteractionResult.FAIL) + ci.cancel(); + else if (process.getObject() != null && !process.getObject().equals(component)) { + this.server.getPlayerList().broadcastMessage(component, ChatType.CHAT, this.player.getUUID()); + + this.chatSpamTickCount += 20; + if (this.chatSpamTickCount > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) { + this.disconnect(new TranslatableComponent("disconnect.spam")); + } + ci.cancel(); + } + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerLevel.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerLevel.java new file mode 100644 index 00000000..fa2dac60 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerLevel.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric; + +import me.shedaniel.architectury.event.events.LifecycleEvent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.ProgressListener; +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.CallbackInfo; + +@Mixin(ServerLevel.class) +public class MixinServerLevel { + @Inject(method = "save", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;save(Z)V")) + private void save(ProgressListener progressListener, boolean bl, boolean bl2, CallbackInfo ci) { + LifecycleEvent.SERVER_WORLD_SAVE.invoker().act((ServerLevel) (Object) this); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientLevel.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientLevel.java new file mode 100644 index 00000000..bd1c0f8f --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientLevel.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric.client; + +import me.shedaniel.architectury.event.events.LifecycleEvent; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +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.CallbackInfo; + +import java.util.function.Supplier; + +@Mixin(ClientLevel.class) +public class MixinClientLevel { + @Inject(method = "", at = @At("RETURN")) + private void construct(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey resourceKey, DimensionType dimensionType, int i, Supplier supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) { + LifecycleEvent.CLIENT_WORLD_LOAD.invoker().act((ClientLevel) (Object) this); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientPacketListener.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientPacketListener.java index ae271cfb..71578980 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientPacketListener.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientPacketListener.java @@ -16,12 +16,17 @@ package me.shedaniel.architectury.mixin.fabric.client; +import me.shedaniel.architectury.event.events.ChatEvent; import me.shedaniel.architectury.event.events.PlayerEvent; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundChatPacket; import net.minecraft.network.protocol.game.ClientboundLoginPacket; import net.minecraft.network.protocol.game.ClientboundRespawnPacket; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -35,19 +40,32 @@ public class MixinClientPacketListener { @Unique private LocalPlayer tmpPlayer; @Inject(method = "handleLogin", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Options;broadcastOptions()V")) - private void handleLogin(ClientboundLoginPacket clientboundLoginPacket, CallbackInfo ci) { + private void handleLogin(ClientboundLoginPacket packet, CallbackInfo ci) { PlayerEvent.CLIENT_PLAYER_JOIN.invoker().join(minecraft.player); } @Inject(method = "handleRespawn", at = @At("HEAD")) - private void handleRespawnPre(ClientboundRespawnPacket clientboundRespawnPacket, CallbackInfo ci) { + private void handleRespawnPre(ClientboundRespawnPacket packet, CallbackInfo ci) { this.tmpPlayer = minecraft.player; } @Inject(method = "handleRespawn", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;addPlayer(ILnet/minecraft/client/player/AbstractClientPlayer;)V")) - private void handleRespawn(ClientboundRespawnPacket clientboundRespawnPacket, CallbackInfo ci) { + private void handleRespawn(ClientboundRespawnPacket packet, CallbackInfo ci) { PlayerEvent.CLIENT_PLAYER_RESPAWN.invoker().respawn(tmpPlayer, minecraft.player); this.tmpPlayer = null; } + + @Inject(method = "handleChat", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/gui/Gui;handleChat(Lnet/minecraft/network/chat/ChatType;Lnet/minecraft/network/chat/Component;Ljava/util/UUID;)V"), + cancellable = true) + private void handleChat(ClientboundChatPacket packet, CallbackInfo ci) { + InteractionResultHolder process = ChatEvent.CLIENT_RECEIVED.invoker().process(packet.getType(), packet.getMessage(), packet.getSender()); + if (process.getResult() == InteractionResult.FAIL) + ci.cancel(); + else if (process.getObject() != null && !process.getObject().equals(packet.getMessage())) { + this.minecraft.gui.handleChat(packet.getType(), packet.getMessage(), packet.getSender()); + ci.cancel(); + } + } } diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinDebugScreenOverlay.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinDebugScreenOverlay.java new file mode 100644 index 00000000..b46dd38f --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinDebugScreenOverlay.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric.client; + +import me.shedaniel.architectury.event.events.GuiEvent; +import net.minecraft.client.gui.components.DebugScreenOverlay; +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; + +import java.util.List; + +@Mixin(DebugScreenOverlay.class) +public class MixinDebugScreenOverlay { + @Inject(method = "getGameInformation", at = @At("RETURN")) + private void getLeftTexts(CallbackInfoReturnable> cir) { + GuiEvent.DEBUG_TEXT_LEFT.invoker().gatherText(cir.getReturnValue()); + } + + @Inject(method = "getSystemInformation", at = @At("RETURN")) + private void getRightTexts(CallbackInfoReturnable> cir) { + GuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(cir.getReturnValue()); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinScreen.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinScreen.java new file mode 100644 index 00000000..84dcdf23 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinScreen.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric.client; + +import me.shedaniel.architectury.event.events.ChatEvent; +import me.shedaniel.architectury.event.events.GuiEvent; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +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.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(Screen.class) +public abstract class MixinScreen { + @Shadow @Final protected List buttons; + + @Shadow + public abstract List children(); + + @Inject(method = "init(Lnet/minecraft/client/Minecraft;II)V", at = @At(value = "INVOKE", target = "Ljava/util/List;clear()V", ordinal = 0), + cancellable = true) + private void preInit(Minecraft minecraft, int i, int j, CallbackInfo ci) { + if (GuiEvent.INIT_PRE.invoker().init((Screen) (Object) this, buttons, (List) children()) == InteractionResult.FAIL) { + ci.cancel(); + } + } + + @Inject(method = "init(Lnet/minecraft/client/Minecraft;II)V", at = @At(value = "RETURN")) + private void postInit(Minecraft minecraft, int i, int j, CallbackInfo ci) { + GuiEvent.INIT_POST.invoker().init((Screen) (Object) this, buttons, (List) children()); + } + + @ModifyVariable(method = "sendMessage(Ljava/lang/String;Z)V", at = @At("HEAD"), argsOnly = true, ordinal = 0) + private String modifyMessage(String message) { + InteractionResultHolder process = ChatEvent.CLIENT.invoker().process(message); + if (process.getResult() == InteractionResult.FAIL) + return ""; + if (process.getObject() != null) + return process.getObject(); + return message; + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java new file mode 100644 index 00000000..3a8b14ad --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.registry.fabric; + +import me.shedaniel.architectury.registry.ColorHandlers; +import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; +import net.minecraft.client.color.block.BlockColor; +import net.minecraft.client.color.item.ItemColor; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; + +public class ColorHandlersImpl implements ColorHandlers.Impl { + @Override + public void registerItemColors(ItemColor color, ItemLike... items) { + ColorProviderRegistry.ITEM.register(color, items); + } + + @Override + public void registerBlockColors(BlockColor color, Block... blocks) { + ColorProviderRegistry.BLOCK.register(color, blocks); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java index f48f0134..0632a337 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java @@ -31,6 +31,16 @@ public class RegistriesImpl implements Registries.Impl { return RegistryProviderImpl.INSTANCE; } + @Override + public ResourceLocation getId(T object, ResourceKey> fallback) { + return RegistryProviderImpl.INSTANCE.get(fallback).getId(object); + } + + @Override + public ResourceLocation getId(T object, net.minecraft.core.Registry fallback) { + return RegistryProviderImpl.INSTANCE.get(fallback).getId(object); + } + public enum RegistryProviderImpl implements Registries.RegistryProvider { INSTANCE; diff --git a/fabric/src/main/resources/architectury.accessWidener b/fabric/src/main/resources/architectury.accessWidener new file mode 100644 index 00000000..bf65cb34 --- /dev/null +++ b/fabric/src/main/resources/architectury.accessWidener @@ -0,0 +1,3 @@ +accessWidener v1 named +accessible method net/minecraft/client/gui/screens/Screen addButton (Lnet/minecraft/client/gui/components/AbstractWidget;)Lnet/minecraft/client/gui/components/AbstractWidget; +accessible method net/minecraft/client/gui/screens/Screen addWidget (Lnet/minecraft/client/gui/components/events/GuiEventListener;)Lnet/minecraft/client/gui/components/events/GuiEventListener; \ No newline at end of file diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index 3c4cc1e3..2e50b051 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -4,11 +4,14 @@ "compatibilityLevel": "JAVA_8", "minVersion": "0.7.11", "client": [ + "client.MixinClientLevel", "client.MixinClientPacketListener", - "client.MixinMinecraft" + "client.MixinDebugScreenOverlay", + "client.MixinMinecraft", + "client.MixinScreen" ], "mixins": [ - "MixinPlayerList" + "LivingDeathInvoker", "MixinCommands", "MixinMinecraftServer", "MixinPlayer", "MixinPlayerList", "MixinServerGamePacketListenerImpl", "MixinServerLevel" ], "injectors": { "defaultRequire": 1 diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 1f3c52eb..239935b7 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -20,6 +20,7 @@ "me.shedaniel.architectury.compat.fabric.ModMenuCompatibility" ] }, + "accessWidener": "architectury.accessWidener", "depends": { "minecraft": ">=1.16.2" } diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java index 83b8ca38..4114d747 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java @@ -19,22 +19,27 @@ package me.shedaniel.architectury.event.forge; import me.shedaniel.architectury.event.EventFactory; import me.shedaniel.architectury.event.events.*; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.IGuiEventListener; +import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.text.ITextComponent; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.client.event.ClientPlayerNetworkEvent; -import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.client.event.*; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.CommandEvent; import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.event.TickEvent.ClientTickEvent; -import net.minecraftforge.event.TickEvent.Phase; -import net.minecraftforge.event.TickEvent.ServerTickEvent; -import net.minecraftforge.event.TickEvent.WorldTickEvent; +import net.minecraftforge.event.ServerChatEvent; +import net.minecraftforge.event.TickEvent.*; +import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.player.ItemTooltipEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; import net.minecraftforge.event.entity.player.PlayerEvent.PlayerRespawnEvent; +import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; @@ -43,6 +48,8 @@ import net.minecraftforge.fml.event.server.FMLServerStoppedEvent; import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; import net.minecraftforge.fml.server.ServerLifecycleHooks; +import java.util.List; + public class EventFactoryImpl implements EventFactory.Impl { @Override public void registerClient() { @@ -76,7 +83,8 @@ public class EventFactoryImpl implements EventFactory.Impl { @SubscribeEvent public static void event(RenderGameOverlayEvent.Post event) { - GuiEvent.RENDER_HUD.invoker().renderHud(event.getMatrixStack(), event.getPartialTicks()); + if (event.getType() == RenderGameOverlayEvent.ElementType.ALL) + GuiEvent.RENDER_HUD.invoker().renderHud(event.getMatrixStack(), event.getPartialTicks()); } @SubscribeEvent @@ -93,6 +101,50 @@ public class EventFactoryImpl implements EventFactory.Impl { public static void event(ClientPlayerNetworkEvent.RespawnEvent event) { PlayerEvent.CLIENT_PLAYER_RESPAWN.invoker().respawn(event.getOldPlayer(), event.getNewPlayer()); } + + @SubscribeEvent + public static void event(GuiScreenEvent.InitGuiEvent.Pre event) { + if (GuiEvent.INIT_PRE.invoker().init(event.getGui(), event.getWidgetList(), (List) event.getGui().children()) == ActionResultType.FAIL) { + event.setCanceled(true); + } + } + + @SubscribeEvent + public static void event(GuiScreenEvent.InitGuiEvent.Post event) { + GuiEvent.INIT_POST.invoker().init(event.getGui(), event.getWidgetList(), (List) event.getGui().children()); + } + + @SubscribeEvent + public static void event(RenderGameOverlayEvent.Text event) { + GuiEvent.DEBUG_TEXT_LEFT.invoker().gatherText(event.getLeft()); + GuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(event.getRight()); + } + + @SubscribeEvent + public static void event(ClientChatEvent event) { + ActionResult process = ChatEvent.CLIENT.invoker().process(event.getMessage()); + if (process.getObject() != null) + event.setMessage(process.getObject()); + if (process.getResult() == ActionResultType.FAIL) + event.setCanceled(true); + } + + @SubscribeEvent + public static void event(ClientChatReceivedEvent event) { + ActionResult process = ChatEvent.CLIENT_RECEIVED.invoker().process(event.getType(), event.getMessage(), event.getSenderUUID()); + if (process.getObject() != null) + event.setMessage(process.getObject()); + if (process.getResult() == ActionResultType.FAIL) + event.setCanceled(true); + } + + @SubscribeEvent + public static void event(WorldEvent.Save event) { + if (event.getWorld() instanceof ClientWorld) { + ClientWorld world = (ClientWorld) event.getWorld(); + LifecycleEvent.CLIENT_WORLD_LOAD.invoker().act(world); + } + } } public static class Common { @@ -136,7 +188,7 @@ public class EventFactoryImpl implements EventFactory.Impl { @SubscribeEvent public static void event(RegisterCommandsEvent event) { - CommandRegistrationEvent.EVENT.invoker().register(event.getDispatcher()); + CommandRegistrationEvent.EVENT.invoker().register(event.getDispatcher(), event.getEnvironment()); } @SubscribeEvent @@ -153,6 +205,57 @@ public class EventFactoryImpl implements EventFactory.Impl { public static void event(PlayerRespawnEvent event) { PlayerEvent.PLAYER_RESPAWN.invoker().respawn((ServerPlayerEntity) event.getPlayer(), event.isEndConquered()); } + + @SubscribeEvent + public static void event(CommandEvent event) { + CommandPerformEvent performEvent = new CommandPerformEvent(event.getParseResults(), event.getException()); + if (CommandPerformEvent.EVENT.invoker().act(performEvent) == ActionResultType.FAIL) { + event.setCanceled(true); + } + event.setParseResults(performEvent.getResults()); + event.setException(performEvent.getThrowable()); + } + + @SubscribeEvent + public static void event(PlayerTickEvent event) { + if (event.phase == Phase.START) { + TickEvent.PLAYER_PRE.invoker().tick(event.player); + } else if (event.phase == Phase.END) { + TickEvent.PLAYER_POST.invoker().tick(event.player); + } + } + + @SubscribeEvent + public static void event(ServerChatEvent event) { + ActionResult process = ChatEvent.SERVER.invoker().process(event.getMessage(), event.getComponent()); + if (process.getObject() != null) + event.setComponent(process.getObject()); + if (process.getResult() == ActionResultType.FAIL) + event.setCanceled(true); + } + + @SubscribeEvent + public static void event(WorldEvent.Load event) { + if (event.getWorld() instanceof ServerWorld) { + ServerWorld world = (ServerWorld) event.getWorld(); + LifecycleEvent.SERVER_WORLD_LOAD.invoker().act(world); + } + } + + @SubscribeEvent + public static void event(WorldEvent.Save event) { + if (event.getWorld() instanceof ServerWorld) { + ServerWorld world = (ServerWorld) event.getWorld(); + LifecycleEvent.SERVER_WORLD_SAVE.invoker().act(world); + } + } + + @SubscribeEvent + public static void event(LivingDeathEvent event) { + if (EntityEvent.LIVING_DEATH.invoker().die(event.getEntityLiving(), event.getSource()) == ActionResultType.FAIL) { + event.setCanceled(true); + } + } } @OnlyIn(Dist.DEDICATED_SERVER) diff --git a/forge/src/main/java/me/shedaniel/architectury/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java b/forge/src/main/java/me/shedaniel/architectury/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java new file mode 100644 index 00000000..3c4a5de6 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.me.shedaniel.architectury.hooks.forge; + +import me.shedaniel.architectury.hooks.ScreenHooks; +import net.minecraft.client.gui.IGuiEventListener; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.Widget; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; + +import java.lang.reflect.InvocationTargetException; + +public class ScreenHooksImpl implements ScreenHooks.Impl { + @Override + public T addButton(Screen screen, T t) { + try { + return (T) ObfuscationReflectionHelper.findMethod(Screen.class, "func_230480_a_", Widget.class).invoke(screen, t); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + public T addChild(Screen screen, T t) { + try { + return (T) ObfuscationReflectionHelper.findMethod(Screen.class, "func_230481_d_", IGuiEventListener.class).invoke(screen, t); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java new file mode 100644 index 00000000..2952b8fb --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.registry.forge; + +import com.google.common.collect.Lists; +import me.shedaniel.architectury.registry.ColorHandlers; +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.color.IBlockColor; +import net.minecraft.client.renderer.color.IItemColor; +import net.minecraft.util.IItemProvider; +import net.minecraftforge.client.event.ColorHandlerEvent; +import net.minecraftforge.common.MinecraftForge; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; + +public class ColorHandlersImpl implements ColorHandlers.Impl { + private static final List> ITEM_COLORS = Lists.newArrayList(); + private static final List> BLOCK_COLORS = Lists.newArrayList(); + + public ColorHandlersImpl() { + MinecraftForge.EVENT_BUS.addListener(event -> { + for (Pair pair : ITEM_COLORS) { + event.getItemColors().register(pair.getLeft(), pair.getRight()); + } + }); + MinecraftForge.EVENT_BUS.addListener(event -> { + for (Pair pair : BLOCK_COLORS) { + event.getBlockColors().register(pair.getLeft(), pair.getRight()); + } + }); + } + + @Override + public void registerItemColors(IItemColor itemColor, IItemProvider... items) { + if (Minecraft.getInstance().getItemColors() == null) { + ITEM_COLORS.add(Pair.of(itemColor, items)); + } else { + Minecraft.getInstance().getItemColors().register(itemColor, items); + } + } + + @Override + public void registerBlockColors(IBlockColor blockColor, Block... blocks) { + if (Minecraft.getInstance().getBlockColors() == null) { + BLOCK_COLORS.add(Pair.of(blockColor, blocks)); + } else { + Minecraft.getInstance().getBlockColors().register(blockColor, blocks); + } + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java index 8abf30b7..e42cab19 100644 --- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java @@ -43,6 +43,20 @@ public class RegistriesImpl implements Registries.Impl { return new RegistryProviderImpl(modId); } + @Override + public ResourceLocation getId(T t, RegistryKey> registryKey) { + if (t instanceof IForgeRegistryEntry) + return ((IForgeRegistryEntry) t).getRegistryName(); + return null; + } + + @Override + public ResourceLocation getId(T t, net.minecraft.util.registry.Registry registry) { + if (t instanceof IForgeRegistryEntry) + return ((IForgeRegistryEntry) t).getRegistryName(); + return null; + } + public static class RegistryProviderImpl implements Registries.RegistryProvider { private final String modId; private final IEventBus eventBus;