Bläddra i källkod

More events and hooks

shedaniel 4 år sedan
förälder
incheckning
daa3d4d8a4
34 ändrade filer med 1201 tillägg och 24 borttagningar
  1. 24 0
      common/src/main/java/me/shedaniel/architectury/event/Actor.java
  2. 47 0
      common/src/main/java/me/shedaniel/architectury/event/EventFactory.java
  3. 61 0
      common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java
  4. 60 0
      common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java
  5. 5 1
      common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java
  6. 34 0
      common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java
  7. 34 0
      common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
  8. 38 9
      common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java
  9. 4 0
      common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java
  10. 51 0
      common/src/main/java/me/shedaniel/architectury/hooks/ScreenHooks.java
  11. 52 0
      common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java
  12. 24 0
      common/src/main/java/me/shedaniel/architectury/registry/Registries.java
  13. 4 0
      fabric/build.gradle
  14. 2 1
      fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java
  15. 37 0
      fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/ScreenHooksImpl.java
  16. 38 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/LivingDeathInvoker.java
  17. 48 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinCommands.java
  18. 44 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinMinecraftServer.java
  19. 37 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java
  20. 67 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerGamePacketListenerImpl.java
  21. 33 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerLevel.java
  22. 40 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientLevel.java
  23. 21 3
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinClientPacketListener.java
  24. 39 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinDebugScreenOverlay.java
  25. 66 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinScreen.java
  26. 36 0
      fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java
  27. 10 0
      fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
  28. 3 0
      fabric/src/main/resources/architectury.accessWidener
  29. 5 2
      fabric/src/main/resources/architectury.mixins.json
  30. 1 0
      fabric/src/main/resources/fabric.mod.json
  31. 111 8
      forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java
  32. 45 0
      forge/src/main/java/me/shedaniel/architectury/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java
  33. 66 0
      forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java
  34. 14 0
      forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java

+ 24 - 0
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<T> {
+    InteractionResult act(T t);
+}

+ 47 - 0
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 <T> Event<T> createInteractionResultHolder(Class<T> 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 <T> Event<Consumer<T>> createConsumerLoop(Class<T> clazz) {
+        return create(listeners -> (Consumer<T>) 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<T> listener : listeners) {
+                    method.invoke(listener, args);
+                }
+                return null;
+            }
+        }));
+    }
+    
+    @SuppressWarnings("UnstableApiUsage")
+    public static <T> Event<Actor<T>> createActorLoop(Class<T> clazz) {
+        return create(listeners -> (Actor<T>) 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<T> listener : listeners) {
+                    InteractionResult result = (InteractionResult) method.invoke(listener, args);
+                    if (result != InteractionResult.PASS) {
+                        return result;
+                    }
+                }
+                return InteractionResult.PASS;
+            }
+        }));
+    }
+    
     private static class EventImpl<T> implements Event<T> {
         private final Function<T[], T> function;
         private T invoker = null;

+ 61 - 0
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> CLIENT = EventFactory.createInteractionResultHolder(Client.class);
+    /**
+     * Invoked when client receives a message, equivalent to forge's {@code ClientChatReceivedEvent}.
+     */
+    @Environment(EnvType.CLIENT) Event<ClientReceived> CLIENT_RECEIVED = EventFactory.createInteractionResultHolder(ClientReceived.class);
+    /**
+     * Invoked when server receives a message, equivalent to forge's {@code ServerChatEvent}.
+     */
+    Event<Server> SERVER = EventFactory.createInteractionResultHolder(Server.class);
+    
+    @Environment(EnvType.CLIENT)
+    interface Client {
+        @NotNull
+        InteractionResultHolder<String> process(String message);
+    }
+    
+    @Environment(EnvType.CLIENT)
+    interface ClientReceived {
+        @NotNull
+        InteractionResultHolder<Component> process(ChatType type, Component message, @Nullable UUID sender);
+    }
+    
+    interface Server {
+        @NotNull
+        InteractionResultHolder<Component> process(String message, Component component);
+    }
+}

+ 60 - 0
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<Actor<CommandPerformEvent>> EVENT = EventFactory.createActorLoop(CommandPerformEvent.class);
+    
+    @NotNull
+    private ParseResults<CommandSourceStack> results;
+    @Nullable
+    private Throwable throwable;
+    
+    public CommandPerformEvent(@NotNull ParseResults<CommandSourceStack> results, @Nullable Throwable throwable) {
+        this.results = results;
+        this.throwable = throwable;
+    }
+    
+    @NotNull
+    public ParseResults<CommandSourceStack> getResults() {
+        return results;
+    }
+    
+    public void setResults(@NotNull ParseResults<CommandSourceStack> results) {
+        this.results = results;
+    }
+    
+    @Nullable
+    public Throwable getThrowable() {
+        return throwable;
+    }
+    
+    public void setThrowable(@Nullable Throwable throwable) {
+        this.throwable = throwable;
+    }
+}

+ 5 - 1
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<CommandRegistrationEvent> EVENT = EventFactory.createLoop(CommandRegistrationEvent.class);
     
-    void register(CommandDispatcher<CommandSourceStack> var1);
+    void register(CommandDispatcher<CommandSourceStack> dispatcher, Commands.CommandSelection selection);
 }

+ 34 - 0
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<LivingDeath> LIVING_DEATH = EventFactory.createInteractionResult(LivingDeath.class);
+    
+    interface LivingDeath {
+        InteractionResult die(LivingEntity entity, DamageSource source);
+    }
+}

+ 34 - 0
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<RenderHud> RENDER_HUD = EventFactory.createLoop(RenderHud.class);
+    Event<DebugText> DEBUG_TEXT_LEFT = EventFactory.createLoop(DebugText.class);
+    Event<DebugText> 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<ScreenInitPre> INIT_PRE = EventFactory.createInteractionResult(ScreenInitPre.class);
+    /**
+     * Invoked after Screen#init, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Post}.
+     */
+    Event<ScreenInitPost> 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<String> strings);
+    }
+    
+    @Environment(EnvType.CLIENT)
+    interface ScreenInitPre {
+        InteractionResult init(Screen screen, List<AbstractWidget> widgets, List<GuiEventListener> children);
+    }
+    
+    @Environment(EnvType.CLIENT)
+    interface ScreenInitPost {
+        void init(Screen screen, List<AbstractWidget> widgets, List<GuiEventListener> children);
+    }
 }

+ 38 - 9
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<ClientState> CLIENT_STARTED = EventFactory.createLoop(ClientState.class);
+    /**
+     * Invoked when client is initialising, not available in forge.
+     */
     @Deprecated @Environment(EnvType.CLIENT) Event<ClientState> 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<ServerState> 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<ServerState> 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<ServerState> 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<ServerState> SERVER_STOPPED = EventFactory.createLoop(ServerState.class);
-    Event<WorldLoad> WORLD_LOAD = EventFactory.createLoop(WorldLoad.class);
-    Event<WorldSave> WORLD_SAVE = EventFactory.createLoop(WorldSave.class);
+    /**
+     * Invoked after a world is loaded only on server, equivalent to forge's {@code WorldEvent.Load}.
+     */
+    Event<ServerWorldState> SERVER_WORLD_LOAD = EventFactory.createLoop(ServerWorldState.class);
+    /**
+     * Invoked during a world is saved, equivalent to forge's {@code WorldEvent.Save}.
+     */
+    Event<ServerWorldState> 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<ClientWorldState> CLIENT_WORLD_LOAD = EventFactory.createLoop(ClientWorldState.class);
     
     interface InstanceState<T> {
         void stateChanged(T instance);
@@ -47,11 +75,12 @@ public interface LifecycleEvent {
     
     interface ServerState extends InstanceState<MinecraftServer> {}
     
-    interface WorldLoad {
-        void load(ResourceKey<Level> key, ServerLevel world);
+    interface WorldState<T extends Level> {
+        void act(T world);
     }
     
-    interface WorldSave {
-        void save(ServerLevel world);
-    }
+    @Environment(EnvType.CLIENT)
+    interface ClientWorldState extends WorldState<ClientLevel> {}
+    
+    interface ServerWorldState extends WorldState<ServerLevel> {}
 }

+ 4 - 0
common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java

@@ -35,6 +35,8 @@ public interface TickEvent<T> {
     @Environment(EnvType.CLIENT) Event<ClientWorld> CLIENT_WORLD_POST = EventFactory.createLoop(ClientWorld.class);
     Event<ServerWorld> SERVER_WORLD_PRE = EventFactory.createLoop(ServerWorld.class);
     Event<ServerWorld> SERVER_WORLD_POST = EventFactory.createLoop(ServerWorld.class);
+    Event<Player> PLAYER_PRE = EventFactory.createLoop(Player.class);
+    Event<Player> PLAYER_POST = EventFactory.createLoop(Player.class);
     
     void tick(T instance);
     
@@ -49,4 +51,6 @@ public interface TickEvent<T> {
     interface ClientWorld extends WorldTick<ClientLevel> {}
     
     interface ServerWorld extends WorldTick<ServerLevel> {}
+    
+    interface Player extends TickEvent<net.minecraft.world.entity.player.Player> {}
 }

+ 51 - 0
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 extends AbstractWidget> T addButton(Screen screen, T widget) {
+        return IMPL.addButton(screen, widget);
+    }
+    
+    public static <T extends GuiEventListener> T addChild(Screen screen, T listener) {
+        return IMPL.addChild(screen, listener);
+    }
+    
+    public interface Impl {
+        <T extends AbstractWidget> T addButton(Screen screen, T widget);
+        
+        <T extends GuiEventListener> T addChild(Screen screen, T listener);
+    }
+    
+    static {
+        ArchitecturyPopulator.populate(ScreenHooks.class);
+    }
+}

+ 52 - 0
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);
+    }
+}

+ 24 - 0
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 <T> ResourceLocation getId(T object, ResourceKey<net.minecraft.core.Registry<T>> fallback) {
+        return IMPL.getId(object, fallback);
+    }
+    
+    /**
+     * Forge: If the object is {@code IForgeRegistryEntry}, use `getRegistryName`, else null
+     * Fabric: Use registry
+     */
+    @Nullable
+    public <T> ResourceLocation getId(T object, net.minecraft.core.Registry<T> fallback) {
+        return IMPL.getId(object, fallback);
+    }
+    
     public interface Impl {
         RegistryProvider get(String modId);
+        
+        <T> ResourceLocation getId(T object, ResourceKey<net.minecraft.core.Registry<T>> fallback);
+        
+        <T> ResourceLocation getId(T object, net.minecraft.core.Registry<T> fallback);
     }
     
     public interface RegistryProvider {

+ 4 - 0
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
 }

+ 2 - 1
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

+ 37 - 0
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 extends AbstractWidget> T addButton(Screen screen, T widget) {
+        return screen.addButton(widget);
+    }
+    
+    @Override
+    public <T extends GuiEventListener> T addChild(Screen screen, T listener) {
+        return screen.addWidget(listener);
+    }
+}

+ 38 - 0
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();
+        }
+    }
+}

+ 48 - 0
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<CommandSourceStack> dispatcher, StringReader input, Object source) throws CommandSyntaxException {
+        CommandSourceStack stack = (CommandSourceStack) source;
+        ParseResults<CommandSourceStack> 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());
+    }
+}

+ 44 - 0
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<ResourceKey<Level>, 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);
+        }
+    }
+}

+ 37 - 0
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);
+    }
+}

+ 67 - 0
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<Component> 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();
+        }
+    }
+}

+ 33 - 0
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);
+    }
+}

+ 40 - 0
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 = "<init>", at = @At("RETURN"))
+    private void construct(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey<Level> resourceKey, DimensionType dimensionType, int i, Supplier<ProfilerFiller> supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
+        LifecycleEvent.CLIENT_WORLD_LOAD.invoker().act((ClientLevel) (Object) this);
+    }
+}

+ 21 - 3
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<Component> 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();
+        }
+    }
 }

+ 39 - 0
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<List<String>> cir) {
+        GuiEvent.DEBUG_TEXT_LEFT.invoker().gatherText(cir.getReturnValue());
+    }
+    
+    @Inject(method = "getSystemInformation", at = @At("RETURN"))
+    private void getRightTexts(CallbackInfoReturnable<List<String>> cir) {
+        GuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(cir.getReturnValue());
+    }
+}

+ 66 - 0
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<AbstractWidget> buttons;
+    
+    @Shadow
+    public abstract List<? extends GuiEventListener> 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<GuiEventListener>) 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<GuiEventListener>) children());
+    }
+    
+    @ModifyVariable(method = "sendMessage(Ljava/lang/String;Z)V", at = @At("HEAD"), argsOnly = true, ordinal = 0)
+    private String modifyMessage(String message) {
+        InteractionResultHolder<String> process = ChatEvent.CLIENT.invoker().process(message);
+        if (process.getResult() == InteractionResult.FAIL)
+            return "";
+        if (process.getObject() != null)
+            return process.getObject();
+        return message;
+    }
+}

+ 36 - 0
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);
+    }
+}

+ 10 - 0
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 <T> ResourceLocation getId(T object, ResourceKey<net.minecraft.core.Registry<T>> fallback) {
+        return RegistryProviderImpl.INSTANCE.get(fallback).getId(object);
+    }
+    
+    @Override
+    public <T> ResourceLocation getId(T object, net.minecraft.core.Registry<T> fallback) {
+        return RegistryProviderImpl.INSTANCE.get(fallback).getId(object);
+    }
+    
     public enum RegistryProviderImpl implements Registries.RegistryProvider {
         INSTANCE;
         

+ 3 - 0
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;

+ 5 - 2
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

+ 1 - 0
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"
   }

+ 111 - 8
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<IGuiEventListener>) 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<IGuiEventListener>) 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<String> 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<ITextComponent> 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<ITextComponent> 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)

+ 45 - 0
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 extends Widget> 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 extends IGuiEventListener> 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);
+        }
+    }
+}

+ 66 - 0
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<Pair<IItemColor, IItemProvider[]>> ITEM_COLORS = Lists.newArrayList();
+    private static final List<Pair<IBlockColor, Block[]>> BLOCK_COLORS = Lists.newArrayList();
+    
+    public ColorHandlersImpl() {
+        MinecraftForge.EVENT_BUS.<ColorHandlerEvent.Item>addListener(event -> {
+            for (Pair<IItemColor, IItemProvider[]> pair : ITEM_COLORS) {
+                event.getItemColors().register(pair.getLeft(), pair.getRight());
+            }
+        });
+        MinecraftForge.EVENT_BUS.<ColorHandlerEvent.Block>addListener(event -> {
+            for (Pair<IBlockColor, Block[]> 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);
+        }
+    }
+}

+ 14 - 0
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 <T> ResourceLocation getId(T t, RegistryKey<net.minecraft.util.registry.Registry<T>> registryKey) {
+        if (t instanceof IForgeRegistryEntry)
+            return ((IForgeRegistryEntry<?>) t).getRegistryName();
+        return null;
+    }
+    
+    @Override
+    public <T> ResourceLocation getId(T t, net.minecraft.util.registry.Registry<T> 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;