Prechádzať zdrojové kódy

More events and hooks

shedaniel 4 rokov pred
rodič
commit
1d7e7adff1
22 zmenil súbory, kde vykonal 503 pridanie a 55 odobranie
  1. 2 2
      common/src/main/java/me/shedaniel/architectury/ArchitecturyPopulator.java
  2. 0 27
      common/src/main/java/me/shedaniel/architectury/event/EventFactory.java
  3. 55 0
      common/src/main/java/me/shedaniel/architectury/event/EventHandler.java
  4. 39 0
      common/src/main/java/me/shedaniel/architectury/event/events/ExplosionEvent.java
  5. 12 0
      common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
  6. 5 1
      common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java
  7. 11 0
      common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java
  8. 17 1
      common/src/main/java/me/shedaniel/architectury/registry/Registry.java
  9. 3 0
      fabric/build.gradle
  10. 6 2
      fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventHandlerImpl.java
  11. 42 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/ExplosionPreInvoker.java
  12. 1 1
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinCommands.java
  13. 13 14
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinExplosion.java
  14. 39 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayerAdvancements.java
  15. 32 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayer.java
  16. 51 0
      fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinGameRenderer.java
  17. 42 1
      fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
  18. 2 0
      fabric/src/main/java/me/shedaniel/architectury/utils/fabric/GameInstanceImpl.java
  19. 3 1
      fabric/src/main/resources/architectury.mixins.json
  20. 52 2
      forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImpl.java
  21. 1 1
      forge/src/main/java/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java
  22. 75 2
      forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java

+ 2 - 2
common/src/main/java/me/shedaniel/architectury/ArchitecturyPopulator.java

@@ -33,7 +33,7 @@ public final class ArchitecturyPopulator {
                     if (Modifier.isStatic(field.getModifiers())) {
                         FieldUtils.removeFinalModifier(field);
                         field.setAccessible(true);
-                        String type = field.getType().toString().replace("$", "");
+                        String type = field.getType().getName().replace("$", "");
                         Class<?> newClass = Class.forName(type.substring(0, type.lastIndexOf('.')) + "." + Architectury.getModLoader() + "." + type.substring(type.lastIndexOf('.') + 1));
                         field.set(null, newClass.getConstructor().newInstance());
                     }
@@ -44,7 +44,7 @@ public final class ArchitecturyPopulator {
                     if (!Modifier.isStatic(field.getModifiers())) {
                         FieldUtils.removeFinalModifier(field);
                         field.setAccessible(true);
-                        String type = field.getType().toString().replace("$", "");
+                        String type = field.getType().getName().replace("$", "");
                         Class<?> newClass = Class.forName(type.substring(0, type.lastIndexOf('.')) + "." + Architectury.getModLoader() + "." + type.substring(type.lastIndexOf('.') + 1));
                         field.set(o, newClass.getConstructor().newInstance());
                     }

+ 0 - 27
common/src/main/java/me/shedaniel/architectury/event/EventFactory.java

@@ -17,11 +17,6 @@
 package me.shedaniel.architectury.event;
 
 import com.google.common.reflect.AbstractInvocationHandler;
-import me.shedaniel.architectury.ArchitecturyPopulator;
-import me.shedaniel.architectury.Populatable;
-import me.shedaniel.architectury.platform.Platform;
-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;
@@ -38,9 +33,6 @@ import java.util.function.Function;
 public final class EventFactory {
     private EventFactory() {}
     
-    @Populatable
-    private static final Impl IMPL = null;
-    
     public static <T> Event<T> create(Function<T[], T> function) {
         Class<?>[] arguments = TypeResolver.resolveRawArguments(Function.class, function.getClass());
         return new EventImpl<>(arguments[1], function);
@@ -177,23 +169,4 @@ public final class EventFactory {
             }
         }
     }
-    
-    public interface Impl {
-        @Environment(EnvType.CLIENT)
-        void registerClient();
-        
-        void registerCommon();
-        
-        @Environment(EnvType.SERVER)
-        void registerServer();
-    }
-    
-    static {
-        ArchitecturyPopulator.populate(EventFactory.class);
-        if (Platform.getEnv() == EnvType.CLIENT)
-            IMPL.registerClient();
-        IMPL.registerCommon();
-        if (Platform.getEnv() == EnvType.SERVER)
-            IMPL.registerServer();
-    }
 }

+ 55 - 0
common/src/main/java/me/shedaniel/architectury/event/EventHandler.java

@@ -0,0 +1,55 @@
+/*
+ * 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 me.shedaniel.architectury.ArchitecturyPopulator;
+import me.shedaniel.architectury.Populatable;
+import me.shedaniel.architectury.platform.Platform;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+
+public final class EventHandler {
+    private EventHandler() {}
+    
+    @Populatable
+    private static final Impl IMPL = null;
+    private static boolean initialized = false;
+    
+    public static void init() {
+        if (initialized) return;
+        initialized = true;
+        if (Platform.getEnv() == EnvType.CLIENT)
+            IMPL.registerClient();
+        IMPL.registerCommon();
+        if (Platform.getEnv() == EnvType.SERVER)
+            IMPL.registerServer();
+    }
+    
+    public interface Impl {
+        @Environment(EnvType.CLIENT)
+        void registerClient();
+        
+        void registerCommon();
+        
+        @Environment(EnvType.SERVER)
+        void registerServer();
+    }
+    
+    static {
+        ArchitecturyPopulator.populate(EventHandler.class);
+    }
+}

+ 39 - 0
common/src/main/java/me/shedaniel/architectury/event/events/ExplosionEvent.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.event.events;
+
+import me.shedaniel.architectury.event.Event;
+import me.shedaniel.architectury.event.EventFactory;
+import net.minecraft.world.InteractionResult;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Explosion;
+import net.minecraft.world.level.Level;
+
+import java.util.List;
+
+public interface ExplosionEvent {
+    Event<Pre> PRE = EventFactory.createInteractionResult(Pre.class);
+    Event<Detonate> DETONATE = EventFactory.createInteractionResult(Detonate.class);
+    
+    interface Pre {
+        InteractionResult explode(Level world, Explosion explosion);
+    }
+    
+    interface Detonate {
+        void explode(Level world, Explosion explosion, List<Entity> affectedEntities);
+    }
+}

+ 12 - 0
common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java

@@ -44,6 +44,8 @@ public interface GuiEvent {
      * Invoked after Screen#init, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Post}.
      */
     Event<ScreenInitPost> INIT_POST = EventFactory.createLoop(ScreenInitPost.class);
+    Event<ScreenRenderPre> RENDER_PRE = EventFactory.createInteractionResult(ScreenRenderPre.class);
+    Event<ScreenRenderPost> RENDER_POST = EventFactory.createInteractionResult(ScreenRenderPost.class);
     
     @Environment(EnvType.CLIENT)
     interface RenderHud {
@@ -64,4 +66,14 @@ public interface GuiEvent {
     interface ScreenInitPost {
         void init(Screen screen, List<AbstractWidget> widgets, List<GuiEventListener> children);
     }
+    
+    @Environment(EnvType.CLIENT)
+    interface ScreenRenderPre {
+        InteractionResult render(Screen screen, PoseStack matrices, int mouseX, int mouseY, float delta);
+    }
+    
+    @Environment(EnvType.CLIENT)
+    interface ScreenRenderPost {
+        void render(Screen screen, PoseStack matrices, int mouseX, int mouseY, float delta);
+    }
 }

+ 5 - 1
common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java

@@ -53,9 +53,13 @@ public interface LifecycleEvent {
      */
     Event<ServerState> SERVER_STOPPED = EventFactory.createLoop(ServerState.class);
     /**
-     * Invoked after a world is loaded only on server, equivalent to forge's {@code WorldEvent.Load}.
+     * Invoked after a world is loaded only on server, equivalent to forge's {@code WorldEvent.Load} and fabric's {@code ServerWorldEvents#LOAD}.
      */
     Event<ServerWorldState> SERVER_WORLD_LOAD = EventFactory.createLoop(ServerWorldState.class);
+    /**
+     * Invoked after a world is unloaded, equivalent to forge's {@code WorldEvent.Unload} and fabric's {@code ServerWorldEvents#UNLOAD}.
+     */
+    Event<ServerWorldState> SERVER_WORLD_UNLOAD = EventFactory.createLoop(ServerWorldState.class);
     /**
      * Invoked during a world is saved, equivalent to forge's {@code WorldEvent.Save}.
      */

+ 11 - 0
common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java

@@ -20,6 +20,7 @@ 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.advancements.Advancement;
 import net.minecraft.client.player.LocalPlayer;
 import net.minecraft.server.level.ServerPlayer;
 
@@ -30,6 +31,8 @@ public interface PlayerEvent {
     @Environment(EnvType.CLIENT) Event<ClientPlayerJoin> CLIENT_PLAYER_JOIN = EventFactory.createLoop(ClientPlayerJoin.class);
     @Environment(EnvType.CLIENT) Event<ClientPlayerQuit> CLIENT_PLAYER_QUIT = EventFactory.createLoop(ClientPlayerQuit.class);
     @Environment(EnvType.CLIENT) Event<ClientPlayerRespawn> CLIENT_PLAYER_RESPAWN = EventFactory.createLoop(ClientPlayerRespawn.class);
+    Event<PlayerAdvancement> PLAYER_ADVANCEMENT = EventFactory.createLoop(PlayerAdvancement.class);
+    Event<PlayerClone> PLAYER_CLONE = EventFactory.createLoop(PlayerClone.class);
     
     interface PlayerJoin {
         void join(ServerPlayer player);
@@ -43,6 +46,14 @@ public interface PlayerEvent {
         void respawn(ServerPlayer newPlayer, boolean conqueredEnd);
     }
     
+    interface PlayerClone {
+        void clone(ServerPlayer oldPlayer, ServerPlayer newPlayer, boolean wonGame);
+    }
+    
+    interface PlayerAdvancement {
+        void award(ServerPlayer player, Advancement advancement);
+    }
+    
     @Environment(EnvType.CLIENT)
     interface ClientPlayerJoin {
         void join(LocalPlayer player);

+ 17 - 1
common/src/main/java/me/shedaniel/architectury/registry/Registry.java

@@ -16,12 +16,16 @@
 
 package me.shedaniel.architectury.registry;
 
+import net.minecraft.resources.ResourceKey;
 import net.minecraft.resources.ResourceLocation;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
 import java.util.function.Supplier;
 
-public interface Registry<T> {
+public interface Registry<T> extends Iterable<T> {
     Supplier<T> delegate(ResourceLocation id);
     
     Supplier<T> register(ResourceLocation id, Supplier<T> supplier);
@@ -29,6 +33,18 @@ public interface Registry<T> {
     @Nullable
     ResourceLocation getId(T obj);
     
+    Optional<ResourceKey<T>> getKey(T obj);
+    
     @Nullable
     T get(ResourceLocation id);
+    
+    boolean contains(ResourceLocation id);
+    
+    boolean containsValue(T obj);
+    
+    Set<ResourceLocation> getIds();
+    
+    Set<Map.Entry<ResourceKey<T>, T>> entrySet();
+    
+    ResourceKey<? extends net.minecraft.core.Registry<T>> key();
 }

+ 3 - 0
fabric/build.gradle

@@ -17,6 +17,8 @@ dependencies {
     modCompile("net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}")
     modCompile("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}")
     modCompileOnly("io.github.prospector:modmenu:${rootProject.mod_menu_version}")
+    implementation "net.jodah:typetools:0.6.2"
+    shadow "net.jodah:typetools:0.6.2"
 
     compile(project(":common")) {
         transitive = false
@@ -34,6 +36,7 @@ processResources {
 }
 
 shadowJar {
+    relocate "net.jodah.typetools", "me.shedaniel.architectury.shadowed.impl.net.jodah.typetools"
     configurations = [project.configurations.shadow]
     classifier "shadow"
 }

+ 6 - 2
fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java → fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventHandlerImpl.java

@@ -16,7 +16,7 @@
 
 package me.shedaniel.architectury.event.fabric;
 
-import me.shedaniel.architectury.event.EventFactory;
+import me.shedaniel.architectury.event.EventHandler;
 import me.shedaniel.architectury.event.events.*;
 import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
 import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
@@ -25,9 +25,10 @@ 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.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
 import net.minecraft.commands.Commands;
 
-public class EventFactoryImpl implements EventFactory.Impl {
+public class EventHandlerImpl implements EventHandler.Impl {
     @Override
     public void registerClient() {
         ClientLifecycleEvents.CLIENT_STARTED.register(LifecycleEvent.CLIENT_STARTED.invoker()::stateChanged);
@@ -54,6 +55,9 @@ 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);
         
+        ServerWorldEvents.LOAD.register((server, world) -> LifecycleEvent.SERVER_WORLD_LOAD.invoker().act(world));
+        ServerWorldEvents.UNLOAD.register((server, world) -> LifecycleEvent.SERVER_WORLD_UNLOAD.invoker().act(world));
+        
         CommandRegistrationCallback.EVENT.register((commandDispatcher, b) -> CommandRegistrationEvent.EVENT.invoker().register(commandDispatcher, b ? Commands.CommandSelection.DEDICATED : Commands.CommandSelection.INTEGRATED));
     }
     

+ 42 - 0
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/ExplosionPreInvoker.java

@@ -0,0 +1,42 @@
+/*
+ * 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.ExplosionEvent;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.InteractionResult;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Explosion;
+import net.minecraft.world.level.ExplosionDamageCalculator;
+import net.minecraft.world.level.Level;
+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 org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+@Mixin(value = {Level.class, ServerLevel.class})
+public class ExplosionPreInvoker {
+    @Inject(method = "explode(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;Lnet/minecraft/world/level/ExplosionDamageCalculator;DDDFZLnet/minecraft/world/level/Explosion$BlockInteraction;)Lnet/minecraft/world/level/Explosion;", 
+            at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Explosion;explode()V"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD)
+    private void explodePre(Entity entity, DamageSource damageSource, ExplosionDamageCalculator explosionDamageCalculator, double d, double e, double f, float g, boolean bl, Explosion.BlockInteraction blockInteraction, CallbackInfoReturnable<Explosion> cir, Explosion explosion) {
+        if (ExplosionEvent.PRE.invoker().explode((Level)(Object) this, explosion) == InteractionResult.FAIL) {
+            cir.setReturnValue(explosion);
+        }
+    }
+}

+ 1 - 1
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinCommands.java

@@ -32,7 +32,7 @@ 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"))
+              at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/CommandDispatcher;execute(Lcom/mojang/brigadier/StringReader;Ljava/lang/Object;)I", remap = false))
     private int performCommand(CommandDispatcher<CommandSourceStack> dispatcher, StringReader input, Object source) throws CommandSyntaxException {
         CommandSourceStack stack = (CommandSourceStack) source;
         ParseResults<CommandSourceStack> parse = dispatcher.parse(input, stack);

+ 13 - 14
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinMinecraftServer.java → fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinExplosion.java

@@ -16,11 +16,10 @@
 
 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 me.shedaniel.architectury.event.events.ExplosionEvent;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Explosion;
 import net.minecraft.world.level.Level;
 import org.spongepowered.asm.mixin.Final;
 import org.spongepowered.asm.mixin.Mixin;
@@ -28,17 +27,17 @@ 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 org.spongepowered.asm.mixin.injection.callback.LocalCapture;
 
-import java.util.Map;
+import java.util.List;
+import java.util.Set;
 
-@Mixin(MinecraftServer.class)
-public class MixinMinecraftServer {
-    @Shadow @Final private Map<ResourceKey<Level>, ServerLevel> levels;
+@Mixin(Explosion.class)
+public class MixinExplosion {
+    @Shadow @Final private Level level;
     
-    @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);
-        }
+    @Inject(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;<init>(DDD)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD)
+    private void explodePost(CallbackInfo ci, Set<BlockPos> set, float q, int r, int s, int t, int u, int v, int w, List<Entity> list) {
+        ExplosionEvent.DETONATE.invoker().explode(level, (Explosion) (Object) this, list);
     }
 }

+ 39 - 0
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayerAdvancements.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;
+
+import me.shedaniel.architectury.event.events.PlayerEvent;
+import net.minecraft.advancements.Advancement;
+import net.minecraft.server.PlayerAdvancements;
+import net.minecraft.server.level.ServerPlayer;
+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.CallbackInfoReturnable;
+
+@Mixin(PlayerAdvancements.class)
+public class MixinPlayerAdvancements {
+    @Shadow private ServerPlayer player;
+    
+    @Inject(method = "award",
+            at = @At(value = "INVOKE", target = "Lnet/minecraft/advancements/AdvancementRewards;grant(Lnet/minecraft/server/level/ServerPlayer;)V",
+                     shift = At.Shift.AFTER))
+    private void award(Advancement advancement, String string, CallbackInfoReturnable<Boolean> cir) {
+        PlayerEvent.PLAYER_ADVANCEMENT.invoker().award(player, advancement);
+    }
+}

+ 32 - 0
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayer.java

@@ -0,0 +1,32 @@
+/*
+ * 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.PlayerEvent;
+import net.minecraft.server.level.ServerPlayer;
+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(ServerPlayer.class)
+public class MixinServerPlayer {
+    @Inject(method = "restoreFrom", at = @At("RETURN"))
+    private void restoreFrom(ServerPlayer serverPlayer, boolean bl, CallbackInfo ci) {
+        PlayerEvent.PLAYER_CLONE.invoker().clone((ServerPlayer) (Object) this, serverPlayer, bl);
+    }
+}

+ 51 - 0
fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinGameRenderer.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.mixin.fabric.client;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import me.shedaniel.architectury.event.events.GuiEvent;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.world.InteractionResult;
+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 org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+@Mixin(GameRenderer.class)
+public abstract class MixinGameRenderer {
+    @Shadow @Final private Minecraft minecraft;
+    
+    @Inject(method = "render(FJZ)V",
+            at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;render(Lcom/mojang/blaze3d/vertex/PoseStack;IIF)V",
+                     ordinal = 0), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true)
+    public void renderScreenPre(float tickDelta, long startTime, boolean tick, CallbackInfo ci, int mouseX, int mouseY, PoseStack matrices) {
+        if (GuiEvent.RENDER_PRE.invoker().render(minecraft.screen, matrices, mouseX, mouseY, minecraft.getDeltaFrameTime()) == InteractionResult.FAIL) {
+            ci.cancel();
+        }
+    }
+    
+    @Inject(method = "render(FJZ)V",
+            at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;render(Lcom/mojang/blaze3d/vertex/PoseStack;IIF)V",
+                     shift = At.Shift.AFTER, ordinal = 0), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
+    public void renderScreenPost(float tickDelta, long startTime, boolean tick, CallbackInfo ci, int mouseX, int mouseY, PoseStack matrices) {
+        GuiEvent.RENDER_POST.invoker().render(minecraft.screen, matrices, mouseX, mouseY, minecraft.getDeltaFrameTime());
+    }
+}

+ 42 - 1
fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java

@@ -21,8 +21,13 @@ import me.shedaniel.architectury.registry.Registry;
 import net.minecraft.resources.ResourceKey;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.util.LazyLoadedValue;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
 import java.util.function.Supplier;
 
 public class RegistriesImpl implements Registries.Impl {
@@ -78,10 +83,46 @@ public class RegistriesImpl implements Registries.Impl {
         public @Nullable ResourceLocation getId(T obj) {
             return delegate.getKey(obj);
         }
-        
+    
+        @Override
+        public Optional<ResourceKey<T>> getKey(T obj) {
+            return delegate.getResourceKey(obj);
+        }
+    
         @Override
         public @Nullable T get(ResourceLocation id) {
             return delegate.get(id);
         }
+    
+        @Override
+        public boolean contains(ResourceLocation id) {
+            return delegate.containsKey(id);
+        }
+    
+        @Override
+        public boolean containsValue(T obj) {
+            return delegate.getResourceKey(obj).isPresent();
+        }
+    
+        @Override
+        public Set<ResourceLocation> getIds() {
+            return delegate.keySet();
+        }
+    
+        @Override
+        public Set<Map.Entry<ResourceKey<T>, T>> entrySet() {
+            return delegate.entrySet();
+        }
+    
+        @Override
+        public ResourceKey<? extends net.minecraft.core.Registry<T>> key() {
+            return delegate.key();
+        }
+    
+        @NotNull
+        @Override
+        public Iterator<T> iterator() {
+            return delegate.iterator();
+        }
     }
 }

+ 2 - 0
fabric/src/main/java/me/shedaniel/architectury/utils/fabric/GameInstanceImpl.java

@@ -16,6 +16,7 @@
 
 package me.shedaniel.architectury.utils.fabric;
 
+import me.shedaniel.architectury.event.EventHandler;
 import me.shedaniel.architectury.event.events.LifecycleEvent;
 import me.shedaniel.architectury.platform.Platform;
 import me.shedaniel.architectury.utils.GameInstance;
@@ -38,6 +39,7 @@ public class GameInstanceImpl implements GameInstance.Impl {
     }
     
     public static void init() {
+        EventHandler.init();
         LifecycleEvent.SERVER_STARTING.register(server -> GameInstanceImpl.server = server);
         LifecycleEvent.SERVER_STOPPED.register(server -> GameInstanceImpl.server = null);
     }

+ 3 - 1
fabric/src/main/resources/architectury.mixins.json

@@ -7,11 +7,13 @@
     "client.MixinClientLevel",
     "client.MixinClientPacketListener",
     "client.MixinDebugScreenOverlay",
+    "client.MixinGameRenderer",
     "client.MixinMinecraft",
     "client.MixinScreen"
   ],
   "mixins": [
-    "LivingDeathInvoker", "MixinCommands", "MixinMinecraftServer", "MixinPlayer", "MixinPlayerList", "MixinServerGamePacketListenerImpl", "MixinServerLevel"
+    "ExplosionPreInvoker", "LivingDeathInvoker", "MixinCommands", "MixinExplosion", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList",
+    "MixinServerGamePacketListenerImpl", "MixinServerLevel", "MixinServerPlayer"
   ],
   "injectors": {
     "defaultRequire": 1

+ 52 - 2
forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java → forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImpl.java

@@ -16,7 +16,7 @@
 
 package me.shedaniel.architectury.event.forge;
 
-import me.shedaniel.architectury.event.EventFactory;
+import me.shedaniel.architectury.event.EventHandler;
 import me.shedaniel.architectury.event.events.*;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.IGuiEventListener;
@@ -35,10 +35,14 @@ import net.minecraftforge.event.RegisterCommandsEvent;
 import net.minecraftforge.event.ServerChatEvent;
 import net.minecraftforge.event.TickEvent.*;
 import net.minecraftforge.event.entity.living.LivingDeathEvent;
+import net.minecraftforge.event.entity.player.AdvancementEvent;
 import net.minecraftforge.event.entity.player.ItemTooltipEvent;
+import net.minecraftforge.event.entity.player.PlayerEvent.Clone;
 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.ExplosionEvent.Detonate;
+import net.minecraftforge.event.world.ExplosionEvent.Start;
 import net.minecraftforge.event.world.WorldEvent;
 import net.minecraftforge.eventbus.api.SubscribeEvent;
 import net.minecraftforge.fml.LogicalSide;
@@ -50,7 +54,7 @@ import net.minecraftforge.fml.server.ServerLifecycleHooks;
 
 import java.util.List;
 
-public class EventFactoryImpl implements EventFactory.Impl {
+public class EventHandlerImpl implements EventHandler.Impl {
     @Override
     public void registerClient() {
         MinecraftForge.EVENT_BUS.register(Client.class);
@@ -145,6 +149,18 @@ public class EventFactoryImpl implements EventFactory.Impl {
                 LifecycleEvent.CLIENT_WORLD_LOAD.invoker().act(world);
             }
         }
+        
+        @SubscribeEvent
+        public static void event(GuiScreenEvent.DrawScreenEvent.Pre event) {
+            if (GuiEvent.RENDER_PRE.invoker().render(event.getGui(), event.getMatrixStack(), event.getMouseX(), event.getMouseY(), event.getRenderPartialTicks()) == ActionResultType.FAIL) {
+                event.setCanceled(true);
+            }
+        }
+        
+        @SubscribeEvent
+        public static void event(GuiScreenEvent.DrawScreenEvent.Post event) {
+            GuiEvent.RENDER_POST.invoker().render(event.getGui(), event.getMatrixStack(), event.getMouseX(), event.getMouseY(), event.getRenderPartialTicks());
+        }
     }
     
     public static class Common {
@@ -242,6 +258,14 @@ public class EventFactoryImpl implements EventFactory.Impl {
             }
         }
         
+        @SubscribeEvent
+        public static void event(WorldEvent.Unload event) {
+            if (event.getWorld() instanceof ServerWorld) {
+                ServerWorld world = (ServerWorld) event.getWorld();
+                LifecycleEvent.SERVER_WORLD_UNLOAD.invoker().act(world);
+            }
+        }
+        
         @SubscribeEvent
         public static void event(WorldEvent.Save event) {
             if (event.getWorld() instanceof ServerWorld) {
@@ -256,6 +280,32 @@ public class EventFactoryImpl implements EventFactory.Impl {
                 event.setCanceled(true);
             }
         }
+        
+        @SubscribeEvent
+        public static void event(AdvancementEvent event) {
+            if (event.getPlayer() instanceof ServerPlayerEntity) {
+                PlayerEvent.PLAYER_ADVANCEMENT.invoker().award((ServerPlayerEntity) event.getPlayer(), event.getAdvancement());
+            }
+        }
+        
+        @SubscribeEvent
+        public static void event(Clone event) {
+            if (event.getOriginal() instanceof ServerPlayerEntity && event.getPlayer() instanceof ServerPlayerEntity) {
+                PlayerEvent.PLAYER_CLONE.invoker().clone((ServerPlayerEntity) event.getOriginal(), (ServerPlayerEntity) event.getPlayer(), !event.isWasDeath());
+            }
+        }
+        
+        @SubscribeEvent
+        public static void event(Start event) {
+            if (ExplosionEvent.PRE.invoker().explode(event.getWorld(), event.getExplosion()) == ActionResultType.FAIL) {
+                event.setCanceled(true);
+            }
+        }
+        
+        @SubscribeEvent
+        public static void event(Detonate event) {
+            ExplosionEvent.DETONATE.invoker().explode(event.getWorld(), event.getExplosion(), event.getAffectedEntities());
+        }
     }
     
     @OnlyIn(Dist.DEDICATED_SERVER)

+ 1 - 1
forge/src/main/java/me/shedaniel/architectury/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java → forge/src/main/java/me/shedaniel/architectury/hooks/forge/ScreenHooksImpl.java

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package me.shedaniel.architectury.me.shedaniel.architectury.hooks.forge;
+package me.shedaniel.architectury.hooks.forge;
 
 import me.shedaniel.architectury.hooks.ScreenHooks;
 import net.minecraft.client.gui.IGuiEventListener;

+ 75 - 2
forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java

@@ -34,7 +34,10 @@ import net.minecraftforge.registries.RegistryManager;
 
 import javax.annotation.Nullable;
 import java.lang.reflect.Type;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
 import java.util.function.Supplier;
 
 public class RegistriesImpl implements Registries.Impl {
@@ -119,12 +122,47 @@ public class RegistriesImpl implements Registries.Impl {
         public ResourceLocation getId(T obj) {
             return delegate.getKey(obj);
         }
-        
+    
+        @Override
+        public Optional<RegistryKey<T>> getKey(T t) {
+            return delegate.getResourceKey(t);
+        }
+    
         @Override
         @Nullable
         public T get(ResourceLocation id) {
             return delegate.get(id);
         }
+    
+        @Override
+        public boolean contains(ResourceLocation resourceLocation) {
+            return delegate.containsKey(resourceLocation);
+        }
+    
+        @Override
+        public boolean containsValue(T t) {
+            return delegate.getResourceKey(t).isPresent();
+        }
+    
+        @Override
+        public Set<ResourceLocation> getIds() {
+            return delegate.keySet();
+        }
+    
+        @Override
+        public Set<Map.Entry<RegistryKey<T>, T>> entrySet() {
+            return delegate.entrySet();
+        }
+    
+        @Override
+        public RegistryKey<? extends net.minecraft.util.registry.Registry<T>> key() {
+            return delegate.key();
+        }
+    
+        @Override
+        public Iterator<T> iterator() {
+            return delegate.iterator();
+        }
     }
     
     public static class ForgeBackedRegistryImpl<T extends IForgeRegistryEntry<T>> implements Registry<T> {
@@ -154,11 +192,46 @@ public class RegistriesImpl implements Registries.Impl {
         public ResourceLocation getId(T obj) {
             return delegate.getKey(obj);
         }
-        
+    
+        @Override
+        public Optional<RegistryKey<T>> getKey(T t) {
+            return Optional.ofNullable(getId(t)).map(id -> RegistryKey.create(key(), id));
+        }
+    
         @Override
         @Nullable
         public T get(ResourceLocation id) {
             return delegate.getValue(id);
         }
+    
+        @Override
+        public boolean contains(ResourceLocation resourceLocation) {
+            return delegate.containsKey(resourceLocation);
+        }
+    
+        @Override
+        public boolean containsValue(T t) {
+            return delegate.containsValue(t);
+        }
+    
+        @Override
+        public Set<ResourceLocation> getIds() {
+            return delegate.getKeys();
+        }
+    
+        @Override
+        public Set<Map.Entry<RegistryKey<T>, T>> entrySet() {
+            return delegate.getEntries();
+        }
+    
+        @Override
+        public RegistryKey<? extends net.minecraft.util.registry.Registry<T>> key() {
+            return RegistryKey.createRegistryKey(delegate.getRegistryName());
+        }
+    
+        @Override
+        public Iterator<T> iterator() {
+            return delegate.iterator();
+        }
     }
 }