From e28f43db8739cfed46083d1797ac19c05c24b5dc Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 13 Jan 2021 20:39:26 +0800 Subject: [PATCH 01/45] Fix MenuRegistry crash, thanks @OroArmor --- build.gradle | 2 +- .../architectury/registry/fabric/MenuRegistryImpl.java | 2 +- .../architectury/registry/forge/MenuRegistryImpl.java | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 3d3517c5..ee692f25 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "architectury-plugin" version "1.3.47" - id "forgified-fabric-loom" version "0.5.29" apply false + id "forgified-fabric-loom" version "0.5.30" apply false id "org.cadixdev.licenser" version "0.5.0" id "com.jfrog.bintray" version "1.8.4" id "com.matthewprenger.cursegradle" version "1.4.0" apply false diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/MenuRegistryImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/MenuRegistryImpl.java index 066954a2..2b66236a 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/MenuRegistryImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/MenuRegistryImpl.java @@ -40,7 +40,7 @@ import net.minecraft.world.inventory.MenuType; import org.jetbrains.annotations.Nullable; public class MenuRegistryImpl { - public static void openMenu(ServerPlayer player, ExtendedMenuProvider provider) { + public static void openExtendedMenu(ServerPlayer player, ExtendedMenuProvider provider) { player.openMenu(new ExtendedScreenHandlerFactory() { @Override public void writeScreenOpeningData(ServerPlayer player, FriendlyByteBuf buf) { diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/MenuRegistryImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/MenuRegistryImpl.java index 019cd51d..dc04742a 100644 --- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/MenuRegistryImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/MenuRegistryImpl.java @@ -35,15 +35,15 @@ import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.fml.network.NetworkHooks; public class MenuRegistryImpl { - public static void openMenu(ServerPlayer player, ExtendedMenuProvider provider) { + public static void openExtendedMenu(ServerPlayer player, ExtendedMenuProvider provider) { NetworkHooks.openGui(player, provider, provider::saveExtraData); } - public static MenuType registerMenuType(SimpleMenuTypeFactory factory) { + public static MenuType of(SimpleMenuTypeFactory factory) { return new MenuType<>(factory::create); } - public static MenuType registerExtendedMenuType(ExtendedMenuTypeFactory factory) { + public static MenuType ofExtended(ExtendedMenuTypeFactory factory) { return IForgeContainerType.create(factory::create); } From 50352219e52838b088b980fcf2ba9292d2ae5550 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 16 Jan 2021 01:39:03 +0800 Subject: [PATCH 02/45] ArchitecturyBlockEntity to sync data to client (#10) * ArchitecturyBlockEntity to sync data to client * Add remap = false to @Shadow * Upgrade gradle plugins * Fix ArchitecturyBlockEntity on Forge * Rename ArchitecturyBlockEntity to BlockEntityExtension --- .../extensions/BlockEntityExtension.java | 54 +++++++++++++++++ .../architectury/hooks/BlockEntityHooks.java | 35 +++++++++++ .../hooks/fabric/BlockEntityHooksImpl.java | 58 +++++++++++++++++++ .../fabric/MixinBlockEntityExtension.java | 54 +++++++++++++++++ .../main/resources/architectury.mixins.json | 6 +- .../hooks/forge/BlockEntityHooksImpl.java | 52 +++++++++++++++++ .../mixin/forge/MixinBlockEntity.java | 52 +++++++++++++++++ .../forge/MixinBlockEntityExtension.java | 39 +++++++++++++ .../main/resources/architectury.mixins.json | 2 +- 9 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 common/src/main/java/me/shedaniel/architectury/extensions/BlockEntityExtension.java create mode 100644 common/src/main/java/me/shedaniel/architectury/hooks/BlockEntityHooks.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/BlockEntityHooksImpl.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockEntityExtension.java create mode 100644 forge/src/main/java/me/shedaniel/architectury/hooks/forge/BlockEntityHooksImpl.java create mode 100644 forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntity.java create mode 100644 forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntityExtension.java diff --git a/common/src/main/java/me/shedaniel/architectury/extensions/BlockEntityExtension.java b/common/src/main/java/me/shedaniel/architectury/extensions/BlockEntityExtension.java new file mode 100644 index 00000000..0add5bb1 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/extensions/BlockEntityExtension.java @@ -0,0 +1,54 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.extensions; + +import me.shedaniel.architectury.hooks.BlockEntityHooks; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Extensions to {@link net.minecraft.world.level.block.entity.BlockEntity}, implement this on to your class. + */ +public interface BlockEntityExtension { + /** + * Handles data sent by {@link BlockEntityExtension#saveClientData(CompoundTag)} on the server. + */ + @Environment(EnvType.CLIENT) + void loadClientData(@NotNull BlockState pos, @NotNull CompoundTag tag); + + /** + * Writes data to sync to the client. + */ + @NotNull + CompoundTag saveClientData(@NotNull CompoundTag tag); + + /** + * Sync data to the clients by {@link BlockEntityExtension#saveClientData(CompoundTag)} and {@link BlockEntityExtension#loadClientData(BlockState, CompoundTag)}. + */ + @ApiStatus.NonExtendable + default void syncData() { + BlockEntityHooks.syncData((BlockEntity) this); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/BlockEntityHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/BlockEntityHooks.java new file mode 100644 index 00000000..c471c9e9 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/hooks/BlockEntityHooks.java @@ -0,0 +1,35 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.hooks; + +import me.shedaniel.architectury.ExpectPlatform; +import net.minecraft.world.level.block.entity.BlockEntity; + +public class BlockEntityHooks { + private BlockEntityHooks() {} + + /** + * Sync data to the clients. + */ + @ExpectPlatform + public static void syncData(BlockEntity entity) { + throw new AssertionError(); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/BlockEntityHooksImpl.java b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/BlockEntityHooksImpl.java new file mode 100644 index 00000000..e2c8c284 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/BlockEntityHooksImpl.java @@ -0,0 +1,58 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.hooks.fabric; + +import com.google.common.base.Preconditions; +import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.Objects; + +public class BlockEntityHooksImpl { + /* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * 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. + */ + public static void syncData(BlockEntity entity) { + if (entity instanceof BlockEntityClientSerializable) { + ((BlockEntityClientSerializable) entity).sync(); + } else { + Level world = Objects.requireNonNull(entity.getLevel()); + if (!(world instanceof ServerLevel)) { + throw new IllegalStateException("Cannot call sync() on the logical client! Did you check world.isClient first?"); + } else { + ((ServerLevel) world).getChunkSource().blockChanged(entity.getBlockPos()); + } + } + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockEntityExtension.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockEntityExtension.java new file mode 100644 index 00000000..0fb45747 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockEntityExtension.java @@ -0,0 +1,54 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.mixin.fabric; + +import me.shedaniel.architectury.extensions.BlockEntityExtension; +import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(BlockEntityExtension.class) +public interface MixinBlockEntityExtension extends BlockEntityClientSerializable { + @Shadow(remap = false) + @NotNull + CompoundTag saveClientData(@NotNull CompoundTag tag); + + @Shadow(remap = false) + void loadClientData(@NotNull BlockState pos, @NotNull CompoundTag tag); + + @Override + default void fromClientTag(CompoundTag tag) { + BlockEntity entity = (BlockEntity) this; + if (entity.hasLevel()) { + entity.setLevelAndPosition(entity.getLevel(), new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"))); + loadClientData(entity.getBlockState(), tag); + } + } + + @Override + default CompoundTag toClientTag(CompoundTag tag) { + return saveClientData(tag); + } +} diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index 39fd628e..33df1a24 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -9,9 +9,9 @@ "client.MixinTextureAtlas" ], "mixins": [ - "ExplosionPreInvoker", "LivingDeathInvoker", "MixinBlockItem", "MixinCommands", "MixinDedicatedServer", "MixinExplosion", "MixinFurnaceResultSlot", - "MixinItemEntity", "MixinLivingEntity", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot", "MixinServerGamePacketListenerImpl", - "MixinServerLevel", "MixinServerPlayer", "MixinServerPlayerGameMode", "PlayerAttackInvoker" + "ExplosionPreInvoker", "LivingDeathInvoker", "MixinBlockEntityExtension", "MixinBlockItem", "MixinCommands", "MixinDedicatedServer", "MixinExplosion", + "MixinFurnaceResultSlot", "MixinItemEntity", "MixinLivingEntity", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot", + "MixinServerGamePacketListenerImpl", "MixinServerLevel", "MixinServerPlayer", "MixinServerPlayerGameMode", "PlayerAttackInvoker" ], "injectors": { "defaultRequire": 1 diff --git a/forge/src/main/java/me/shedaniel/architectury/hooks/forge/BlockEntityHooksImpl.java b/forge/src/main/java/me/shedaniel/architectury/hooks/forge/BlockEntityHooksImpl.java new file mode 100644 index 00000000..3135ff24 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/hooks/forge/BlockEntityHooksImpl.java @@ -0,0 +1,52 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.hooks.forge; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.Objects; + +public class BlockEntityHooksImpl { + /* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * 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. + */ + public static void syncData(BlockEntity entity) { + Level world = Objects.requireNonNull(entity.getLevel()); + if (!(world instanceof ServerLevel)) { + throw new IllegalStateException("Cannot call sync() on the logical client! Did you check world.isClient first?"); + } else { + ((ServerLevel) world).getChunkSource().blockChanged(entity.getBlockPos()); + } + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntity.java b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntity.java new file mode 100644 index 00000000..ad0de1c0 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntity.java @@ -0,0 +1,52 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.mixin.forge; + +import me.shedaniel.architectury.extensions.BlockEntityExtension; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(BlockEntity.class) +public abstract class MixinBlockEntity { + @Inject(method = "getUpdatePacket", at = @At("HEAD"), cancellable = true) + public void getUpdatePacket(CallbackInfoReturnable cir) { + if (this instanceof BlockEntityExtension) { + BlockEntityExtension entity = (BlockEntityExtension) this; + BlockEntity be = (BlockEntity) entity; + cir.setReturnValue(new ClientboundBlockEntityDataPacket(be.getBlockPos(), 10, be.getUpdateTag())); + cir.cancel(); + } + } + + @Inject(method = "getUpdateTag", at = @At("HEAD"), cancellable = true) + public void getUpdateTag(CallbackInfoReturnable cir) { + if (this instanceof BlockEntityExtension) { + BlockEntityExtension entity = (BlockEntityExtension) this; + BlockEntity be = (BlockEntity) entity; + cir.setReturnValue(entity.saveClientData(new CompoundTag())); + cir.cancel(); + } + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntityExtension.java b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntityExtension.java new file mode 100644 index 00000000..fea8725e --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinBlockEntityExtension.java @@ -0,0 +1,39 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.mixin.forge; + +import me.shedaniel.architectury.extensions.BlockEntityExtension; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.extensions.IForgeTileEntity; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(BlockEntityExtension.class) +public interface MixinBlockEntityExtension extends IForgeTileEntity { + @Shadow(remap = false) + void loadClientData(@NotNull BlockState pos, @NotNull CompoundTag tag); + + @Override + default void handleUpdateTag(BlockState state, CompoundTag tag) { + loadClientData(state, tag); + } +} diff --git a/forge/src/main/resources/architectury.mixins.json b/forge/src/main/resources/architectury.mixins.json index c3391c94..f3eddc58 100644 --- a/forge/src/main/resources/architectury.mixins.json +++ b/forge/src/main/resources/architectury.mixins.json @@ -5,7 +5,7 @@ "minVersion": "0.8", "client": [ ], - "mixins": ["BiomeGenerationSettingsBuilderAccessor", "MobSpawnSettingsBuilderAccessor"], + "mixins": ["BiomeGenerationSettingsBuilderAccessor", "MixinBlockEntity", "MixinBlockEntityExtension", "MobSpawnSettingsBuilderAccessor"], "injectors": { "defaultRequire": 1 } From b066d19de87252a6dc3551b56f11800d6467def0 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 16 Jan 2021 02:30:41 +0800 Subject: [PATCH 03/45] Add AbstractRecipeSerializer --- .../core/AbstractRecipeSerializer.java | 10 +++ .../forge/MixinAbstractRecipeSerializer.java | 8 +++ .../plugin/forge/ArchitecturyMixinPlugin.java | 72 +++++++++++++++++++ .../main/resources/architectury.mixins.json | 6 +- 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java create mode 100644 forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java create mode 100644 forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java diff --git a/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java new file mode 100644 index 00000000..d62ea842 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java @@ -0,0 +1,10 @@ +package me.shedaniel.architectury.core; + +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeSerializer; + +/** + * The equivalent of {@link RecipeSerializer} to use in common that has forge registry entries extended. + */ +public abstract class AbstractRecipeSerializer> implements RecipeSerializer { +} diff --git a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java new file mode 100644 index 00000000..8662416d --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java @@ -0,0 +1,8 @@ +package me.shedaniel.architectury.mixin.forge; + +import me.shedaniel.architectury.core.AbstractRecipeSerializer; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(AbstractRecipeSerializer.class) +public class MixinAbstractRecipeSerializer { +} diff --git a/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java new file mode 100644 index 00000000..43d1b8b9 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java @@ -0,0 +1,72 @@ +package me.shedaniel.architectury.plugin.forge; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class ArchitecturyMixinPlugin implements IMixinConfigPlugin { + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + // Inject our own sugar + switch (mixinClassName) { + case "me.shedaniel.architectury.mixin.forge.MixinAbstractRecipeSerializer": + targetClass.superName = "net/minecraftforge/registries/ForgeRegistryEntry"; + for (MethodNode method : targetClass.methods) { + if (Objects.equals(method.name, "")) { + for (AbstractInsnNode insnNode : method.instructions) { + if (insnNode.getOpcode() == Opcodes.INVOKESPECIAL && insnNode instanceof MethodInsnNode) { + MethodInsnNode node = (MethodInsnNode) insnNode; + if (Objects.equals(node.name, "") && Objects.equals(node.owner, "java/lang/Object")) { + node.owner = "net/minecraftforge/registries/ForgeRegistryEntry"; + break; + } + } + } + } + } + String recipeSerializer = targetClass.interfaces.get(0); + if (targetClass.signature != null) { + targetClass.signature = targetClass.signature.replace("Ljava/lang/Object;", "Lnet/minecraftforge/registries/ForgeRegistryEntry;>"); + } + break; + } + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/forge/src/main/resources/architectury.mixins.json b/forge/src/main/resources/architectury.mixins.json index f3eddc58..8408592c 100644 --- a/forge/src/main/resources/architectury.mixins.json +++ b/forge/src/main/resources/architectury.mixins.json @@ -1,11 +1,15 @@ { "required": true, "package": "me.shedaniel.architectury.mixin.forge", + "plugin": "me.shedaniel.architectury.plugin.forge.ArchitecturyMixinPlugin", "compatibilityLevel": "JAVA_8", "minVersion": "0.8", "client": [ ], - "mixins": ["BiomeGenerationSettingsBuilderAccessor", "MixinBlockEntity", "MixinBlockEntityExtension", "MobSpawnSettingsBuilderAccessor"], + "mixins": [ + "BiomeGenerationSettingsBuilderAccessor", "MixinAbstractRecipeSerializer", "MixinBlockEntity", "MixinBlockEntityExtension", + "MobSpawnSettingsBuilderAccessor" + ], "injectors": { "defaultRequire": 1 } From 9c6101835d46d5d5565ecfc177d61507548bc723 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 20 Jan 2021 20:28:15 +0800 Subject: [PATCH 04/45] Close #18 Currently testing item / block addition, keybinds and creative tabs. --- build.gradle | 17 +++++-- common/build.gradle | 3 +- .../core/AbstractRecipeSerializer.java | 19 +++++++ .../events/client/ClientPlayerEvent.java | 3 +- .../registry/RegistrySupplier.java | 9 ++++ fabric/build.gradle | 24 ++++----- .../mixin/fabric/client/MixinMinecraft.java | 4 +- forge/build.gradle | 16 +++--- .../forge/MixinAbstractRecipeSerializer.java | 19 +++++++ .../plugin/forge/ArchitecturyMixinPlugin.java | 19 +++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- logs/latest.log | 0 settings.gradle | 3 ++ testmod-common/build.gradle | 12 +++++ .../shedaniel/architectury/test/TestMod.java | 42 ++++++++++++++++ .../test/debug}/ConsoleMessageSink.java | 2 +- .../architectury/test/debug/DebugEvents.java | 16 +++--- .../architectury/test/debug}/MessageSink.java | 2 +- .../client/ClientOverlayMessageSink.java | 13 +++-- .../test/registry/TestRegistries.java | 49 +++++++++++++++++++ .../test/registry/client/TestKeybinds.java | 43 ++++++++++++++++ .../test/tab/TestCreativeTabs.java | 32 ++++++++++++ .../assets/architectury-test/lang/en_us.json | 4 ++ testmod-fabric/build.gradle | 30 ++++++++++++ .../src/main}/resources/fabric.mod.json | 0 testmod-forge/build.gradle | 36 ++++++++++++++ testmod-forge/gradle.properties | 1 + .../architectury/test/TestModForge.java | 32 ++++++++++++ .../src/main/resources/META-INF/mods.toml | 14 ++++++ testmod-forge/src/main/resources/pack.mcmeta | 6 +++ 30 files changed, 431 insertions(+), 41 deletions(-) delete mode 100644 logs/latest.log create mode 100644 testmod-common/build.gradle create mode 100644 testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java rename {common/src/test/java/me/shedaniel/architectury/test => testmod-common/src/main/java/me/shedaniel/architectury/test/debug}/ConsoleMessageSink.java (96%) rename common/src/test/java/me/shedaniel/architectury/test/TestMod.java => testmod-common/src/main/java/me/shedaniel/architectury/test/debug/DebugEvents.java (96%) rename {common/src/test/java/me/shedaniel/architectury/test => testmod-common/src/main/java/me/shedaniel/architectury/test/debug}/MessageSink.java (95%) rename {common/src/test/java/me/shedaniel/architectury/test => testmod-common/src/main/java/me/shedaniel/architectury/test/debug}/client/ClientOverlayMessageSink.java (93%) create mode 100644 testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java create mode 100644 testmod-common/src/main/java/me/shedaniel/architectury/test/registry/client/TestKeybinds.java create mode 100644 testmod-common/src/main/java/me/shedaniel/architectury/test/tab/TestCreativeTabs.java create mode 100644 testmod-common/src/main/resources/assets/architectury-test/lang/en_us.json create mode 100644 testmod-fabric/build.gradle rename {common/src/test => testmod-fabric/src/main}/resources/fabric.mod.json (100%) create mode 100644 testmod-forge/build.gradle create mode 100644 testmod-forge/gradle.properties create mode 100644 testmod-forge/src/main/java/me/shedaniel/architectury/test/TestModForge.java create mode 100644 testmod-forge/src/main/resources/META-INF/mods.toml create mode 100644 testmod-forge/src/main/resources/pack.mcmeta diff --git a/build.gradle b/build.gradle index ee692f25..b6ac6273 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id "architectury-plugin" version "1.3.47" - id "forgified-fabric-loom" version "0.5.30" apply false + id "architectury-plugin" version "2.0.57" + id "forgified-fabric-loom" version "0.6.47" apply false id "org.cadixdev.licenser" version "0.5.0" id "com.jfrog.bintray" version "1.8.4" id "com.matthewprenger.cursegradle" version "1.4.0" apply false @@ -15,8 +15,9 @@ architectury { subprojects { apply plugin: "forgified-fabric-loom" - dependencies { - testCompile sourceSets.main.output + loom { + silentMojangMappingsLicense() + useFabricMixin = true } } @@ -31,6 +32,14 @@ allprojects { tasks.withType(JavaCompile) { options.encoding = "UTF-8" + + // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too + // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used. + // We'll use that if it's available, but otherwise we'll use the older option. + def targetVersion = 8 + if (JavaVersion.current().isJava9Compatible()) { + options.release = targetVersion + } } license { diff --git a/common/build.gradle b/common/build.gradle index 8ed220e7..7e64e18b 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,10 +1,9 @@ loom { accessWidener = file("src/main/resources/architectury.accessWidener") - silentMojangMappingsLicense() } dependencies { - minecraft "com.mojang:minecraft:${rootProject.architect.minecraft}" + minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}" mappings minecraft.officialMojangMappings() // We depend on fabric loader here to use the fabric @Environment annotations // Do NOT use other classes from fabric loader diff --git a/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java index d62ea842..bdde8377 100644 --- a/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java +++ b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java @@ -1,3 +1,22 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + package me.shedaniel.architectury.core; import net.minecraft.world.item.crafting.Recipe; diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java index b7597286..81efa1cd 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java @@ -24,6 +24,7 @@ import me.shedaniel.architectury.event.EventFactory; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.player.LocalPlayer; +import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public interface ClientPlayerEvent { @@ -38,7 +39,7 @@ public interface ClientPlayerEvent { @Environment(EnvType.CLIENT) interface ClientPlayerQuit { - void quit(LocalPlayer player); + void quit(@Nullable LocalPlayer player); } @Environment(EnvType.CLIENT) diff --git a/common/src/main/java/me/shedaniel/architectury/registry/RegistrySupplier.java b/common/src/main/java/me/shedaniel/architectury/registry/RegistrySupplier.java index e00a5d7b..a9bde6c9 100644 --- a/common/src/main/java/me/shedaniel/architectury/registry/RegistrySupplier.java +++ b/common/src/main/java/me/shedaniel/architectury/registry/RegistrySupplier.java @@ -29,12 +29,21 @@ import java.util.function.Supplier; import java.util.stream.Stream; public interface RegistrySupplier extends Supplier { + /** + * @return the identifier of the registry + */ @NotNull ResourceLocation getRegistryId(); + /** + * @return the identifier of the entry + */ @NotNull ResourceLocation getId(); + /** + * @return whether the entry has been registered + */ boolean isPresent(); @Nullable diff --git a/fabric/build.gradle b/fabric/build.gradle index 83353a7a..93da73c8 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -10,12 +10,16 @@ plugins { } loom { - accessWidener(file("src/main/resources/architectury.accessWidener")) - silentMojangMappingsLicense() + accessWidener = file("src/main/resources/architectury.accessWidener") } configurations { shadow + dev +} + +artifacts { + dev(jar) } architectury { @@ -23,25 +27,23 @@ architectury { } dependencies { - minecraft("com.mojang:minecraft:${rootProject.architect.minecraft}") - mappings(minecraft.officialMojangMappings()) - 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}") + minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}" + mappings minecraft.officialMojangMappings() + 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" compileOnly(project(path: ":common")) { transitive = false } - runtimeOnly(project(path: ":common", configuration: "transformed")) { + runtimeOnly(project(path: ":common", configuration: "transformDevelopmentFabric")) { transitive = false } - shadow(project(path: ":common", configuration: "transformed")) { + shadow(project(path: ":common", configuration: "transformProductionFabric")) { transitive = false } - - testCompile project(":common").sourceSets.test.output } processResources { diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java index 7ed0192c..5eaea2be 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java @@ -44,9 +44,7 @@ public class MixinMinecraft { @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/chat/NarratorChatListener;clear()V")) private void handleLogin(Screen screen, CallbackInfo ci) { - if (player != null) { - ClientPlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(player); - } + ClientPlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(player); } @Inject(method = "startUseItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", ordinal = 1), diff --git a/forge/build.gradle b/forge/build.gradle index d54cd3a5..b00dcd3a 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -4,12 +4,16 @@ plugins { } loom { - silentMojangMappingsLicense() mixinConfig = "architectury.mixins.json" } configurations { shadow + dev +} + +artifacts { + dev(jar) } architectury { @@ -17,19 +21,19 @@ architectury { } dependencies { - minecraft("com.mojang:minecraft:${rootProject.architect.minecraft}") - mappings(minecraft.officialMojangMappings()) - forge("net.minecraftforge:forge:${rootProject.architect.minecraft}-${rootProject.forge_version}") + minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}" + mappings loom.officialMojangMappings() + forge "net.minecraftforge:forge:${rootProject.architectury.minecraft}-${rootProject.forge_version}" implementation "net.jodah:typetools:0.6.2" shadow "net.jodah:typetools:0.6.2" compileOnly(project(path: ":common")) { transitive = false } - runtimeOnly(project(path: ":common", configuration: "transformForgeFakeMod")) { + runtimeOnly(project(path: ":common", configuration: "transformDevelopmentForge")) { transitive = false } - shadow(project(path: ":common", configuration: "transformForge")) { + shadow(project(path: ":common", configuration: "transformProductionForge")) { transitive = false } } diff --git a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java index 8662416d..06b44783 100644 --- a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java +++ b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java @@ -1,3 +1,22 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + package me.shedaniel.architectury.mixin.forge; import me.shedaniel.architectury.core.AbstractRecipeSerializer; diff --git a/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java index 43d1b8b9..25b01e1d 100644 --- a/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java +++ b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java @@ -1,3 +1,22 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + package me.shedaniel.architectury.plugin.forge; import org.objectweb.asm.Opcodes; diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4b7e1f3d..da9702f9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/logs/latest.log b/logs/latest.log deleted file mode 100644 index e69de29b..00000000 diff --git a/settings.gradle b/settings.gradle index 77a9dc32..b1c39389 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,5 +11,8 @@ pluginManagement { include("common") include("fabric") include("forge") +include("testmod-common") +include("testmod-fabric") +include("testmod-forge") rootProject.name = "architectury" diff --git a/testmod-common/build.gradle b/testmod-common/build.gradle new file mode 100644 index 00000000..67a232ad --- /dev/null +++ b/testmod-common/build.gradle @@ -0,0 +1,12 @@ +dependencies { + minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}" + mappings loom.officialMojangMappings() + // We depend on fabric loader here to use the fabric @Environment annotations + // Do NOT use other classes from fabric loader + modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + implementation project(":common") +} + +architectury { + common() +} diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java new file mode 100644 index 00000000..9eecf7ae --- /dev/null +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java @@ -0,0 +1,42 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.test; + +import me.shedaniel.architectury.platform.Platform; +import me.shedaniel.architectury.test.debug.ConsoleMessageSink; +import me.shedaniel.architectury.test.debug.DebugEvents; +import me.shedaniel.architectury.test.debug.MessageSink; +import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink; +import me.shedaniel.architectury.test.registry.TestRegistries; +import me.shedaniel.architectury.test.registry.client.TestKeybinds; +import me.shedaniel.architectury.utils.Env; +import me.shedaniel.architectury.utils.EnvExecutor; + +public class TestMod { + public static final MessageSink SINK = EnvExecutor.getEnvSpecific(() -> ClientOverlayMessageSink::new, () -> ConsoleMessageSink::new); + public static final String MOD_ID = "architectury-test"; + + public static void initialize() { + DebugEvents.initialize(); + TestRegistries.initialize(); + if (Platform.getEnvironment() == Env.CLIENT) + TestKeybinds.initialize(); + } +} diff --git a/common/src/test/java/me/shedaniel/architectury/test/ConsoleMessageSink.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/ConsoleMessageSink.java similarity index 96% rename from common/src/test/java/me/shedaniel/architectury/test/ConsoleMessageSink.java rename to testmod-common/src/main/java/me/shedaniel/architectury/test/debug/ConsoleMessageSink.java index 01faf68e..37cfa725 100644 --- a/common/src/test/java/me/shedaniel/architectury/test/ConsoleMessageSink.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/ConsoleMessageSink.java @@ -17,7 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package me.shedaniel.architectury.test; +package me.shedaniel.architectury.test.debug; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/common/src/test/java/me/shedaniel/architectury/test/TestMod.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/DebugEvents.java similarity index 96% rename from common/src/test/java/me/shedaniel/architectury/test/TestMod.java rename to testmod-common/src/main/java/me/shedaniel/architectury/test/debug/DebugEvents.java index 396c1651..4f482d39 100644 --- a/common/src/test/java/me/shedaniel/architectury/test/TestMod.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/DebugEvents.java @@ -17,7 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package me.shedaniel.architectury.test; +package me.shedaniel.architectury.test.debug; import com.mojang.blaze3d.platform.InputConstants; import me.shedaniel.architectury.event.events.*; @@ -27,7 +27,9 @@ import me.shedaniel.architectury.event.events.client.ClientLifecycleEvent; import me.shedaniel.architectury.event.events.client.ClientPlayerEvent; import me.shedaniel.architectury.hooks.ExplosionHooks; import me.shedaniel.architectury.platform.Platform; -import me.shedaniel.architectury.test.client.ClientOverlayMessageSink; +import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink; +import me.shedaniel.architectury.test.debug.ConsoleMessageSink; +import me.shedaniel.architectury.test.debug.MessageSink; import me.shedaniel.architectury.utils.Env; import me.shedaniel.architectury.utils.EnvExecutor; import net.fabricmc.api.EnvType; @@ -44,9 +46,9 @@ import net.minecraft.world.level.Level; import java.util.Optional; -public class TestMod { - public static final MessageSink SINK = EnvExecutor.getEnvSpecific(() -> ClientOverlayMessageSink::new, () -> ConsoleMessageSink::new); - +import static me.shedaniel.architectury.test.TestMod.SINK; + +public class DebugEvents { public static void initialize() { debugEvents(); if (Platform.getEnvironment() == Env.CLIENT) @@ -203,7 +205,9 @@ public class TestMod { SINK.accept(player.getScoreboardName() + " joined (client)"); }); ClientPlayerEvent.CLIENT_PLAYER_QUIT.register(player -> { - SINK.accept(player.getScoreboardName() + " quit (client)"); + if (player != null) { + SINK.accept(player.getScoreboardName() + " quit (client)"); + } }); ClientPlayerEvent.CLIENT_PLAYER_RESPAWN.register((oldPlayer, newPlayer) -> { SINK.accept(newPlayer.getScoreboardName() + " respawned (client)"); diff --git a/common/src/test/java/me/shedaniel/architectury/test/MessageSink.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/MessageSink.java similarity index 95% rename from common/src/test/java/me/shedaniel/architectury/test/MessageSink.java rename to testmod-common/src/main/java/me/shedaniel/architectury/test/debug/MessageSink.java index aaf42f77..5c06b316 100644 --- a/common/src/test/java/me/shedaniel/architectury/test/MessageSink.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/MessageSink.java @@ -17,7 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package me.shedaniel.architectury.test; +package me.shedaniel.architectury.test.debug; public interface MessageSink { void accept(String message); diff --git a/common/src/test/java/me/shedaniel/architectury/test/client/ClientOverlayMessageSink.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/client/ClientOverlayMessageSink.java similarity index 93% rename from common/src/test/java/me/shedaniel/architectury/test/client/ClientOverlayMessageSink.java rename to testmod-common/src/main/java/me/shedaniel/architectury/test/debug/client/ClientOverlayMessageSink.java index 498172e0..e7cb4419 100644 --- a/common/src/test/java/me/shedaniel/architectury/test/client/ClientOverlayMessageSink.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/client/ClientOverlayMessageSink.java @@ -17,13 +17,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package me.shedaniel.architectury.test.client; +package me.shedaniel.architectury.test.debug.client; import com.google.common.collect.Lists; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import me.shedaniel.architectury.event.events.GuiEvent; -import me.shedaniel.architectury.test.ConsoleMessageSink; +import me.shedaniel.architectury.test.debug.ConsoleMessageSink; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; @@ -57,6 +57,8 @@ public class ClientOverlayMessageSink extends ConsoleMessageSink { } public void render(PoseStack matrices, float delta) { + matrices.pushPose(); + matrices.scale(0.5f, 0.5f, 1f); Minecraft minecraft = Minecraft.getInstance(); long currentMills = Util.getMillis(); int lineHeight = minecraft.font.lineHeight; @@ -64,13 +66,13 @@ public class ClientOverlayMessageSink extends ConsoleMessageSink { synchronized (messages) { Iterator messageIterator = messages.iterator(); int y = 1; - + RenderSystem.enableBlend(); - + while (messageIterator.hasNext()) { Message message = messageIterator.next(); int timeExisted = (int) (currentMills - message.created); - + if (timeExisted >= 5000) { messageIterator.remove(); } else { @@ -87,6 +89,7 @@ public class ClientOverlayMessageSink extends ConsoleMessageSink { RenderSystem.disableAlphaTest(); RenderSystem.disableBlend(); + matrices.popPose(); } private static class Message { diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java new file mode 100644 index 00000000..57a817d7 --- /dev/null +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java @@ -0,0 +1,49 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.test.registry; + +import me.shedaniel.architectury.registry.BlockProperties; +import me.shedaniel.architectury.registry.DeferredRegister; +import me.shedaniel.architectury.registry.RegistrySupplier; +import me.shedaniel.architectury.test.TestMod; +import me.shedaniel.architectury.test.tab.TestCreativeTabs; +import net.minecraft.core.Registry; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; + +public class TestRegistries { + public static final DeferredRegister ITEMS = DeferredRegister.create(TestMod.MOD_ID, Registry.ITEM_REGISTRY); + public static final DeferredRegister BLOCKS = DeferredRegister.create(TestMod.MOD_ID, Registry.BLOCK_REGISTRY); + + public static final RegistrySupplier TEST_ITEM = ITEMS.register("test_item", () -> + new Item(new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); + + public static final RegistrySupplier TEST_BLOCK = BLOCKS.register("test_block", () -> + new Block(BlockProperties.copy(Blocks.STONE))); + public static final RegistrySupplier TEST_BLOCK_ITEM = ITEMS.register("test_block", () -> + new BlockItem(TEST_BLOCK.get(), new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); + + public static void initialize() { + BLOCKS.register(); + ITEMS.register(); + } +} diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/client/TestKeybinds.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/client/TestKeybinds.java new file mode 100644 index 00000000..4a390338 --- /dev/null +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/client/TestKeybinds.java @@ -0,0 +1,43 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.test.registry.client; + +import com.mojang.blaze3d.platform.InputConstants; +import me.shedaniel.architectury.event.events.client.ClientTickEvent; +import me.shedaniel.architectury.registry.KeyBindings; +import me.shedaniel.architectury.test.TestMod; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.resources.language.I18n; +import org.lwjgl.glfw.GLFW; + +public class TestKeybinds { + @Environment(EnvType.CLIENT) + public static void initialize() { + KeyMapping mapping = new KeyMapping("key.architectury-test.test", InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_O, "category.architectury-test"); + KeyBindings.registerKeyBinding(mapping); + ClientTickEvent.CLIENT_POST.register(instance -> { + while (mapping.consumeClick()) { + TestMod.SINK.accept("Key \"%s\" pressed!", I18n.get("key.architectury-test.test")); + } + }); + } +} diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/tab/TestCreativeTabs.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/tab/TestCreativeTabs.java new file mode 100644 index 00000000..41219a6f --- /dev/null +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/tab/TestCreativeTabs.java @@ -0,0 +1,32 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.test.tab; + +import me.shedaniel.architectury.registry.CreativeTabs; +import me.shedaniel.architectury.test.TestMod; +import me.shedaniel.architectury.test.registry.TestRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.ItemStack; + +public class TestCreativeTabs { + public static final CreativeModeTab TEST_TAB = CreativeTabs.create(new ResourceLocation(TestMod.MOD_ID, "test_tab"), + () -> new ItemStack(TestRegistries.TEST_ITEM.get())); +} diff --git a/testmod-common/src/main/resources/assets/architectury-test/lang/en_us.json b/testmod-common/src/main/resources/assets/architectury-test/lang/en_us.json new file mode 100644 index 00000000..44b0918b --- /dev/null +++ b/testmod-common/src/main/resources/assets/architectury-test/lang/en_us.json @@ -0,0 +1,4 @@ +{ + "category.architectury-test": "Architectury Test", + "key.architectury-test.test": "Test Keybind" +} \ No newline at end of file diff --git a/testmod-fabric/build.gradle b/testmod-fabric/build.gradle new file mode 100644 index 00000000..88aa6105 --- /dev/null +++ b/testmod-fabric/build.gradle @@ -0,0 +1,30 @@ +plugins { + id "com.github.johnrengelman.shadow" version "5.0.0" + id "com.matthewprenger.cursegradle" +} + +architectury { + platformSetupLoomIde() +} + +dependencies { + minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}" + mappings loom.officialMojangMappings() + 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 project(path: ":fabric", configuration: "dev") + compileOnly(project(path: ":common")) { + transitive = false + } + runtimeOnly(project(path: ":common", configuration: "transformDevelopmentFabric")) { + transitive = false + } + compileOnly(project(path: ":testmod-common")) { + transitive = false + } + runtimeOnly(project(path: ":testmod-common", configuration: "transformDevelopmentFabric")) { + transitive = false + } +} diff --git a/common/src/test/resources/fabric.mod.json b/testmod-fabric/src/main/resources/fabric.mod.json similarity index 100% rename from common/src/test/resources/fabric.mod.json rename to testmod-fabric/src/main/resources/fabric.mod.json diff --git a/testmod-forge/build.gradle b/testmod-forge/build.gradle new file mode 100644 index 00000000..7008ff10 --- /dev/null +++ b/testmod-forge/build.gradle @@ -0,0 +1,36 @@ +plugins { + id "com.github.johnrengelman.shadow" version "5.0.0" + id "com.matthewprenger.cursegradle" +} + +loom { + mixinConfig = "architectury.mixins.json" + + localMods { + it.add(project(":forge").sourceSets.main) + } +} + +architectury { + platformSetupLoomIde() +} + +dependencies { + minecraft "com.mojang:minecraft:${gradle.rootProject.architectury.minecraft}" + mappings loom.officialMojangMappings() + forge "net.minecraftforge:forge:${gradle.rootProject.architectury.minecraft}-${rootProject.forge_version}" + + implementation project(path: ":forge", configuration: "dev") + compileOnly(project(path: ":common")) { + transitive = false + } + runtimeOnly(project(path: ":common", configuration: "transformDevelopmentForge")) { + transitive = false + } + compileOnly(project(path: ":testmod-common")) { + transitive = false + } + runtimeOnly(project(path: ":testmod-common", configuration: "transformDevelopmentForge")) { + transitive = false + } +} diff --git a/testmod-forge/gradle.properties b/testmod-forge/gradle.properties new file mode 100644 index 00000000..01c185b6 --- /dev/null +++ b/testmod-forge/gradle.properties @@ -0,0 +1 @@ +loom.forge=true \ No newline at end of file diff --git a/testmod-forge/src/main/java/me/shedaniel/architectury/test/TestModForge.java b/testmod-forge/src/main/java/me/shedaniel/architectury/test/TestModForge.java new file mode 100644 index 00000000..d9f130ce --- /dev/null +++ b/testmod-forge/src/main/java/me/shedaniel/architectury/test/TestModForge.java @@ -0,0 +1,32 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.test; + +import me.shedaniel.architectury.platform.forge.EventBuses; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +@Mod(TestMod.MOD_ID) +public class TestModForge { + public TestModForge() { + EventBuses.registerModEventBus(TestMod.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); + TestMod.initialize(); + } +} diff --git a/testmod-forge/src/main/resources/META-INF/mods.toml b/testmod-forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 00000000..7e073ef4 --- /dev/null +++ b/testmod-forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,14 @@ +modLoader = "javafml" +loaderVersion = "[33,)" +issueTrackerURL = "https://github.com/shedaniel/architectury/issues" +license = "LGPL-3" + +[[mods]] +modId = "architectury-test" +version = "${version}" +displayName = "Architectury Test" +authors = "shedaniel" +description = ''' +A intermediary api aimed to ease developing multiplatform mods. +''' +license = "LGPL-3" \ No newline at end of file diff --git a/testmod-forge/src/main/resources/pack.mcmeta b/testmod-forge/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..610d07f3 --- /dev/null +++ b/testmod-forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Architectury Test", + "pack_format": 6 + } +} \ No newline at end of file From de90542afdbbca46e21eb46724624476ab570843 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 20 Jan 2021 21:21:23 +0800 Subject: [PATCH 05/45] Add raw input events, close #16 --- .../events/client/ClientRawInputEvent.java | 59 +++++++++++++++++++ fabric/build.gradle | 2 +- .../fabric/client/MixinKeyboardHandler.java | 10 ++++ .../fabric/client/MixinMouseHandler.java | 56 ++++++++++++++---- .../event/forge/EventHandlerImplClient.java | 24 ++++++++ .../shedaniel/architectury/test/TestMod.java | 2 +- .../test/{debug => events}/DebugEvents.java | 25 ++++---- 7 files changed, 155 insertions(+), 23 deletions(-) create mode 100644 common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java rename testmod-common/src/main/java/me/shedaniel/architectury/test/{debug => events}/DebugEvents.java (93%) diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java new file mode 100644 index 00000000..587b494a --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java @@ -0,0 +1,59 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.event.events.client; + +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.Minecraft; +import net.minecraft.world.InteractionResult; + +@Environment(EnvType.CLIENT) +public interface ClientRawInputEvent { + /** + * Invoked after the mouse has scrolled, but doesn't have a screen opened, and in a world, equivalent to forge's {@code InputEvent.MouseScrollEvent}. + */ + Event MOUSE_SCROLLED = EventFactory.createInteractionResult(MouseScrolled.class); + /** + * Invoked after the mouse has clicked, before the screen intercepts, equivalent to forge's {@code InputEvent.RawMouseEvent}. + */ + Event MOUSE_CLICKED_PRE = EventFactory.createInteractionResult(MouseClicked.class); + /** + * Invoked after the mouse has clicked, after the screen intercepts, equivalent to forge's {@code InputEvent.MouseInputEvent}. + */ + Event MOUSE_CLICKED_POST = EventFactory.createInteractionResult(MouseClicked.class); + /** + * Invoked after a key was pressed, after the screen intercepts, equivalent to forge's {@code InputEvent.KeyInputEvent}. + */ + Event KEY_PRESSED = EventFactory.createInteractionResult(KeyPressed.class); + + interface KeyPressed { + InteractionResult keyPressed(Minecraft client, int keyCode, int scanCode, int action, int modifiers); + } + + interface MouseScrolled { + InteractionResult mouseScrolled(Minecraft client, double amount); + } + + interface MouseClicked { + InteractionResult mouseClicked(Minecraft client, int button, int action, int mods); + } +} diff --git a/fabric/build.gradle b/fabric/build.gradle index 93da73c8..7b36866b 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,8 +1,8 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import shadow.org.apache.tools.zip.ZipEntry -import shadow.org.codehaus.plexus.util.IOUtil import shadow.org.apache.tools.zip.ZipOutputStream +import shadow.org.codehaus.plexus.util.IOUtil plugins { id "com.github.johnrengelman.shadow" version "5.0.0" diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinKeyboardHandler.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinKeyboardHandler.java index ec0d5d23..e556b2bb 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinKeyboardHandler.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinKeyboardHandler.java @@ -19,6 +19,7 @@ package me.shedaniel.architectury.mixin.fabric.client; +import me.shedaniel.architectury.event.events.client.ClientRawInputEvent; import me.shedaniel.architectury.event.events.client.ClientScreenInputEvent; import me.shedaniel.architectury.impl.fabric.ScreenInputDelegate; import net.minecraft.client.KeyboardHandler; @@ -111,4 +112,13 @@ public class MixinKeyboardHandler { info.cancel(); } } + + @Inject(method = "keyPress", at = @At("RETURN"), cancellable = true) + public void onRawKey(long handle, int key, int scanCode, int action, int modifiers, CallbackInfo info) { + if (handle == this.minecraft.getWindow().getWindow()) { + InteractionResult result = ClientRawInputEvent.KEY_PRESSED.invoker().keyPressed(minecraft, key, scanCode, action, modifiers); + if (result != InteractionResult.PASS) + info.cancel(); + } + } } diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMouseHandler.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMouseHandler.java index 840abd51..93597f76 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMouseHandler.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMouseHandler.java @@ -19,6 +19,7 @@ package me.shedaniel.architectury.mixin.fabric.client; +import me.shedaniel.architectury.event.events.client.ClientRawInputEvent; import me.shedaniel.architectury.event.events.client.ClientScreenInputEvent; import me.shedaniel.architectury.impl.fabric.ScreenInputDelegate; import net.minecraft.client.Minecraft; @@ -52,7 +53,7 @@ public class MixinMouseHandler { @Inject(method = "onScroll", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;mouseScrolled(DDD)Z", ordinal = 0), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) - public void onMouseScrolled(long long_1, double double_1, double double_2, CallbackInfo info, double amount, double x, double y) { + public void onMouseScrolled(long handle, double xOffset, double yOffset, CallbackInfo info, double amount, double x, double y) { if (!info.isCancelled()) { InteractionResult result = ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(minecraft, minecraft.screen, x, y, amount); if (result != InteractionResult.PASS) @@ -63,29 +64,62 @@ public class MixinMouseHandler { @Inject(method = "onScroll", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/Screen;mouseScrolled(DDD)Z", ordinal = 0, shift = At.Shift.AFTER), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) - public void onMouseScrolledPost(long long_1, double double_1, double double_2, CallbackInfo info, double amount, double x, double y) { + public void onMouseScrolledPost(long handle, double xOffset, double yOffset, CallbackInfo info, double amount, double x, double y) { if (!info.isCancelled()) { InteractionResult result = ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(minecraft, minecraft.screen, x, y, amount); } } - @Inject(method = "onPress", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/gui/screens/Screen;wrapScreenError(Ljava/lang/Runnable;Ljava/lang/String;Ljava/lang/String;)V", - ordinal = 0), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) - public void onMouseClicked(long long_1, int button, int int_2, int int_3, CallbackInfo info, boolean bl, int i, boolean[] bls, double d, double e) { + @Inject(method = "onScroll", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSpectator()Z", + ordinal = 0), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) + public void onRawMouseScrolled(long handle, double xOffset, double yOffset, CallbackInfo info, double amount) { if (!info.isCancelled()) { - InteractionResult result = ClientScreenInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(minecraft, minecraft.screen, d, e, button); + InteractionResult result = ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(minecraft, amount); if (result != InteractionResult.PASS) info.cancel(); } } - @Inject(method = "onPress", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/gui/screens/Screen;wrapScreenError(Ljava/lang/Runnable;Ljava/lang/String;Ljava/lang/String;)V", - ordinal = 0, shift = At.Shift.AFTER), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) - public void onMouseClickedPost(long long_1, int button, int int_2, int int_3, CallbackInfo info, boolean bl, int i, boolean[] bls, double d, double e) { + @SuppressWarnings("UnresolvedMixinReference") + @Inject(method = {"lambda$onPress$0", "method_1611"}, at = @At("HEAD"), cancellable = true, remap = false) + public void onGuiMouseClicked(boolean[] bls, double d, double e, int button, CallbackInfo info) { if (!info.isCancelled()) { + InteractionResult result = ClientScreenInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(minecraft, minecraft.screen, d, e, button); + if (result != InteractionResult.PASS) { + bls[0] = true; + info.cancel(); + } + } + } + + @SuppressWarnings("UnresolvedMixinReference") + @Inject(method = {"lambda$onPress$0", "method_1611"}, at = @At("RETURN"), cancellable = true, remap = false) + public void onGuiMouseClickedPost(boolean[] bls, double d, double e, int button, CallbackInfo info) { + if (!info.isCancelled() && !bls[0]) { InteractionResult result = ClientScreenInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(minecraft, minecraft.screen, d, e, button); + if (result != InteractionResult.PASS) { + bls[0] = true; + info.cancel(); + } + } + } + + @Inject(method = "onPress", at = @At(value = "FIELD", + target = "Lnet/minecraft/client/Minecraft;overlay:Lnet/minecraft/client/gui/screens/Overlay;", + ordinal = 0), cancellable = true) + public void onRawMouseClicked(long handle, int button, int action, int mods, CallbackInfo info) { + if (!info.isCancelled()) { + InteractionResult result = ClientRawInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(minecraft, button, action, mods); + if (result != InteractionResult.PASS) + info.cancel(); + } + } + + @Inject(method = "onPress", at = @At("RETURN"), cancellable = true) + public void onRawMouseClickedPost(long handle, int button, int action, int mods, CallbackInfo info) { + if (handle == this.minecraft.getWindow().getWindow()) { + InteractionResult result = ClientRawInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(minecraft, button, action, mods); if (result != InteractionResult.PASS) info.cancel(); } diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java index c5dc285d..190240eb 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java @@ -264,6 +264,30 @@ public class EventHandlerImplClient { ClientScreenInputEvent.KEY_RELEASED_POST.invoker().keyReleased(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers()); } + @SubscribeEvent + public static void event(InputEvent.MouseScrollEvent event) { + if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getScrollDelta()) == InteractionResult.FAIL) { + event.setCanceled(true); + } + } + + @SubscribeEvent + public static void event(InputEvent.RawMouseEvent event) { + if (ClientRawInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getMods()) == InteractionResult.FAIL) { + event.setCanceled(true); + } + } + + @SubscribeEvent + public static void event(InputEvent.MouseInputEvent event) { + ClientRawInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getMods()); + } + + @SubscribeEvent + public static void event(InputEvent.KeyInputEvent event) { + ClientRawInputEvent.KEY_PRESSED.invoker().keyPressed(Minecraft.getInstance(), event.getKey(), event.getScanCode(), event.getAction(), event.getModifiers()); + } + @OnlyIn(Dist.CLIENT) public static class ModBasedEventHandler { @SubscribeEvent diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java index 9eecf7ae..732322eb 100644 --- a/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java @@ -21,7 +21,7 @@ package me.shedaniel.architectury.test; import me.shedaniel.architectury.platform.Platform; import me.shedaniel.architectury.test.debug.ConsoleMessageSink; -import me.shedaniel.architectury.test.debug.DebugEvents; +import me.shedaniel.architectury.test.events.DebugEvents; import me.shedaniel.architectury.test.debug.MessageSink; import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink; import me.shedaniel.architectury.test.registry.TestRegistries; diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/DebugEvents.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java similarity index 93% rename from testmod-common/src/main/java/me/shedaniel/architectury/test/debug/DebugEvents.java rename to testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java index 4f482d39..3acf5dc1 100644 --- a/testmod-common/src/main/java/me/shedaniel/architectury/test/debug/DebugEvents.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java @@ -17,21 +17,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package me.shedaniel.architectury.test.debug; +package me.shedaniel.architectury.test.events; import com.mojang.blaze3d.platform.InputConstants; import me.shedaniel.architectury.event.events.*; -import me.shedaniel.architectury.event.events.client.ClientChatEvent; -import me.shedaniel.architectury.event.events.client.ClientScreenInputEvent; -import me.shedaniel.architectury.event.events.client.ClientLifecycleEvent; -import me.shedaniel.architectury.event.events.client.ClientPlayerEvent; +import me.shedaniel.architectury.event.events.client.*; import me.shedaniel.architectury.hooks.ExplosionHooks; import me.shedaniel.architectury.platform.Platform; -import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink; -import me.shedaniel.architectury.test.debug.ConsoleMessageSink; -import me.shedaniel.architectury.test.debug.MessageSink; import me.shedaniel.architectury.utils.Env; -import me.shedaniel.architectury.utils.EnvExecutor; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.core.Position; @@ -229,7 +222,7 @@ public class DebugEvents { SINK.accept("Client texture stitched: " + atlas.location()); }); ClientScreenInputEvent.MOUSE_SCROLLED_PRE.register((client, screen, mouseX, mouseY, amount) -> { - SINK.accept("Screen Mouse amount: %.2f distance", amount); + SINK.accept("Screen Mouse scrolled: %.2f distance", amount); return InteractionResult.PASS; }); ClientScreenInputEvent.MOUSE_CLICKED_PRE.register((client, screen, mouseX, mouseY, button) -> { @@ -256,6 +249,18 @@ public class DebugEvents { SINK.accept("Screen Key released: " + InputConstants.getKey(keyCode, scanCode).getDisplayName().getString()); return InteractionResult.PASS; }); + ClientRawInputEvent.MOUSE_SCROLLED.register((client, amount) -> { + SINK.accept("Raw Mouse scrolled: %.2f distance", amount); + return InteractionResult.PASS; + }); + ClientRawInputEvent.MOUSE_CLICKED_PRE.register((client, button, action, mods) -> { + SINK.accept("Raw Mouse clicked: " + button); + return InteractionResult.PASS; + }); + ClientRawInputEvent.KEY_PRESSED.register((client, keyCode, scanCode, action, modifiers) -> { + SINK.accept("Raw Key pressed: " + InputConstants.getKey(keyCode, scanCode).getDisplayName().getString()); + return InteractionResult.PASS; + }); } private static String toSimpleName(Object o) { From bca73403d8548f6ef74b523f3bc800deaf583ee7 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Thu, 21 Jan 2021 21:09:32 +0800 Subject: [PATCH 06/45] Add LevelResourceHooks --- .../hooks/LevelResourceHooks.java | 30 +++++++++++++++++++ .../main/resources/architectury.accessWidener | 3 +- .../resources/META-INF/accesstransformer.cfg | 3 +- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 common/src/main/java/me/shedaniel/architectury/hooks/LevelResourceHooks.java diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/LevelResourceHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/LevelResourceHooks.java new file mode 100644 index 00000000..3eb08748 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/hooks/LevelResourceHooks.java @@ -0,0 +1,30 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.hooks; + +import net.minecraft.world.level.storage.LevelResource; + +public class LevelResourceHooks { + private LevelResourceHooks() {} + + public static LevelResource create(String id) { + return new LevelResource(id); + } +} diff --git a/common/src/main/resources/architectury.accessWidener b/common/src/main/resources/architectury.accessWidener index d67323f8..80026d51 100644 --- a/common/src/main/resources/architectury.accessWidener +++ b/common/src/main/resources/architectury.accessWidener @@ -38,4 +38,5 @@ mutable field net/minecraft/world/level/biome/BiomeSpecialEffects ambientMoodSet accessible field net/minecraft/world/level/biome/BiomeSpecialEffects ambientAdditionsSettings Ljava/util/Optional; mutable field net/minecraft/world/level/biome/BiomeSpecialEffects ambientAdditionsSettings Ljava/util/Optional; accessible field net/minecraft/world/level/biome/BiomeSpecialEffects backgroundMusic Ljava/util/Optional; -mutable field net/minecraft/world/level/biome/BiomeSpecialEffects backgroundMusic Ljava/util/Optional; \ No newline at end of file +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects backgroundMusic Ljava/util/Optional; +accessible method net/minecraft/world/level/storage/LevelResource (Ljava/lang/String;)V \ No newline at end of file diff --git a/forge/src/main/resources/META-INF/accesstransformer.cfg b/forge/src/main/resources/META-INF/accesstransformer.cfg index 12dffb11..d9108c0c 100644 --- a/forge/src/main/resources/META-INF/accesstransformer.cfg +++ b/forge/src/main/resources/META-INF/accesstransformer.cfg @@ -31,4 +31,5 @@ public-f net.minecraft.world.biome.BiomeAmbience field_235212_i_ # music public-f net.minecraft.world.biome.BiomeAmbience field_242523_e # skyColor public-f net.minecraft.world.biome.BiomeAmbience field_242524_f # foliageColor public-f net.minecraft.world.biome.BiomeAmbience field_242525_g # grassColor -public-f net.minecraft.world.biome.BiomeAmbience field_242526_h # grassColorModifier \ No newline at end of file +public-f net.minecraft.world.biome.BiomeAmbience field_242526_h # grassColorModifier +public net.minecraft.world.storage.FolderName (Ljava/lang/String;)V \ No newline at end of file From 0bb1d9c87e948333d9b8a10edaf4dee0af2047d0 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Thu, 21 Jan 2021 21:22:08 +0800 Subject: [PATCH 07/45] Fix AW --- build.gradle | 2 +- fabric/build.gradle | 53 ------------------- .../main/resources/architectury.accessWidener | 43 ++++++++++++++- 3 files changed, 43 insertions(+), 55 deletions(-) diff --git a/build.gradle b/build.gradle index b6ac6273..fddfe4ac 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "architectury-plugin" version "2.0.57" - id "forgified-fabric-loom" version "0.6.47" apply false + id "forgified-fabric-loom" version "0.6.49" apply false id "org.cadixdev.licenser" version "0.5.0" id "com.jfrog.bintray" version "1.8.4" id "com.matthewprenger.cursegradle" version "1.4.0" apply false diff --git a/fabric/build.gradle b/fabric/build.gradle index 93da73c8..d27b4a02 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -55,9 +55,6 @@ processResources { shadowJar { relocate "net.jodah.typetools", "me.shedaniel.architectury.shadowed.impl.net.jodah.typetools" - transform(MergeAccessWidenersTransformer.class) { - it.resource = "architectury.accessWidener" - } configurations = [project.configurations.shadow] classifier "shadow" } @@ -109,53 +106,3 @@ curseforge { } rootProject.tasks.getByName("curseforgePublish").dependsOn tasks.getByName("curseforge") - -class MergeAccessWidenersTransformer implements Transformer { - String resource - ByteArrayOutputStream data - - MergeAccessWidenersTransformer() { - data = new ByteArrayOutputStream() - data.write("accessWidener v1 named\n".bytes) - } - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (resource != null && resource.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - def lines = context.is.readLines() - lines.removeIf { it == "accessWidener v1 named" } - IOUtil.copy(lines.join("\n"), data) - data.write('\n'.bytes) - - context.is.close() - } - - @Override - boolean hasTransformedResource() { - return data.size() > 0 - } - - void modifyOutputStream(org.apache.tools.zip.ZipOutputStream jos, boolean preserveFileTimestamps) { - throw new AbstractMethodError() - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - - IOUtil.copy(new ByteArrayInputStream(data.toByteArray()), os) - data.reset() - data.write('accessWidener v1 named\n'.bytes) - } -} \ No newline at end of file diff --git a/fabric/src/main/resources/architectury.accessWidener b/fabric/src/main/resources/architectury.accessWidener index b21dac89..0d428916 100644 --- a/fabric/src/main/resources/architectury.accessWidener +++ b/fabric/src/main/resources/architectury.accessWidener @@ -54,4 +54,45 @@ accessible field net/minecraft/world/item/DyeColor textureDiffuseColor I accessible method net/minecraft/world/entity/player/Player closeContainer ()V accessible method net/minecraft/advancements/CriteriaTriggers register (Lnet/minecraft/advancements/CriterionTrigger;)Lnet/minecraft/advancements/CriterionTrigger; accessible method net/minecraft/world/inventory/MenuType (Lnet/minecraft/world/inventory/MenuType$MenuSupplier;)V -accessible class net/minecraft/world/inventory/MenuType$MenuSupplier \ No newline at end of file +accessible class net/minecraft/world/inventory/MenuType$MenuSupplier +accessible method net/minecraft/world/level/block/state/BlockBehaviour$Properties (Lnet/minecraft/world/level/material/Material;Ljava/util/function/Function;)V +accessible field net/minecraft/world/level/biome/Biome climateSettings Lnet/minecraft/world/level/biome/Biome$ClimateSettings; +accessible field net/minecraft/world/level/biome/Biome depth F +mutable field net/minecraft/world/level/biome/Biome depth F +accessible field net/minecraft/world/level/biome/Biome scale F +mutable field net/minecraft/world/level/biome/Biome scale F +accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory; +mutable field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory; +accessible field net/minecraft/world/level/biome/Biome$ClimateSettings precipitation Lnet/minecraft/world/level/biome/Biome$Precipitation; +mutable field net/minecraft/world/level/biome/Biome$ClimateSettings precipitation Lnet/minecraft/world/level/biome/Biome$Precipitation; +accessible field net/minecraft/world/level/biome/Biome$ClimateSettings temperature F +mutable field net/minecraft/world/level/biome/Biome$ClimateSettings temperature F +accessible field net/minecraft/world/level/biome/Biome$ClimateSettings temperatureModifier Lnet/minecraft/world/level/biome/Biome$TemperatureModifier; +mutable field net/minecraft/world/level/biome/Biome$ClimateSettings temperatureModifier Lnet/minecraft/world/level/biome/Biome$TemperatureModifier; +accessible field net/minecraft/world/level/biome/Biome$ClimateSettings downfall F +mutable field net/minecraft/world/level/biome/Biome$ClimateSettings downfall F +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects fogColor I +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects fogColor I +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects waterColor I +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects waterColor I +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects waterFogColor I +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects waterFogColor I +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects skyColor I +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects skyColor I +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects foliageColorOverride Ljava/util/Optional; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects foliageColorOverride Ljava/util/Optional; +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects grassColorOverride Ljava/util/Optional; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects grassColorOverride Ljava/util/Optional; +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects grassColorModifier Lnet/minecraft/world/level/biome/BiomeSpecialEffects$GrassColorModifier; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects grassColorModifier Lnet/minecraft/world/level/biome/BiomeSpecialEffects$GrassColorModifier; +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects ambientParticleSettings Ljava/util/Optional; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects ambientParticleSettings Ljava/util/Optional; +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects ambientLoopSoundEvent Ljava/util/Optional; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects ambientLoopSoundEvent Ljava/util/Optional; +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects ambientMoodSettings Ljava/util/Optional; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects ambientMoodSettings Ljava/util/Optional; +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects ambientAdditionsSettings Ljava/util/Optional; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects ambientAdditionsSettings Ljava/util/Optional; +accessible field net/minecraft/world/level/biome/BiomeSpecialEffects backgroundMusic Ljava/util/Optional; +mutable field net/minecraft/world/level/biome/BiomeSpecialEffects backgroundMusic Ljava/util/Optional; +accessible method net/minecraft/world/level/storage/LevelResource (Ljava/lang/String;)V \ No newline at end of file From 7c4b602b10501ea547e1869afe674feba3791304 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 21 Jan 2021 19:36:32 +0100 Subject: [PATCH 08/45] Add 1.16.5 to CurseForge versions --- .gitignore | 5 ++++- fabric/build.gradle | 1 + forge/build.gradle | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6f666f8e..1b2a32bf 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,7 @@ libs/ .project .settings/org.eclipse.core.resources.prefs .idea/ -classes/ \ No newline at end of file +classes/ + +.vscode/ +logs/ \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle index d27b4a02..0268f9cb 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -86,6 +86,7 @@ curseforge { changelog = releaseChangelog() addGameVersion "1.16-Snapshot" addGameVersion "1.16.4" + addGameVersion "1.16.5" addGameVersion "Java 8" addGameVersion "Fabric" relations { diff --git a/forge/build.gradle b/forge/build.gradle index b00dcd3a..cfe8a840 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -80,6 +80,7 @@ curseforge { changelogType = "html" changelog = releaseChangelog() addGameVersion "1.16.4" + addGameVersion "1.16.5" addGameVersion "Java 8" addGameVersion "Forge" mainArtifact(remapJar.archivePath) { From 465aeae5bd2846ded85fcedc909829fa25dfaca7 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 23 Jan 2021 02:15:49 +0800 Subject: [PATCH 09/45] Add ClientLifecycleEvent.CLIENT_SETUP and allow deferring in ColorHandlers --- .../architectury/event/EventFactory.java | 30 ++++++++++++ .../events/client/ClientLifecycleEvent.java | 1 + .../architectury/registry/ColorHandlers.java | 21 +++++++- .../architectury/registry/RenderTypes.java | 1 - .../init/fabric/ArchitecturyClient.java | 10 ++++ .../registry/fabric/ColorHandlersImpl.java | 17 +++++-- fabric/src/main/resources/fabric.mod.json | 3 ++ .../event/forge/EventHandlerImplClient.java | 6 +++ .../event/forge/ModBasedEventHandlerImpl.java | 49 ------------------- .../registry/forge/ColorHandlersImpl.java | 32 ++++++++---- 10 files changed, 104 insertions(+), 66 deletions(-) create mode 100644 fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java delete mode 100644 forge/src/main/java/me/shedaniel/architectury/event/forge/ModBasedEventHandlerImpl.java diff --git a/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java b/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java index 443138ca..e054b55c 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java +++ b/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java @@ -58,6 +58,12 @@ public final class EventFactory { return new EventImpl<>(function); } + @SafeVarargs + public static Event createLoop(T... typeGetter) { + if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!"); + return createLoop((Class) typeGetter.getClass().getComponentType()); + } + @SuppressWarnings("UnstableApiUsage") public static Event createLoop(Class clazz) { return of(listeners -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() { @@ -71,6 +77,12 @@ public final class EventFactory { })); } + @SafeVarargs + public static Event createInteractionResult(T... typeGetter) { + if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!"); + return createInteractionResult((Class) typeGetter.getClass().getComponentType()); + } + @SuppressWarnings("UnstableApiUsage") public static Event createInteractionResult(Class clazz) { return of(listeners -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() { @@ -87,6 +99,12 @@ public final class EventFactory { })); } + @SafeVarargs + public static Event createInteractionResultHolder(T... typeGetter) { + if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!"); + return createInteractionResultHolder((Class) typeGetter.getClass().getComponentType()); + } + @SuppressWarnings("UnstableApiUsage") public static Event createInteractionResultHolder(Class clazz) { return of(listeners -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() { @@ -103,6 +121,12 @@ public final class EventFactory { })); } + @SafeVarargs + public static Event> createConsumerLoop(T... typeGetter) { + if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!"); + return createConsumerLoop((Class) typeGetter.getClass().getComponentType()); + } + @SuppressWarnings("UnstableApiUsage") public static Event> createConsumerLoop(Class clazz) { Event> event = of(listeners -> (Consumer) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{Consumer.class}, new AbstractInvocationHandler() { @@ -124,6 +148,12 @@ public final class EventFactory { return event; } + @SafeVarargs + public static Event> createActorLoop(T... typeGetter) { + if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!"); + return createActorLoop((Class) typeGetter.getClass().getComponentType()); + } + @SuppressWarnings("UnstableApiUsage") public static Event> createActorLoop(Class clazz) { Event> event = of(listeners -> (Actor) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{Actor.class}, new AbstractInvocationHandler() { diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java index 2b727c5f..c8b2de2b 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java @@ -41,6 +41,7 @@ public interface ClientLifecycleEvent { * Invoked after a world is loaded only on client, equivalent to forge's {@code WorldEvent.Load}. */ Event CLIENT_WORLD_LOAD = EventFactory.createLoop(ClientWorldState.class); + Event CLIENT_SETUP = EventFactory.createLoop(); @Deprecated @Environment(EnvType.CLIENT) diff --git a/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java index 6851087e..e9c30d53 100644 --- a/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java +++ b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java @@ -27,17 +27,34 @@ import net.minecraft.client.color.item.ItemColor; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; +import java.util.Arrays; +import java.util.function.Supplier; + @Environment(EnvType.CLIENT) public final class ColorHandlers { private ColorHandlers() {} - @ExpectPlatform public static void registerItemColors(ItemColor color, ItemLike... items) { + registerItemColors(color, Arrays.stream(items) + .map(item -> (Supplier) () -> item) + .toArray(Supplier[]::new)); + } + + public static void registerBlockColors(BlockColor color, Block... blocks) { + registerBlockColors(color, Arrays.stream(blocks) + .map(block -> (Supplier) () -> block) + .toArray(Supplier[]::new)); + } + + @SafeVarargs + @ExpectPlatform + public static void registerItemColors(ItemColor color, Supplier... items) { throw new AssertionError(); } + @SafeVarargs @ExpectPlatform - public static void registerBlockColors(BlockColor color, Block... blocks) { + public static void registerBlockColors(BlockColor color, Supplier... blocks) { throw new AssertionError(); } } diff --git a/common/src/main/java/me/shedaniel/architectury/registry/RenderTypes.java b/common/src/main/java/me/shedaniel/architectury/registry/RenderTypes.java index 6ad0fff1..9fc7643d 100644 --- a/common/src/main/java/me/shedaniel/architectury/registry/RenderTypes.java +++ b/common/src/main/java/me/shedaniel/architectury/registry/RenderTypes.java @@ -39,6 +39,5 @@ public final class RenderTypes { public static void register(RenderType type, Fluid... fluids) { throw new AssertionError(); } - } diff --git a/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java b/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java new file mode 100644 index 00000000..85aff307 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java @@ -0,0 +1,10 @@ +package me.shedaniel.architectury.init.fabric; + +import me.shedaniel.architectury.event.events.client.ClientLifecycleEvent; +import net.minecraft.client.Minecraft; + +public class ArchitecturyClient { + public static void init() { + ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(Minecraft.getInstance()); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java index 204ea10b..6e09315b 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java @@ -25,12 +25,21 @@ import net.minecraft.client.color.item.ItemColor; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; +import java.util.Arrays; +import java.util.function.Supplier; + public class ColorHandlersImpl { - public static void registerItemColors(ItemColor color, ItemLike... items) { - ColorProviderRegistry.ITEM.register(color, items); + @SafeVarargs + public static void registerItemColors(ItemColor itemColor, Supplier... items) { + ColorProviderRegistry.ITEM.register(itemColor, Arrays.stream(items) + .map(Supplier::get) + .toArray(ItemLike[]::new)); } - public static void registerBlockColors(BlockColor color, Block... blocks) { - ColorProviderRegistry.BLOCK.register(color, blocks); + @SafeVarargs + public static void registerBlockColors(BlockColor blockColor, Supplier... blocks) { + ColorProviderRegistry.BLOCK.register(blockColor, Arrays.stream(blocks) + .map(Supplier::get) + .toArray(Block[]::new)); } } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index ceeb75ad..e4476b10 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -16,6 +16,9 @@ "main": [ "me.shedaniel.architectury.utils.fabric.GameInstanceImpl::init" ], + "client": [ + "me.shedaniel.architectury.init.fabric.ArchitecturyClient::init" + ], "modmenu": [ "me.shedaniel.architectury.compat.fabric.ModMenuCompatibility" ] diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java index c5dc285d..3e08053a 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java @@ -38,6 +38,7 @@ import net.minecraftforge.event.entity.player.ItemTooltipEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import java.util.List; @@ -275,5 +276,10 @@ public class EventHandlerImplClient { public static void event(net.minecraftforge.client.event.TextureStitchEvent.Post event) { TextureStitchEvent.POST.invoker().stitch(event.getMap()); } + + @SubscribeEvent + public static void event(FMLClientSetupEvent event) { + ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(event.getMinecraftSupplier().get()); + } } } diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/ModBasedEventHandlerImpl.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/ModBasedEventHandlerImpl.java deleted file mode 100644 index 1d44c81b..00000000 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/ModBasedEventHandlerImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of architectury. - * Copyright (C) 2020, 2021 shedaniel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package me.shedaniel.architectury.event.forge; - -import me.shedaniel.architectury.event.events.TextureStitchEvent; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.eventbus.api.SubscribeEvent; - -public class ModBasedEventHandlerImpl { - @OnlyIn(Dist.CLIENT) - public static class Client { - @SubscribeEvent - public static void event(net.minecraftforge.client.event.TextureStitchEvent.Pre event) { - TextureStitchEvent.PRE.invoker().stitch(event.getMap(), event::addSprite); - } - - @SubscribeEvent - public static void event(net.minecraftforge.client.event.TextureStitchEvent.Post event) { - TextureStitchEvent.POST.invoker().stitch(event.getMap()); - } - } - - public static class Common { - - } - - @OnlyIn(Dist.DEDICATED_SERVER) - public static class Server { - - } -} diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java index 41c6a3d2..7bb927c1 100644 --- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java @@ -29,38 +29,50 @@ import net.minecraftforge.client.event.ColorHandlerEvent; import net.minecraftforge.common.MinecraftForge; import org.apache.commons.lang3.tuple.Pair; +import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; public class ColorHandlersImpl { - private static final List> ITEM_COLORS = Lists.newArrayList(); - private static final List> BLOCK_COLORS = Lists.newArrayList(); + private static final List[]>> ITEM_COLORS = Lists.newArrayList(); + private static final List[]>> BLOCK_COLORS = Lists.newArrayList(); static { MinecraftForge.EVENT_BUS.addListener(event -> { - for (Pair pair : ITEM_COLORS) { - event.getItemColors().register(pair.getLeft(), pair.getRight()); + for (Pair[]> pair : ITEM_COLORS) { + event.getItemColors().register(pair.getLeft(), Arrays.stream(pair.getRight()) + .map(Supplier::get) + .toArray(ItemLike[]::new)); } }); MinecraftForge.EVENT_BUS.addListener(event -> { - for (Pair pair : BLOCK_COLORS) { - event.getBlockColors().register(pair.getLeft(), pair.getRight()); + for (Pair[]> pair : BLOCK_COLORS) { + event.getBlockColors().register(pair.getLeft(), Arrays.stream(pair.getRight()) + .map(Supplier::get) + .toArray(Block[]::new)); } }); } - public static void registerItemColors(ItemColor itemColor, ItemLike... items) { + @SafeVarargs + public static void registerItemColors(ItemColor itemColor, Supplier... items) { if (Minecraft.getInstance().getItemColors() == null) { ITEM_COLORS.add(Pair.of(itemColor, items)); } else { - Minecraft.getInstance().getItemColors().register(itemColor, items); + Minecraft.getInstance().getItemColors().register(itemColor, Arrays.stream(items) + .map(Supplier::get) + .toArray(ItemLike[]::new)); } } - public static void registerBlockColors(BlockColor blockColor, Block... blocks) { + @SafeVarargs + public static void registerBlockColors(BlockColor blockColor, Supplier... blocks) { if (Minecraft.getInstance().getBlockColors() == null) { BLOCK_COLORS.add(Pair.of(blockColor, blocks)); } else { - Minecraft.getInstance().getBlockColors().register(blockColor, blocks); + Minecraft.getInstance().getBlockColors().register(blockColor, Arrays.stream(blocks) + .map(Supplier::get) + .toArray(Block[]::new)); } } } From 526bbfb8ecad6d687293c4b903b12f7858d0272c Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 23 Jan 2021 02:26:27 +0800 Subject: [PATCH 10/45] Fix compilation error --- .../event/events/PlayerEvent.java | 26 +++++++-------- .../architectury/registry/ColorHandlers.java | 18 ++++++---- .../init/fabric/ArchitecturyClient.java | 19 +++++++++++ .../registry/fabric/ColorHandlersImpl.java | 25 ++++++++++---- .../registry/forge/ColorHandlersImpl.java | 33 +++++++++++-------- gradle.properties | 2 +- 6 files changed, 83 insertions(+), 40 deletions(-) diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java index 982e572f..e426ca51 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java @@ -36,19 +36,19 @@ import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; public interface PlayerEvent { - Event PLAYER_JOIN = EventFactory.createLoop(PlayerJoin.class); - Event PLAYER_QUIT = EventFactory.createLoop(PlayerQuit.class); - Event PLAYER_RESPAWN = EventFactory.createLoop(PlayerRespawn.class); - Event PLAYER_ADVANCEMENT = EventFactory.createLoop(PlayerAdvancement.class); - Event PLAYER_CLONE = EventFactory.createLoop(PlayerClone.class); - Event CRAFT_ITEM = EventFactory.createLoop(CraftItem.class); - Event SMELT_ITEM = EventFactory.createLoop(SmeltItem.class); - Event PICKUP_ITEM_PRE = EventFactory.createInteractionResult(PickupItemPredicate.class); - Event PICKUP_ITEM_POST = EventFactory.createLoop(PickupItem.class); - Event DROP_ITEM = EventFactory.createLoop(DropItem.class); - Event OPEN_MENU = EventFactory.createLoop(OpenMenu.class); - Event CLOSE_MENU = EventFactory.createLoop(CloseMenu.class); - Event BREAK_BLOCK = EventFactory.createInteractionResult(BreakBlock.class); + Event PLAYER_JOIN = EventFactory.createLoop(); + Event PLAYER_QUIT = EventFactory.createLoop(); + Event PLAYER_RESPAWN = EventFactory.createLoop(); + Event PLAYER_ADVANCEMENT = EventFactory.createLoop(); + Event PLAYER_CLONE = EventFactory.createLoop(); + Event CRAFT_ITEM = EventFactory.createLoop(); + Event SMELT_ITEM = EventFactory.createLoop(); + Event PICKUP_ITEM_PRE = EventFactory.createInteractionResult(); + Event PICKUP_ITEM_POST = EventFactory.createLoop(); + Event DROP_ITEM = EventFactory.createLoop(); + Event OPEN_MENU = EventFactory.createLoop(); + Event CLOSE_MENU = EventFactory.createLoop(); + Event BREAK_BLOCK = EventFactory.createInteractionResult(); interface PlayerJoin { void join(ServerPlayer player); diff --git a/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java index e9c30d53..48432b29 100644 --- a/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java +++ b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java @@ -35,15 +35,21 @@ public final class ColorHandlers { private ColorHandlers() {} public static void registerItemColors(ItemColor color, ItemLike... items) { - registerItemColors(color, Arrays.stream(items) - .map(item -> (Supplier) () -> item) - .toArray(Supplier[]::new)); + Supplier[] array = new Supplier[items.length]; + for (int i = 0; i < items.length; i++) { + ItemLike item = items[i]; + array[i] = () -> item; + } + registerItemColors(color, array); } public static void registerBlockColors(BlockColor color, Block... blocks) { - registerBlockColors(color, Arrays.stream(blocks) - .map(block -> (Supplier) () -> block) - .toArray(Supplier[]::new)); + Supplier[] array = new Supplier[blocks.length]; + for (int i = 0; i < blocks.length; i++) { + Block block = blocks[i]; + array[i] = () -> block; + } + registerBlockColors(color, array); } @SafeVarargs diff --git a/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java b/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java index 85aff307..4a050f16 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java +++ b/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java @@ -1,3 +1,22 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 shedaniel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + package me.shedaniel.architectury.init.fabric; import me.shedaniel.architectury.event.events.client.ClientLifecycleEvent; diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java index 6e09315b..12860dd0 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java @@ -25,21 +25,32 @@ import net.minecraft.client.color.item.ItemColor; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; -import java.util.Arrays; import java.util.function.Supplier; public class ColorHandlersImpl { @SafeVarargs public static void registerItemColors(ItemColor itemColor, Supplier... items) { - ColorProviderRegistry.ITEM.register(itemColor, Arrays.stream(items) - .map(Supplier::get) - .toArray(ItemLike[]::new)); + ColorProviderRegistry.ITEM.register(itemColor, unpackItems(items)); } @SafeVarargs public static void registerBlockColors(BlockColor blockColor, Supplier... blocks) { - ColorProviderRegistry.BLOCK.register(blockColor, Arrays.stream(blocks) - .map(Supplier::get) - .toArray(Block[]::new)); + ColorProviderRegistry.BLOCK.register(blockColor, unpackBlocks(blocks)); + } + + private static ItemLike[] unpackItems(Supplier[] items) { + ItemLike[] array = new ItemLike[items.length]; + for (int i = 0; i < items.length; i++) { + array[i] = items[i].get(); + } + return array; + } + + private static Block[] unpackBlocks(Supplier[] blocks) { + Block[] array = new Block[blocks.length]; + for (int i = 0; i < blocks.length; i++) { + array[i] = blocks[i].get(); + } + return array; } } diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java index 7bb927c1..8ed2165c 100644 --- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java @@ -29,7 +29,6 @@ import net.minecraftforge.client.event.ColorHandlerEvent; import net.minecraftforge.common.MinecraftForge; import org.apache.commons.lang3.tuple.Pair; -import java.util.Arrays; import java.util.List; import java.util.function.Supplier; @@ -40,16 +39,12 @@ public class ColorHandlersImpl { static { MinecraftForge.EVENT_BUS.addListener(event -> { for (Pair[]> pair : ITEM_COLORS) { - event.getItemColors().register(pair.getLeft(), Arrays.stream(pair.getRight()) - .map(Supplier::get) - .toArray(ItemLike[]::new)); + event.getItemColors().register(pair.getLeft(), unpackItems(pair.getRight())); } }); MinecraftForge.EVENT_BUS.addListener(event -> { for (Pair[]> pair : BLOCK_COLORS) { - event.getBlockColors().register(pair.getLeft(), Arrays.stream(pair.getRight()) - .map(Supplier::get) - .toArray(Block[]::new)); + event.getBlockColors().register(pair.getLeft(), unpackBlocks(pair.getRight())); } }); } @@ -59,9 +54,7 @@ public class ColorHandlersImpl { if (Minecraft.getInstance().getItemColors() == null) { ITEM_COLORS.add(Pair.of(itemColor, items)); } else { - Minecraft.getInstance().getItemColors().register(itemColor, Arrays.stream(items) - .map(Supplier::get) - .toArray(ItemLike[]::new)); + Minecraft.getInstance().getItemColors().register(itemColor, unpackItems(items)); } } @@ -70,9 +63,23 @@ public class ColorHandlersImpl { if (Minecraft.getInstance().getBlockColors() == null) { BLOCK_COLORS.add(Pair.of(blockColor, blocks)); } else { - Minecraft.getInstance().getBlockColors().register(blockColor, Arrays.stream(blocks) - .map(Supplier::get) - .toArray(Block[]::new)); + Minecraft.getInstance().getBlockColors().register(blockColor, unpackBlocks(blocks)); } } + + private static ItemLike[] unpackItems(Supplier[] items) { + ItemLike[] array = new ItemLike[items.length]; + for (int i = 0; i < items.length; i++) { + array[i] = items[i].get(); + } + return array; + } + + private static Block[] unpackBlocks(Supplier[] blocks) { + Block[] array = new Block[blocks.length]; + for (int i = 0; i < blocks.length; i++) { + array[i] = blocks[i].get(); + } + return array; + } } diff --git a/gradle.properties b/gradle.properties index a45ad75e..9370f5a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false minecraft_version=1.16.4 -supported_version=1.16.4 +supported_version=1.16.4/5 archives_base_name=architectury mod_version=1.3 From 27de429afbb7ec0bbafdd95a521cbb6294462cc8 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sun, 24 Jan 2021 22:16:12 +0800 Subject: [PATCH 11/45] Bump minor version to 1.4 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 9370f5a0..21506c73 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ minecraft_version=1.16.4 supported_version=1.16.4/5 archives_base_name=architectury -mod_version=1.3 +mod_version=1.4 maven_group=me.shedaniel fabric_loader_version=0.10.8 From 0c7241ddfb8d628a3f43940c2ebe49a21c3112c2 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 25 Jan 2021 03:06:41 +0100 Subject: [PATCH 12/45] Add GuiEvent.SET_SCREEN --- .../architectury/event/events/GuiEvent.java | 23 ++-- .../mixin/fabric/client/MixinMinecraft.java | 102 ++++++++++++++++-- .../main/resources/architectury.mixins.json | 35 +++++- .../event/forge/EventHandlerImplClient.java | 93 +++++++++------- 4 files changed, 194 insertions(+), 59 deletions(-) diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java index 92843b16..d29374f9 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java @@ -28,6 +28,7 @@ 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 java.util.List; @@ -49,32 +50,42 @@ public interface GuiEvent { Event INIT_POST = EventFactory.createLoop(ScreenInitPost.class); Event RENDER_PRE = EventFactory.createInteractionResult(ScreenRenderPre.class); Event RENDER_POST = EventFactory.createInteractionResult(ScreenRenderPost.class); - + + /** + * Invoked during Minecraft#setScreen, equivalent to forge's {@code GuiOpenEvent}. + */ + Event SET_SCREEN = EventFactory.createInteractionResultHolder(); + + @Environment(EnvType.CLIENT) + interface SetScreenEvent { + InteractionResultHolder modifyScreen(Screen screen); + } + @Environment(EnvType.CLIENT) interface RenderHud { void renderHud(PoseStack matrices, float tickDelta); } - + @Environment(EnvType.CLIENT) interface DebugText { void gatherText(List strings); } - + @Environment(EnvType.CLIENT) interface ScreenInitPre { InteractionResult init(Screen screen, List widgets, List children); } - + @Environment(EnvType.CLIENT) interface ScreenInitPost { void init(Screen screen, List widgets, List children); } - + @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); diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java index 5eaea2be..f908fea4 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java @@ -19,34 +19,51 @@ package me.shedaniel.architectury.mixin.fabric.client; +import me.shedaniel.architectury.event.events.GuiEvent; import me.shedaniel.architectury.event.events.InteractionEvent; import me.shedaniel.architectury.event.events.client.ClientPlayerEvent; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.ConnectScreen; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.main.GameConfig; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.HitResult; import org.jetbrains.annotations.Nullable; 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.Unique; +import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import java.io.File; + @Mixin(Minecraft.class) -public class MixinMinecraft { +public abstract class MixinMinecraft { + // @formatter:off @Shadow @Nullable public LocalPlayer player; - + @Shadow @Nullable public HitResult hitResult; - + + @Shadow public abstract void setScreen(@Nullable Screen screen); + + private @Unique boolean setScreenCancelled; + + private @Unique String hostname; + private @Unique int port; + // @formatter:on + @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/chat/NarratorChatListener;clear()V")) private void handleLogin(Screen screen, CallbackInfo ci) { ClientPlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(player); } - + @Inject(method = "startUseItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", ordinal = 1), locals = LocalCapture.CAPTURE_FAILHARD) private void rightClickAir(CallbackInfo ci, InteractionHand var1[], int var2, int var3, InteractionHand interactionHand, ItemStack itemStack) { @@ -54,9 +71,80 @@ public class MixinMinecraft { InteractionEvent.CLIENT_RIGHT_CLICK_AIR.invoker().click(player, interactionHand); } } - + @Inject(method = "startAttack", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;resetAttackStrengthTicker()V", ordinal = 0)) private void leftClickAir(CallbackInfo ci) { InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(player, InteractionHand.MAIN_HAND); } + + @ModifyVariable( + method = "setScreen", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/player/LocalPlayer;respawn()V", + shift = At.Shift.BY, + by = 2), + argsOnly = true + ) + public Screen modifyScreen(Screen screen) { + Screen old = screen; + InteractionResultHolder event = GuiEvent.SET_SCREEN.invoker().modifyScreen(screen); + if (event.getResult() == InteractionResult.FAIL) { + setScreenCancelled = true; + return old; + } + setScreenCancelled = false; + screen = event.getObject(); + if (old != null && screen != old) { + old.removed(); + } + return screen; + } + + @Inject( + method = "setScreen", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/player/LocalPlayer;respawn()V", + shift = At.Shift.BY, + by = 3), + cancellable = true + ) + public void cancelSetScreen(@Nullable Screen screen, CallbackInfo ci) { + if (setScreenCancelled) { + ci.cancel(); + } + } + + @Redirect( + method = "", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V"), + slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;resizeDisplay()V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/LoadingOverlay;registerTextures(Lnet/minecraft/client/Minecraft;)V") + ) + ) + public void minecraftWhy(Minecraft mc, Screen screen) { + } + + @Inject( + method = "", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;resizeDisplay()V"), + locals = LocalCapture.CAPTURE_FAILHARD + ) + public void saveLocals(GameConfig gc, CallbackInfo ci, File f, String string2, int j) { + hostname = string2; + port = j; + } + + @SuppressWarnings("UnresolvedMixinReference") + @Inject( + method = {"method_29338", "lambda$null$1"}, // .lambda$null$1 + at = @At("RETURN") + ) + public void registerMainScreens(CallbackInfo ci) { + if (hostname != null) { + setScreen(new ConnectScreen(new TitleScreen(), (Minecraft) ((Object) this), hostname, port)); + } else { + setScreen(new TitleScreen(true)); + } + } } diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index 33df1a24..71f945a8 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -4,16 +4,41 @@ "compatibilityLevel": "JAVA_8", "minVersion": "0.7.11", "client": [ - "client.MixinClientLevel", "client.MixinClientPacketListener", "client.MixinDebugScreenOverlay", "client.MixinGameRenderer", "client.MixinIntegratedServer", - "client.MixinKeyboardHandler", "client.MixinMinecraft", "client.MixinMouseHandler", "client.MixinMultiPlayerGameMode", "client.MixinScreen", + "client.MixinClientLevel", + "client.MixinClientPacketListener", + "client.MixinDebugScreenOverlay", + "client.MixinGameRenderer", + "client.MixinIntegratedServer", + "client.MixinKeyboardHandler", + "client.MixinMinecraft", + "client.MixinMouseHandler", + "client.MixinMultiPlayerGameMode", + "client.MixinScreen", "client.MixinTextureAtlas" ], "mixins": [ - "ExplosionPreInvoker", "LivingDeathInvoker", "MixinBlockEntityExtension", "MixinBlockItem", "MixinCommands", "MixinDedicatedServer", "MixinExplosion", - "MixinFurnaceResultSlot", "MixinItemEntity", "MixinLivingEntity", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot", - "MixinServerGamePacketListenerImpl", "MixinServerLevel", "MixinServerPlayer", "MixinServerPlayerGameMode", "PlayerAttackInvoker" + "ExplosionPreInvoker", + "LivingDeathInvoker", + "MixinBlockEntityExtension", + "MixinBlockItem", + "MixinCommands", + "MixinDedicatedServer", + "MixinExplosion", + "MixinFurnaceResultSlot", + "MixinItemEntity", + "MixinLivingEntity", + "MixinPlayer", + "MixinPlayerAdvancements", + "MixinPlayerList", + "MixinResultSlot", + "MixinServerGamePacketListenerImpl", + "MixinServerLevel", + "MixinServerPlayer", + "MixinServerPlayerGameMode", + "PlayerAttackInvoker" ], "injectors": { + "maxShiftBy": 5, "defaultRequire": 1 } } diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java index b394d242..cc70f9f2 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java @@ -27,6 +27,7 @@ import me.shedaniel.architectury.impl.TooltipEventColorContextImpl; import me.shedaniel.architectury.impl.TooltipEventPositionContextImpl; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionResult; @@ -48,7 +49,7 @@ public class EventHandlerImplClient { public static void event(ItemTooltipEvent event) { TooltipEvent.ITEM.invoker().append(event.getItemStack(), event.getToolTip(), event.getFlags()); } - + @SubscribeEvent public static void event(net.minecraftforge.event.TickEvent.ClientTickEvent event) { if (event.phase == net.minecraftforge.event.TickEvent.Phase.START) @@ -56,40 +57,40 @@ public class EventHandlerImplClient { else if (event.phase == net.minecraftforge.event.TickEvent.Phase.END) ClientTickEvent.CLIENT_POST.invoker().tick(Minecraft.getInstance()); } - + @SubscribeEvent public static void event(RenderGameOverlayEvent.Post event) { if (event.getType() == RenderGameOverlayEvent.ElementType.ALL) GuiEvent.RENDER_HUD.invoker().renderHud(event.getMatrixStack(), event.getPartialTicks()); } - + @SubscribeEvent public static void event(ClientPlayerNetworkEvent.LoggedInEvent event) { ClientPlayerEvent.CLIENT_PLAYER_JOIN.invoker().join(event.getPlayer()); } - + @SubscribeEvent public static void event(ClientPlayerNetworkEvent.LoggedOutEvent event) { ClientPlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(event.getPlayer()); } - + @SubscribeEvent public static void event(ClientPlayerNetworkEvent.RespawnEvent event) { ClientPlayerEvent.CLIENT_PLAYER_RESPAWN.invoker().respawn(event.getOldPlayer(), event.getNewPlayer()); } - + @SubscribeEvent public static void event(GuiScreenEvent.InitGuiEvent.Pre event) { if (GuiEvent.INIT_PRE.invoker().init(event.getGui(), event.getWidgetList(), (List) event.getGui().children()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.InitGuiEvent.Post event) { GuiEvent.INIT_POST.invoker().init(event.getGui(), event.getWidgetList(), (List) event.getGui().children()); } - + @SubscribeEvent public static void event(RenderGameOverlayEvent.Text event) { if (Minecraft.getInstance().options.renderDebug) { @@ -97,7 +98,7 @@ public class EventHandlerImplClient { GuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(event.getRight()); } } - + @SubscribeEvent public static void event(net.minecraftforge.client.event.ClientChatEvent event) { InteractionResultHolder process = ClientChatEvent.CLIENT.invoker().process(event.getMessage()); @@ -106,7 +107,7 @@ public class EventHandlerImplClient { if (process.getResult() == InteractionResult.FAIL) event.setCanceled(true); } - + @SubscribeEvent public static void event(ClientChatReceivedEvent event) { InteractionResultHolder process = ClientChatEvent.CLIENT_RECEIVED.invoker().process(event.getType(), event.getMessage(), event.getSenderUUID()); @@ -115,7 +116,7 @@ public class EventHandlerImplClient { if (process.getResult() == InteractionResult.FAIL) event.setCanceled(true); } - + @SubscribeEvent public static void event(WorldEvent.Save event) { if (event.getWorld() instanceof ClientLevel) { @@ -123,51 +124,61 @@ public class EventHandlerImplClient { ClientLifecycleEvent.CLIENT_WORLD_LOAD.invoker().act(world); } } - + + @SubscribeEvent + public static void event(GuiOpenEvent event) { + InteractionResultHolder result = GuiEvent.SET_SCREEN.invoker().modifyScreen(event.getGui()); + if (result.getResult() == InteractionResult.FAIL) { + event.setCanceled(true); + return; + } + event.setGui(result.getObject()); + } + @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()) == InteractionResult.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()); } - + @SubscribeEvent public static void event(PlayerInteractEvent.RightClickEmpty event) { InteractionEvent.CLIENT_RIGHT_CLICK_AIR.invoker().click(event.getPlayer(), event.getHand()); } - + @SubscribeEvent public static void event(PlayerInteractEvent.LeftClickEmpty event) { InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(event.getPlayer(), event.getHand()); } - + @SubscribeEvent public static void event(RecipesUpdatedEvent event) { RecipeUpdateEvent.EVENT.invoker().update(event.getRecipeManager()); } - + private static final ThreadLocal tooltipColorContext = ThreadLocal.withInitial(TooltipEventColorContextImpl::new); private static final ThreadLocal tooltipPositionContext = ThreadLocal.withInitial(TooltipEventPositionContextImpl::new); - + @SubscribeEvent public static void event(RenderTooltipEvent.Pre event) { if (TooltipEvent.RENDER_FORGE_PRE.invoker().renderTooltip(event.getMatrixStack(), event.getLines(), event.getX(), event.getY()) == InteractionResult.FAIL) { event.setCanceled(true); return; } - + TooltipEventPositionContextImpl positionContext = tooltipPositionContext.get(); positionContext.reset(event.getX(), event.getY()); TooltipEvent.RENDER_MODIFY_POSITION.invoker().renderTooltip(event.getMatrixStack(), positionContext); event.setX(positionContext.getTooltipX()); event.setY(positionContext.getTooltipY()); } - + @SubscribeEvent public static void event(RenderTooltipEvent.Color event) { TooltipEventColorContextImpl colorContext = tooltipColorContext.get(); @@ -180,127 +191,127 @@ public class EventHandlerImplClient { event.setBorderEnd(colorContext.getOutlineGradientBottomColor()); event.setBorderStart(colorContext.getOutlineGradientTopColor()); } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseScrollEvent.Pre event) { if (ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getScrollDelta()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseScrollEvent.Post event) { ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getScrollDelta()); } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseClickedEvent.Pre event) { if (ClientScreenInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseClickedEvent.Post event) { ClientScreenInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton()); } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseDragEvent.Pre event) { if (ClientScreenInputEvent.MOUSE_DRAGGED_PRE.invoker().mouseDragged(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getMouseButton(), event.getDragX(), event.getDragY()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseDragEvent.Post event) { ClientScreenInputEvent.MOUSE_DRAGGED_POST.invoker().mouseDragged(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getMouseButton(), event.getDragX(), event.getDragY()); } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseReleasedEvent.Pre event) { if (ClientScreenInputEvent.MOUSE_RELEASED_PRE.invoker().mouseReleased(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.MouseReleasedEvent.Post event) { ClientScreenInputEvent.MOUSE_RELEASED_PRE.invoker().mouseReleased(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton()); } - + @SubscribeEvent public static void event(GuiScreenEvent.KeyboardCharTypedEvent.Pre event) { if (ClientScreenInputEvent.CHAR_TYPED_PRE.invoker().charTyped(Minecraft.getInstance(), event.getGui(), event.getCodePoint(), event.getModifiers()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.KeyboardCharTypedEvent.Post event) { ClientScreenInputEvent.CHAR_TYPED_POST.invoker().charTyped(Minecraft.getInstance(), event.getGui(), event.getCodePoint(), event.getModifiers()); } - + @SubscribeEvent public static void event(GuiScreenEvent.KeyboardKeyPressedEvent.Pre event) { if (ClientScreenInputEvent.KEY_PRESSED_PRE.invoker().keyPressed(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.KeyboardKeyPressedEvent.Post event) { ClientScreenInputEvent.KEY_PRESSED_POST.invoker().keyPressed(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers()); } - + @SubscribeEvent public static void event(GuiScreenEvent.KeyboardKeyReleasedEvent.Pre event) { if (ClientScreenInputEvent.KEY_RELEASED_PRE.invoker().keyReleased(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(GuiScreenEvent.KeyboardKeyReleasedEvent.Post event) { ClientScreenInputEvent.KEY_RELEASED_POST.invoker().keyReleased(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers()); } - + @SubscribeEvent public static void event(InputEvent.MouseScrollEvent event) { if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getScrollDelta()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(InputEvent.RawMouseEvent event) { if (ClientRawInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getMods()) == InteractionResult.FAIL) { event.setCanceled(true); } } - + @SubscribeEvent public static void event(InputEvent.MouseInputEvent event) { ClientRawInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getMods()); } - + @SubscribeEvent public static void event(InputEvent.KeyInputEvent event) { ClientRawInputEvent.KEY_PRESSED.invoker().keyPressed(Minecraft.getInstance(), event.getKey(), event.getScanCode(), event.getAction(), event.getModifiers()); } - + @OnlyIn(Dist.CLIENT) public static class ModBasedEventHandler { @SubscribeEvent public static void event(net.minecraftforge.client.event.TextureStitchEvent.Pre event) { TextureStitchEvent.PRE.invoker().stitch(event.getMap(), event::addSprite); } - + @SubscribeEvent public static void event(net.minecraftforge.client.event.TextureStitchEvent.Post event) { TextureStitchEvent.POST.invoker().stitch(event.getMap()); } - + @SubscribeEvent public static void event(FMLClientSetupEvent event) { ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(event.getMinecraftSupplier().get()); From ca8af3f754347238ae2a9ddc5e3a198bca5a8dfe Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 25 Jan 2021 03:48:06 +0100 Subject: [PATCH 13/45] Fix shader effects location being hardcoded to minecraft namespace --- .../fabric/client/MixinEffectInstance.java | 38 +++++++++++++++++++ .../main/resources/architectury.mixins.json | 37 +++++++++++++++--- 2 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java new file mode 100644 index 00000000..838e8d31 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java @@ -0,0 +1,38 @@ +package me.shedaniel.architectury.mixin.fabric.client; + +import com.mojang.blaze3d.shaders.Program; +import net.minecraft.client.renderer.EffectInstance; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Unique +@Mixin(EffectInstance.class) +public class MixinEffectInstance { + @Redirect( + method = "", + at = @At(value = "NEW", + target = "(Ljava/lang/String;)Lnet/minecraft/resources/ResourceLocation;", + ordinal = 0) + ) + private ResourceLocation mojangPls(String _0, ResourceManager rm, String str) { + return mojangPls(new ResourceLocation(str), ".json"); + } + + @Redirect( + method = "getOrCreate", + at = @At(value = "NEW", + target = "(Ljava/lang/String;)Lnet/minecraft/resources/ResourceLocation;", + ordinal = 0) + ) + private static ResourceLocation mojangPls(String _0, ResourceManager rm, Program.Type type, String str) { + return mojangPls(new ResourceLocation(str), type.getExtension()); + } + + private static ResourceLocation mojangPls(ResourceLocation rl, String ext) { + return new ResourceLocation(rl.getNamespace(), "shaders/program/" + rl.getPath() + ext); + } +} diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index 33df1a24..9d2fa087 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -4,14 +4,39 @@ "compatibilityLevel": "JAVA_8", "minVersion": "0.7.11", "client": [ - "client.MixinClientLevel", "client.MixinClientPacketListener", "client.MixinDebugScreenOverlay", "client.MixinGameRenderer", "client.MixinIntegratedServer", - "client.MixinKeyboardHandler", "client.MixinMinecraft", "client.MixinMouseHandler", "client.MixinMultiPlayerGameMode", "client.MixinScreen", - "client.MixinTextureAtlas" + "client.MixinClientLevel", + "client.MixinClientPacketListener", + "client.MixinDebugScreenOverlay", + "client.MixinGameRenderer", + "client.MixinIntegratedServer", + "client.MixinKeyboardHandler", + "client.MixinMinecraft", + "client.MixinMouseHandler", + "client.MixinMultiPlayerGameMode", + "client.MixinScreen", + "client.MixinTextureAtlas", + "client.MixinEffectInstance" ], "mixins": [ - "ExplosionPreInvoker", "LivingDeathInvoker", "MixinBlockEntityExtension", "MixinBlockItem", "MixinCommands", "MixinDedicatedServer", "MixinExplosion", - "MixinFurnaceResultSlot", "MixinItemEntity", "MixinLivingEntity", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot", - "MixinServerGamePacketListenerImpl", "MixinServerLevel", "MixinServerPlayer", "MixinServerPlayerGameMode", "PlayerAttackInvoker" + "ExplosionPreInvoker", + "LivingDeathInvoker", + "MixinBlockEntityExtension", + "MixinBlockItem", + "MixinCommands", + "MixinDedicatedServer", + "MixinExplosion", + "MixinFurnaceResultSlot", + "MixinItemEntity", + "MixinLivingEntity", + "MixinPlayer", + "MixinPlayerAdvancements", + "MixinPlayerList", + "MixinResultSlot", + "MixinServerGamePacketListenerImpl", + "MixinServerLevel", + "MixinServerPlayer", + "MixinServerPlayerGameMode", + "PlayerAttackInvoker" ], "injectors": { "defaultRequire": 1 From 5d4a779d05b8ccb8cf2e8ec4fdb0eda13d3c0c3f Mon Sep 17 00:00:00 2001 From: shedaniel Date: Mon, 25 Jan 2021 11:07:09 +0800 Subject: [PATCH 14/45] Event cleanups --- .../architectury/event/events/ChatEvent.java | 2 +- .../event/events/CommandPerformEvent.java | 2 +- .../events/CommandRegistrationEvent.java | 2 +- .../event/events/EntityEvent.java | 8 +++--- .../event/events/ExplosionEvent.java | 4 +-- .../architectury/event/events/GuiEvent.java | 14 +++++----- .../event/events/InteractionEvent.java | 12 ++++---- .../event/events/LifecycleEvent.java | 16 +++++------ .../event/events/RecipeUpdateEvent.java | 2 +- .../event/events/TextureStitchEvent.java | 4 +-- .../architectury/event/events/TickEvent.java | 12 ++++---- .../event/events/TooltipEvent.java | 10 +++---- .../event/events/client/ClientChatEvent.java | 4 +-- .../events/client/ClientLifecycleEvent.java | 6 ++-- .../events/client/ClientPlayerEvent.java | 6 ++-- .../events/client/ClientRawInputEvent.java | 8 +++--- .../events/client/ClientScreenInputEvent.java | 28 +++++++++---------- .../event/events/client/ClientTickEvent.java | 8 +++--- 18 files changed, 74 insertions(+), 74 deletions(-) diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java index 8a019ead..2bb0882a 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/ChatEvent.java @@ -30,7 +30,7 @@ public interface ChatEvent { /** * Invoked when server receives a message, equivalent to forge's {@code ServerChatEvent}. */ - Event SERVER = EventFactory.createInteractionResultHolder(Server.class); + Event SERVER = EventFactory.createInteractionResultHolder(); interface Server { @NotNull diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java index 0c51aaa9..99b32364 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/CommandPerformEvent.java @@ -31,7 +31,7 @@ public class CommandPerformEvent { /** * Invoked after server parses a command but before server executes it, equivalent to forge's {@code CommandEvent}. */ - public static final Event> EVENT = EventFactory.createActorLoop(CommandPerformEvent.class); + public static final Event> EVENT = EventFactory.createActorLoop(); @NotNull private ParseResults results; diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java index 58a1e0d1..97103893 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/CommandRegistrationEvent.java @@ -29,7 +29,7 @@ public interface CommandRegistrationEvent { /** * Invoked after server registers its commands, equivalent to forge's {@code RegisterCommandsEvent} and fabric's {@code CommandRegistrationCallback}. */ - Event EVENT = EventFactory.createLoop(CommandRegistrationEvent.class); + Event EVENT = EventFactory.createLoop(); void register(CommandDispatcher dispatcher, Commands.CommandSelection selection); } diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java index 3f251155..82b5c629 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java @@ -34,16 +34,16 @@ public interface EntityEvent { /** * Invoked before LivingEntity#die, equivalent to forge's {@code LivingDeathEvent}. */ - Event LIVING_DEATH = EventFactory.createInteractionResult(LivingDeath.class); + Event LIVING_DEATH = EventFactory.createInteractionResult(); /** * Invoked before LivingEntity#hurt, equivalent to forge's {@code LivingAttackEvent}. */ - Event LIVING_ATTACK = EventFactory.createInteractionResult(LivingAttack.class); + Event LIVING_ATTACK = EventFactory.createInteractionResult(); /** * Invoked before entity is added to a world, equivalent to forge's {@code EntityJoinWorldEvent}. */ - Event ADD = EventFactory.createInteractionResult(Add.class); - Event PLACE_BLOCK = EventFactory.createInteractionResult(PlaceBlock.class); + Event ADD = EventFactory.createInteractionResult(); + Event PLACE_BLOCK = EventFactory.createInteractionResult(); interface LivingDeath { InteractionResult die(LivingEntity entity, DamageSource source); diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/ExplosionEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/ExplosionEvent.java index dc0ebe07..38bc9e9c 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/ExplosionEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/ExplosionEvent.java @@ -29,8 +29,8 @@ import net.minecraft.world.level.Level; import java.util.List; public interface ExplosionEvent { - Event
 PRE = EventFactory.createInteractionResult(Pre.class);
-    Event DETONATE = EventFactory.createInteractionResult(Detonate.class);
+    Event
 PRE = EventFactory.createInteractionResult();
+    Event DETONATE = EventFactory.createInteractionResult();
     
     interface Pre {
         InteractionResult explode(Level world, Explosion explosion);
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
index 92843b16..d606eeb4 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
@@ -36,19 +36,19 @@ public interface GuiEvent {
     /**
      * Invoked after in-game hud is rendered, equivalent to forge's {@code RenderGameOverlayEvent.Post@ElementType#ALL} and fabric's {@code HudRenderCallback}.
      */
-    Event RENDER_HUD = EventFactory.createLoop(RenderHud.class);
-    Event DEBUG_TEXT_LEFT = EventFactory.createLoop(DebugText.class);
-    Event DEBUG_TEXT_RIGHT = EventFactory.createLoop(DebugText.class);
+    Event RENDER_HUD = EventFactory.createLoop();
+    Event DEBUG_TEXT_LEFT = EventFactory.createLoop();
+    Event DEBUG_TEXT_RIGHT = EventFactory.createLoop();
     /**
      * Invoked during Screen#init after previous widgets are cleared, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Pre}.
      */
-    Event INIT_PRE = EventFactory.createInteractionResult(ScreenInitPre.class);
+    Event INIT_PRE = EventFactory.createInteractionResult();
     /**
      * Invoked after Screen#init, equivalent to forge's {@code GuiScreenEvent.InitGuiEvent.Post}.
      */
-    Event INIT_POST = EventFactory.createLoop(ScreenInitPost.class);
-    Event RENDER_PRE = EventFactory.createInteractionResult(ScreenRenderPre.class);
-    Event RENDER_POST = EventFactory.createInteractionResult(ScreenRenderPost.class);
+    Event INIT_POST = EventFactory.createLoop();
+    Event RENDER_PRE = EventFactory.createInteractionResult();
+    Event RENDER_POST = EventFactory.createInteractionResult();
     
     @Environment(EnvType.CLIENT)
     interface RenderHud {
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java
index e10e0726..286380e2 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java
@@ -32,12 +32,12 @@ import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.level.block.state.BlockState;
 
 public interface InteractionEvent {
-    Event LEFT_CLICK_BLOCK = EventFactory.createInteractionResult(LeftClickBlock.class);
-    Event RIGHT_CLICK_BLOCK = EventFactory.createInteractionResult(RightClickBlock.class);
-    Event RIGHT_CLICK_ITEM = EventFactory.createInteractionResultHolder(RightClickItem.class);
-    Event CLIENT_LEFT_CLICK_AIR = EventFactory.createLoop(ClientLeftClickAir.class);
-    Event CLIENT_RIGHT_CLICK_AIR = EventFactory.createLoop(ClientRightClickAir.class);
-    Event INTERACT_ENTITY = EventFactory.createInteractionResult(InteractEntity.class);
+    Event LEFT_CLICK_BLOCK = EventFactory.createInteractionResult();
+    Event RIGHT_CLICK_BLOCK = EventFactory.createInteractionResult();
+    Event RIGHT_CLICK_ITEM = EventFactory.createInteractionResultHolder();
+    Event CLIENT_LEFT_CLICK_AIR = EventFactory.createLoop();
+    Event CLIENT_RIGHT_CLICK_AIR = EventFactory.createLoop();
+    Event INTERACT_ENTITY = EventFactory.createInteractionResult();
     
     interface RightClickBlock {
         InteractionResult click(Player player, InteractionHand hand, BlockPos pos, Direction face);
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java
index 994b0b6d..1fb689e1 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java
@@ -29,35 +29,35 @@ public interface LifecycleEvent {
     /**
      * Invoked when server is starting, equivalent to forge's {@code FMLServerAboutToStartEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STARTING}.
      */
-    Event SERVER_BEFORE_START = EventFactory.createLoop(ServerState.class);
+    Event SERVER_BEFORE_START = EventFactory.createLoop();
     /**
      * Invoked when server is starting, equivalent to forge's {@code FMLServerStartingEvent}.
      */
-    Event SERVER_STARTING = EventFactory.createLoop(ServerState.class);
+    Event SERVER_STARTING = EventFactory.createLoop();
     /**
      * Invoked when server has started, equivalent to forge's {@code FMLServerStartedEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STARTED}.
      */
-    Event SERVER_STARTED = EventFactory.createLoop(ServerState.class);
+    Event SERVER_STARTED = EventFactory.createLoop();
     /**
      * Invoked when server is stopping, equivalent to forge's {@code FMLServerStoppingEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STOPPING}.
      */
-    Event SERVER_STOPPING = EventFactory.createLoop(ServerState.class);
+    Event SERVER_STOPPING = EventFactory.createLoop();
     /**
      * Invoked when server has stopped, equivalent to forge's {@code FMLServerStoppedEvent} and fabric's {@code ServerLifecycleEvents#SERVER_STOPPED}.
      */
-    Event SERVER_STOPPED = EventFactory.createLoop(ServerState.class);
+    Event SERVER_STOPPED = EventFactory.createLoop();
     /**
      * Invoked after a world is loaded only on server, equivalent to forge's {@code WorldEvent.Load} and fabric's {@code ServerWorldEvents#LOAD}.
      */
-    Event SERVER_WORLD_LOAD = EventFactory.createLoop(ServerWorldState.class);
+    Event SERVER_WORLD_LOAD = EventFactory.createLoop();
     /**
      * Invoked after a world is unloaded, equivalent to forge's {@code WorldEvent.Unload} and fabric's {@code ServerWorldEvents#UNLOAD}.
      */
-    Event SERVER_WORLD_UNLOAD = EventFactory.createLoop(ServerWorldState.class);
+    Event SERVER_WORLD_UNLOAD = EventFactory.createLoop();
     /**
      * Invoked during a world is saved, equivalent to forge's {@code WorldEvent.Save}.
      */
-    Event SERVER_WORLD_SAVE = EventFactory.createLoop(ServerWorldState.class);
+    Event SERVER_WORLD_SAVE = EventFactory.createLoop();
     
     interface InstanceState {
         void stateChanged(T instance);
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/RecipeUpdateEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/RecipeUpdateEvent.java
index 0a29d490..bccd8681 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/RecipeUpdateEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/RecipeUpdateEvent.java
@@ -27,7 +27,7 @@ import net.minecraft.world.item.crafting.RecipeManager;
 
 @Environment(EnvType.CLIENT)
 public interface RecipeUpdateEvent {
-    Event EVENT = EventFactory.createLoop(RecipeUpdateEvent.class);
+    Event EVENT = EventFactory.createLoop();
     
     void update(RecipeManager recipeManager);
 }
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/TextureStitchEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/TextureStitchEvent.java
index ee898135..8079675c 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/TextureStitchEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/TextureStitchEvent.java
@@ -30,8 +30,8 @@ import java.util.function.Consumer;
 
 @Environment(EnvType.CLIENT)
 public interface TextureStitchEvent {
-    Event
 PRE = EventFactory.createLoop(Pre.class);
-    Event POST = EventFactory.createLoop(Post.class);
+    Event
 PRE = EventFactory.createLoop();
+    Event POST = EventFactory.createLoop();
     
     @Environment(EnvType.CLIENT)
     interface Pre {
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java
index 4cb693e9..fcfd20fc 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java
@@ -26,12 +26,12 @@ import net.minecraft.server.level.ServerLevel;
 import net.minecraft.world.level.Level;
 
 public interface TickEvent {
-    Event SERVER_PRE = EventFactory.createLoop(Server.class);
-    Event SERVER_POST = EventFactory.createLoop(Server.class);
-    Event SERVER_WORLD_PRE = EventFactory.createLoop(ServerWorld.class);
-    Event SERVER_WORLD_POST = EventFactory.createLoop(ServerWorld.class);
-    Event PLAYER_PRE = EventFactory.createLoop(Player.class);
-    Event PLAYER_POST = EventFactory.createLoop(Player.class);
+    Event SERVER_PRE = EventFactory.createLoop();
+    Event SERVER_POST = EventFactory.createLoop();
+    Event SERVER_WORLD_PRE = EventFactory.createLoop();
+    Event SERVER_WORLD_POST = EventFactory.createLoop();
+    Event PLAYER_PRE = EventFactory.createLoop();
+    Event PLAYER_POST = EventFactory.createLoop();
     
     void tick(T instance);
     
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java
index 96401caf..673994e0 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java
@@ -35,17 +35,17 @@ import java.util.List;
 
 @Environment(EnvType.CLIENT)
 public interface TooltipEvent {
-    Event ITEM = EventFactory.createLoop(Item.class);
+    Event ITEM = EventFactory.createLoop();
     /**
      * Render vanilla events are not invoked on the forge side.
      */
-    Event RENDER_VANILLA_PRE = EventFactory.createInteractionResult(RenderVanilla.class);
+    Event RENDER_VANILLA_PRE = EventFactory.createInteractionResult();
     /**
      * Render forge events are only invoked on the forge side.
      */
-    Event RENDER_FORGE_PRE = EventFactory.createInteractionResult(RenderForge.class);
-    Event RENDER_MODIFY_POSITION = EventFactory.createInteractionResult(RenderModifyPosition.class);
-    Event RENDER_MODIFY_COLOR = EventFactory.createInteractionResult(RenderModifyColor.class);
+    Event RENDER_FORGE_PRE = EventFactory.createInteractionResult();
+    Event RENDER_MODIFY_POSITION = EventFactory.createInteractionResult();
+    Event RENDER_MODIFY_COLOR = EventFactory.createInteractionResult();
     
     @Environment(EnvType.CLIENT)
     interface Item {
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientChatEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientChatEvent.java
index 4b10980f..3ae2273a 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientChatEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientChatEvent.java
@@ -36,11 +36,11 @@ public interface ClientChatEvent {
     /**
      * Invoked when client tries to send a message, equivalent to forge's {@code ClientChatEvent}.
      */
-    Event CLIENT = EventFactory.createInteractionResultHolder(Client.class);
+    Event CLIENT = EventFactory.createInteractionResultHolder();
     /**
      * Invoked when client receives a message, equivalent to forge's {@code ClientChatReceivedEvent}.
      */
-    Event CLIENT_RECEIVED = EventFactory.createInteractionResultHolder(ClientReceived.class);
+    Event CLIENT_RECEIVED = EventFactory.createInteractionResultHolder();
     
     @Environment(EnvType.CLIENT)
     interface Client {
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java
index c8b2de2b..ca650224 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java
@@ -32,15 +32,15 @@ public interface ClientLifecycleEvent {
     /**
      * Invoked when client has been initialised, not available in forge.
      */
-    @Deprecated Event CLIENT_STARTED = EventFactory.createLoop(ClientState.class);
+    @Deprecated Event CLIENT_STARTED = EventFactory.createLoop();
     /**
      * Invoked when client is initialising, not available in forge.
      */
-    @Deprecated Event CLIENT_STOPPING = EventFactory.createLoop(ClientState.class);
+    @Deprecated Event CLIENT_STOPPING = EventFactory.createLoop();
     /**
      * Invoked after a world is loaded only on client, equivalent to forge's {@code WorldEvent.Load}.
      */
-    Event CLIENT_WORLD_LOAD = EventFactory.createLoop(ClientWorldState.class);
+    Event CLIENT_WORLD_LOAD = EventFactory.createLoop();
     Event CLIENT_SETUP = EventFactory.createLoop();
     
     @Deprecated
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java
index 81efa1cd..cd53ab52 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientPlayerEvent.java
@@ -28,9 +28,9 @@ import org.jetbrains.annotations.Nullable;
 
 @Environment(EnvType.CLIENT)
 public interface ClientPlayerEvent {
-    Event CLIENT_PLAYER_JOIN = EventFactory.createLoop(ClientPlayerJoin.class);
-    Event CLIENT_PLAYER_QUIT = EventFactory.createLoop(ClientPlayerQuit.class);
-    Event CLIENT_PLAYER_RESPAWN = EventFactory.createLoop(ClientPlayerRespawn.class);
+    Event CLIENT_PLAYER_JOIN = EventFactory.createLoop();
+    Event CLIENT_PLAYER_QUIT = EventFactory.createLoop();
+    Event CLIENT_PLAYER_RESPAWN = EventFactory.createLoop();
     
     @Environment(EnvType.CLIENT)
     interface ClientPlayerJoin {
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java
index 587b494a..5b4c759e 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientRawInputEvent.java
@@ -31,19 +31,19 @@ public interface ClientRawInputEvent {
     /**
      * Invoked after the mouse has scrolled, but doesn't have a screen opened, and in a world, equivalent to forge's {@code InputEvent.MouseScrollEvent}.
      */
-    Event MOUSE_SCROLLED = EventFactory.createInteractionResult(MouseScrolled.class);
+    Event MOUSE_SCROLLED = EventFactory.createInteractionResult();
     /**
      * Invoked after the mouse has clicked, before the screen intercepts, equivalent to forge's {@code InputEvent.RawMouseEvent}.
      */
-    Event MOUSE_CLICKED_PRE = EventFactory.createInteractionResult(MouseClicked.class);
+    Event MOUSE_CLICKED_PRE = EventFactory.createInteractionResult();
     /**
      * Invoked after the mouse has clicked, after the screen intercepts, equivalent to forge's {@code InputEvent.MouseInputEvent}.
      */
-    Event MOUSE_CLICKED_POST = EventFactory.createInteractionResult(MouseClicked.class);
+    Event MOUSE_CLICKED_POST = EventFactory.createInteractionResult();
     /**
      * Invoked after a key was pressed, after the screen intercepts, equivalent to forge's {@code InputEvent.KeyInputEvent}.
      */
-    Event KEY_PRESSED = EventFactory.createInteractionResult(KeyPressed.class);
+    Event KEY_PRESSED = EventFactory.createInteractionResult();
     
     interface KeyPressed {
         InteractionResult keyPressed(Minecraft client, int keyCode, int scanCode, int action, int modifiers);
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientScreenInputEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientScreenInputEvent.java
index 80cd1201..ceeedbf7 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientScreenInputEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientScreenInputEvent.java
@@ -29,20 +29,20 @@ import net.minecraft.world.InteractionResult;
 
 @Environment(EnvType.CLIENT)
 public interface ClientScreenInputEvent {
-    Event MOUSE_SCROLLED_PRE = EventFactory.createInteractionResult(MouseScrolled.class);
-    Event MOUSE_SCROLLED_POST = EventFactory.createInteractionResult(MouseScrolled.class);
-    Event MOUSE_CLICKED_PRE = EventFactory.createInteractionResult(MouseClicked.class);
-    Event MOUSE_CLICKED_POST = EventFactory.createInteractionResult(MouseClicked.class);
-    Event MOUSE_RELEASED_PRE = EventFactory.createInteractionResult(MouseReleased.class);
-    Event MOUSE_RELEASED_POST = EventFactory.createInteractionResult(MouseReleased.class);
-    Event MOUSE_DRAGGED_PRE = EventFactory.createInteractionResult(MouseDragged.class);
-    Event MOUSE_DRAGGED_POST = EventFactory.createInteractionResult(MouseDragged.class);
-    Event CHAR_TYPED_PRE = EventFactory.createInteractionResult(KeyTyped.class);
-    Event CHAR_TYPED_POST = EventFactory.createInteractionResult(KeyTyped.class);
-    Event KEY_PRESSED_PRE = EventFactory.createInteractionResult(KeyPressed.class);
-    Event KEY_PRESSED_POST = EventFactory.createInteractionResult(KeyPressed.class);
-    Event KEY_RELEASED_PRE = EventFactory.createInteractionResult(KeyReleased.class);
-    Event KEY_RELEASED_POST = EventFactory.createInteractionResult(KeyReleased.class);
+    Event MOUSE_SCROLLED_PRE = EventFactory.createInteractionResult();
+    Event MOUSE_SCROLLED_POST = EventFactory.createInteractionResult();
+    Event MOUSE_CLICKED_PRE = EventFactory.createInteractionResult();
+    Event MOUSE_CLICKED_POST = EventFactory.createInteractionResult();
+    Event MOUSE_RELEASED_PRE = EventFactory.createInteractionResult();
+    Event MOUSE_RELEASED_POST = EventFactory.createInteractionResult();
+    Event MOUSE_DRAGGED_PRE = EventFactory.createInteractionResult();
+    Event MOUSE_DRAGGED_POST = EventFactory.createInteractionResult();
+    Event CHAR_TYPED_PRE = EventFactory.createInteractionResult();
+    Event CHAR_TYPED_POST = EventFactory.createInteractionResult();
+    Event KEY_PRESSED_PRE = EventFactory.createInteractionResult();
+    Event KEY_PRESSED_POST = EventFactory.createInteractionResult();
+    Event KEY_RELEASED_PRE = EventFactory.createInteractionResult();
+    Event KEY_RELEASED_POST = EventFactory.createInteractionResult();
     
     interface KeyPressed {
         InteractionResult keyPressed(Minecraft client, Screen screen, int keyCode, int scanCode, int modifiers);
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientTickEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientTickEvent.java
index 85f452fc..56b5e6ae 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientTickEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientTickEvent.java
@@ -28,10 +28,10 @@ import net.minecraft.client.multiplayer.ClientLevel;
 
 @Environment(EnvType.CLIENT)
 public interface ClientTickEvent {
-    Event CLIENT_PRE = EventFactory.createLoop(Client.class);
-    Event CLIENT_POST = EventFactory.createLoop(Client.class);
-    Event CLIENT_WORLD_PRE = EventFactory.createLoop(ClientWorld.class);
-    Event CLIENT_WORLD_POST = EventFactory.createLoop(ClientWorld.class);
+    Event CLIENT_PRE = EventFactory.createLoop();
+    Event CLIENT_POST = EventFactory.createLoop();
+    Event CLIENT_WORLD_PRE = EventFactory.createLoop();
+    Event CLIENT_WORLD_POST = EventFactory.createLoop();
     
     void tick(T instance);
     

From a4beace95cde5a4e5680ce128582cf40dbdc6fb9 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 11:09:20 +0800
Subject: [PATCH 15/45] Registry API to create modded registries, close #21

---
 build.gradle                                  |  4 +-
 .../core/AbstractRecipeSerializer.java        |  2 +-
 .../architectury/core/RegistryEntry.java      | 26 ++++++
 .../architectury/registry/Registries.java     | 18 +++-
 .../registry/registries/RegistryBuilder.java  | 42 ++++++++++
 .../registry/registries/RegistryOption.java   | 23 ++++++
 .../registries/StandardRegistryOption.java    | 31 +++++++
 .../registry/fabric/RegistriesImpl.java       | 37 +++++++++
 ...erializer.java => MixinRegistryEntry.java} |  6 +-
 .../plugin/forge/ArchitecturyMixinPlugin.java |  6 +-
 .../registry/forge/RegistriesImpl.java        | 82 +++++++++++++++----
 .../main/resources/architectury.mixins.json   |  2 +-
 12 files changed, 249 insertions(+), 30 deletions(-)
 create mode 100644 common/src/main/java/me/shedaniel/architectury/core/RegistryEntry.java
 create mode 100644 common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryBuilder.java
 create mode 100644 common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryOption.java
 create mode 100644 common/src/main/java/me/shedaniel/architectury/registry/registries/StandardRegistryOption.java
 rename forge/src/main/java/me/shedaniel/architectury/mixin/forge/{MixinAbstractRecipeSerializer.java => MixinRegistryEntry.java} (86%)

diff --git a/build.gradle b/build.gradle
index fddfe4ac..bf8f47dc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 plugins {
-    id "architectury-plugin" version "2.0.57"
-    id "forgified-fabric-loom" version "0.6.49" apply false
+    id "architectury-plugin" version "2.0.59"
+    id "forgified-fabric-loom" version "0.6.53" apply false
     id "org.cadixdev.licenser" version "0.5.0"
     id "com.jfrog.bintray" version "1.8.4"
     id "com.matthewprenger.cursegradle" version "1.4.0" apply false
diff --git a/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java
index bdde8377..d67f9d92 100644
--- a/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java
+++ b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java
@@ -25,5 +25,5 @@ import net.minecraft.world.item.crafting.RecipeSerializer;
 /**
  * The equivalent of {@link RecipeSerializer} to use in common that has forge registry entries extended.
  */
-public abstract class AbstractRecipeSerializer> implements RecipeSerializer {
+public abstract class AbstractRecipeSerializer> extends RegistryEntry implements RecipeSerializer {
 }
diff --git a/common/src/main/java/me/shedaniel/architectury/core/RegistryEntry.java b/common/src/main/java/me/shedaniel/architectury/core/RegistryEntry.java
new file mode 100644
index 00000000..77cd8e1d
--- /dev/null
+++ b/common/src/main/java/me/shedaniel/architectury/core/RegistryEntry.java
@@ -0,0 +1,26 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.core;
+
+/**
+ * An entry in registries, will extend {@code ForgeRegistryEntry} on forge.
+ */
+public class RegistryEntry {
+}
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/Registries.java b/common/src/main/java/me/shedaniel/architectury/registry/Registries.java
index 9ce5d9a6..60377776 100644
--- a/common/src/main/java/me/shedaniel/architectury/registry/Registries.java
+++ b/common/src/main/java/me/shedaniel/architectury/registry/Registries.java
@@ -20,9 +20,12 @@
 package me.shedaniel.architectury.registry;
 
 import me.shedaniel.architectury.annotations.ExpectPlatform;
+import me.shedaniel.architectury.core.RegistryEntry;
+import me.shedaniel.architectury.registry.registries.RegistryBuilder;
 import net.minecraft.resources.ResourceKey;
 import net.minecraft.resources.ResourceLocation;
 import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.Map;
@@ -36,6 +39,7 @@ public final class Registries {
     private final RegistryProvider provider;
     private final String modId;
     
+    @NotNull
     public static Registries get(String modId) {
         return REGISTRIES.computeIfAbsent(modId, Registries::new);
     }
@@ -45,15 +49,24 @@ public final class Registries {
         this.modId = modId;
     }
     
+    @NotNull
     public  Registry get(ResourceKey> key) {
         return this.provider.get(key);
     }
     
+    @NotNull
     @Deprecated
     public  Registry get(net.minecraft.core.Registry registry) {
         return this.provider.get(registry);
     }
     
+    @NotNull
+    @SafeVarargs
+    public final > RegistryBuilder builder(ResourceLocation registryId, T... typeGetter) {
+        if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!");
+        return this.provider.builder((Class) typeGetter.getClass().getComponentType(), registryId);
+    }
+    
     /**
      * Forge: If the object is {@code IForgeRegistryEntry}, use `getRegistryName`, else null
      * Fabric: Use registry
@@ -79,8 +92,8 @@ public final class Registries {
      * Forge: If the object is {@code IForgeRegistryEntry}, use `getRegistryName`, else null
      * Fabric: null
      */
-    @Deprecated
     @Nullable
+    @Deprecated
     public static  ResourceLocation getRegistryName(T object) {
         return getId(object, (ResourceKey>) null);
     }
@@ -90,6 +103,7 @@ public final class Registries {
         throw new AssertionError();
     }
     
+    @NotNull
     public String getModId() {
         return modId;
     }
@@ -100,5 +114,7 @@ public final class Registries {
         
         @Deprecated
          Registry get(net.minecraft.core.Registry registry);
+        
+        > RegistryBuilder builder(Class type, ResourceLocation registryId);
     }
 }
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryBuilder.java b/common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryBuilder.java
new file mode 100644
index 00000000..9f2df633
--- /dev/null
+++ b/common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryBuilder.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry.registries;
+
+import me.shedaniel.architectury.core.RegistryEntry;
+import me.shedaniel.architectury.registry.Registry;
+import org.jetbrains.annotations.NotNull;
+
+public interface RegistryBuilder> {
+    @NotNull
+    Registry build();
+    
+    @NotNull
+    RegistryBuilder option(@NotNull RegistryOption option);
+    
+    @NotNull
+    default RegistryBuilder saveToDisc() {
+        return option(StandardRegistryOption.SAVE_TO_DISC);
+    }
+    
+    @NotNull
+    default RegistryBuilder syncToClients() {
+        return option(StandardRegistryOption.SYNC_TO_CLIENTS);
+    }
+}
\ No newline at end of file
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryOption.java b/common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryOption.java
new file mode 100644
index 00000000..8b7bebb5
--- /dev/null
+++ b/common/src/main/java/me/shedaniel/architectury/registry/registries/RegistryOption.java
@@ -0,0 +1,23 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry.registries;
+
+public interface RegistryOption {
+}
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/registries/StandardRegistryOption.java b/common/src/main/java/me/shedaniel/architectury/registry/registries/StandardRegistryOption.java
new file mode 100644
index 00000000..34783086
--- /dev/null
+++ b/common/src/main/java/me/shedaniel/architectury/registry/registries/StandardRegistryOption.java
@@ -0,0 +1,31 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry.registries;
+
+public enum StandardRegistryOption implements RegistryOption {
+    /**
+     * Denote that the registry should save to disc and persist. Defaulted false.
+     */
+    SAVE_TO_DISC,
+    /**
+     * Denote that the registry should sync its contents to clients. Defaulted false.
+     */
+    SYNC_TO_CLIENTS,
+}
diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
index d2218150..2287adb8 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
@@ -20,9 +20,16 @@
 package me.shedaniel.architectury.registry.fabric;
 
 import com.google.common.base.Objects;
+import me.shedaniel.architectury.core.RegistryEntry;
 import me.shedaniel.architectury.registry.Registries;
 import me.shedaniel.architectury.registry.Registry;
 import me.shedaniel.architectury.registry.RegistrySupplier;
+import me.shedaniel.architectury.registry.registries.RegistryBuilder;
+import me.shedaniel.architectury.registry.registries.RegistryOption;
+import me.shedaniel.architectury.registry.registries.StandardRegistryOption;
+import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder;
+import net.fabricmc.fabric.api.event.registry.RegistryAttribute;
+import net.minecraft.core.MappedRegistry;
 import net.minecraft.resources.ResourceKey;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.util.LazyLoadedValue;
@@ -64,6 +71,36 @@ public class RegistriesImpl {
         public  Registry get(net.minecraft.core.Registry registry) {
             return new RegistryImpl<>(registry);
         }
+        
+        @Override
+        @NotNull
+        public > RegistryBuilder builder(Class type, ResourceLocation registryId) {
+            return new RegistryBuilderWrapper<>(FabricRegistryBuilder.createSimple(type, registryId));
+        }
+    }
+    
+    public static class RegistryBuilderWrapper> implements RegistryBuilder {
+        @NotNull
+        private FabricRegistryBuilder> builder;
+        
+        public RegistryBuilderWrapper(@NotNull FabricRegistryBuilder> builder) {
+            this.builder = builder;
+        }
+        
+        @Override
+        public @NotNull Registry build() {
+            return RegistryProviderImpl.INSTANCE.get(builder.buildAndRegister());
+        }
+        
+        @Override
+        public @NotNull RegistryBuilder option(@NotNull RegistryOption option) {
+            if (option == StandardRegistryOption.SAVE_TO_DISC) {
+                this.builder.attribute(RegistryAttribute.PERSISTED);
+            } else if (option == StandardRegistryOption.SYNC_TO_CLIENTS) {
+                this.builder.attribute(RegistryAttribute.SYNCED);
+            }
+            return this;
+        }
     }
     
     public static class RegistryImpl implements Registry {
diff --git a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinRegistryEntry.java
similarity index 86%
rename from forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java
rename to forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinRegistryEntry.java
index 06b44783..8d675a1b 100644
--- a/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinAbstractRecipeSerializer.java
+++ b/forge/src/main/java/me/shedaniel/architectury/mixin/forge/MixinRegistryEntry.java
@@ -19,9 +19,9 @@
 
 package me.shedaniel.architectury.mixin.forge;
 
-import me.shedaniel.architectury.core.AbstractRecipeSerializer;
+import me.shedaniel.architectury.core.RegistryEntry;
 import org.spongepowered.asm.mixin.Mixin;
 
-@Mixin(AbstractRecipeSerializer.class)
-public class MixinAbstractRecipeSerializer {
+@Mixin(RegistryEntry.class)
+public class MixinRegistryEntry {
 }
diff --git a/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java
index 25b01e1d..4f9aec57 100644
--- a/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java
+++ b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java
@@ -61,7 +61,7 @@ public class ArchitecturyMixinPlugin implements IMixinConfigPlugin {
     public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
         // Inject our own sugar
         switch (mixinClassName) {
-            case "me.shedaniel.architectury.mixin.forge.MixinAbstractRecipeSerializer":
+            case "me.shedaniel.architectury.mixin.forge.MixinRegistryEntry":
                 targetClass.superName = "net/minecraftforge/registries/ForgeRegistryEntry";
                 for (MethodNode method : targetClass.methods) {
                     if (Objects.equals(method.name, "")) {
@@ -77,9 +77,7 @@ public class ArchitecturyMixinPlugin implements IMixinConfigPlugin {
                     }
                 }
                 String recipeSerializer = targetClass.interfaces.get(0);
-                if (targetClass.signature != null) {
-                    targetClass.signature = targetClass.signature.replace("Ljava/lang/Object;", "Lnet/minecraftforge/registries/ForgeRegistryEntry;>");
-                }
+                targetClass.signature = ";>Lnet/minecraftforge/registries/ForgeRegistryEntry;";
                 break;
         }
     }
diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
index 7ac31e48..cb2f06d1 100644
--- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
+++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
@@ -22,10 +22,14 @@ package me.shedaniel.architectury.registry.forge;
 import com.google.common.base.Objects;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Table;
+import me.shedaniel.architectury.core.RegistryEntry;
 import me.shedaniel.architectury.platform.forge.EventBuses;
 import me.shedaniel.architectury.registry.Registries;
 import me.shedaniel.architectury.registry.Registry;
 import me.shedaniel.architectury.registry.RegistrySupplier;
+import me.shedaniel.architectury.registry.registries.RegistryBuilder;
+import me.shedaniel.architectury.registry.registries.RegistryOption;
+import me.shedaniel.architectury.registry.registries.StandardRegistryOption;
 import net.minecraft.resources.ResourceKey;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.util.LazyLoadedValue;
@@ -76,7 +80,11 @@ public class RegistriesImpl {
         
         @Override
         public  Registry get(ResourceKey> registryKey) {
-            return new ForgeBackedRegistryImpl<>(registry, (IForgeRegistry) RegistryManager.ACTIVE.getRegistry(registryKey.location()));
+            return get(RegistryManager.ACTIVE.getRegistry(registryKey.location()));
+        }
+    
+        public  Registry get(IForgeRegistry registry) {
+            return new ForgeBackedRegistryImpl<>(this.registry, registry);
         }
         
         @Override
@@ -84,6 +92,13 @@ public class RegistriesImpl {
             return new VanillaBackedRegistryImpl<>(registry);
         }
         
+        @Override
+        public > RegistryBuilder builder(Class type, ResourceLocation registryId) {
+            return new RegistryBuilderWrapper<>(this, new net.minecraftforge.registries.RegistryBuilder<>()
+                    .setName(registryId)
+                    .setType((Class) type));
+        }
+        
         public class EventListener {
             @SubscribeEvent
             public void handleEvent(RegistryEvent.Register event) {
@@ -101,6 +116,37 @@ public class RegistriesImpl {
         }
     }
     
+    public static class RegistryBuilderWrapper> implements RegistryBuilder {
+        @NotNull
+        private final RegistryProviderImpl provider;
+        @NotNull
+        private final net.minecraftforge.registries.RegistryBuilder builder;
+        private boolean saveToDisk = false;
+        private boolean syncToClients = false;
+        
+        public RegistryBuilderWrapper(@NotNull RegistryProviderImpl provider, @NotNull net.minecraftforge.registries.RegistryBuilder builder) {
+            this.provider = provider;
+            this.builder = builder;
+        }
+        
+        @Override
+        public @NotNull Registry build() {
+            if (!syncToClients) builder.disableSync();
+            if (!saveToDisk) builder.disableSaving();
+            return provider.get(builder.create());
+        }
+        
+        @Override
+        public @NotNull RegistryBuilder option(@NotNull RegistryOption option) {
+            if (option == StandardRegistryOption.SAVE_TO_DISC) {
+                this.saveToDisk = true;
+            } else if (option == StandardRegistryOption.SYNC_TO_CLIENTS) {
+                this.syncToClients = true;
+            }
+            return this;
+        }
+    }
+    
     public static class VanillaBackedRegistryImpl implements Registry {
         private net.minecraft.core.Registry delegate;
         
@@ -121,22 +167,22 @@ public class RegistriesImpl {
                 public @NotNull ResourceLocation getId() {
                     return id;
                 }
-    
+                
                 @Override
                 public boolean isPresent() {
                     return contains(id);
                 }
-    
+                
                 @Override
                 public T get() {
                     return value.get();
                 }
-    
+                
                 @Override
                 public int hashCode() {
                     return Objects.hashCode(getRegistryId(), getId());
                 }
-    
+                
                 @Override
                 public boolean equals(Object obj) {
                     if (this == obj) return true;
@@ -144,7 +190,7 @@ public class RegistriesImpl {
                     RegistrySupplier other = (RegistrySupplier) obj;
                     return other.getRegistryId().equals(getRegistryId()) && other.getId().equals(getId());
                 }
-    
+                
                 @Override
                 public String toString() {
                     return getRegistryId().toString() + "@" + id.toString();
@@ -223,27 +269,27 @@ public class RegistriesImpl {
                 public @NotNull ResourceLocation getRegistryId() {
                     return delegate.getRegistryName();
                 }
-    
+                
                 @Override
                 public @NotNull ResourceLocation getId() {
                     return id;
                 }
-    
+                
                 @Override
                 public boolean isPresent() {
                     return contains(id);
                 }
-    
+                
                 @Override
                 public T get() {
                     return value.get();
                 }
-    
+                
                 @Override
                 public int hashCode() {
                     return Objects.hashCode(getRegistryId(), getId());
                 }
-    
+                
                 @Override
                 public boolean equals(Object obj) {
                     if (this == obj) return true;
@@ -251,7 +297,7 @@ public class RegistriesImpl {
                     RegistrySupplier other = (RegistrySupplier) obj;
                     return other.getRegistryId().equals(getRegistryId()) && other.getId().equals(getId());
                 }
-    
+                
                 @Override
                 public String toString() {
                     return getRegistryId().toString() + "@" + id.toString();
@@ -268,27 +314,27 @@ public class RegistriesImpl {
                 public @NotNull ResourceLocation getRegistryId() {
                     return delegate.getRegistryName();
                 }
-    
+                
                 @Override
                 public @NotNull ResourceLocation getId() {
                     return registryObject.getId();
                 }
-    
+                
                 @Override
                 public boolean isPresent() {
                     return registryObject.isPresent();
                 }
-    
+                
                 @Override
                 public T get() {
                     return (T) registryObject.get();
                 }
-    
+                
                 @Override
                 public int hashCode() {
                     return Objects.hashCode(getRegistryId(), getId());
                 }
-    
+                
                 @Override
                 public boolean equals(Object obj) {
                     if (this == obj) return true;
@@ -296,7 +342,7 @@ public class RegistriesImpl {
                     RegistrySupplier other = (RegistrySupplier) obj;
                     return other.getRegistryId().equals(getRegistryId()) && other.getId().equals(getId());
                 }
-    
+                
                 @Override
                 public String toString() {
                     return getRegistryId().toString() + "@" + id.toString();
diff --git a/forge/src/main/resources/architectury.mixins.json b/forge/src/main/resources/architectury.mixins.json
index 8408592c..af3e3799 100644
--- a/forge/src/main/resources/architectury.mixins.json
+++ b/forge/src/main/resources/architectury.mixins.json
@@ -7,7 +7,7 @@
   "client": [
   ],
   "mixins": [
-    "BiomeGenerationSettingsBuilderAccessor", "MixinAbstractRecipeSerializer", "MixinBlockEntity", "MixinBlockEntityExtension",
+    "BiomeGenerationSettingsBuilderAccessor", "MixinRegistryEntry", "MixinBlockEntity", "MixinBlockEntityExtension",
     "MobSpawnSettingsBuilderAccessor"
   ],
   "injectors": {

From 5fbb420dd793bd64d2abcae04d2d425f9f29ee1c Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 25 Jan 2021 04:10:11 +0100
Subject: [PATCH 16/45] Only overwrite screen on SUCCESS, fix whitespace
 changes

---
 .../architectury/event/events/GuiEvent.java   | 26 ++---
 .../mixin/fabric/client/MixinMinecraft.java   | 39 ++++----
 .../event/forge/EventHandlerImplClient.java   | 94 ++++++++++---------
 3 files changed, 82 insertions(+), 77 deletions(-)

diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
index d29374f9..d7305e29 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
@@ -50,44 +50,44 @@ public interface GuiEvent {
     Event INIT_POST = EventFactory.createLoop(ScreenInitPost.class);
     Event RENDER_PRE = EventFactory.createInteractionResult(ScreenRenderPre.class);
     Event RENDER_POST = EventFactory.createInteractionResult(ScreenRenderPost.class);
-
+    
     /**
      * Invoked during Minecraft#setScreen, equivalent to forge's {@code GuiOpenEvent}.
      */
-    Event SET_SCREEN = EventFactory.createInteractionResultHolder();
-
-    @Environment(EnvType.CLIENT)
-    interface SetScreenEvent {
-        InteractionResultHolder modifyScreen(Screen screen);
-    }
-
+    Event SET_SCREEN = EventFactory.createInteractionResultHolder();
+    
     @Environment(EnvType.CLIENT)
     interface RenderHud {
         void renderHud(PoseStack matrices, float tickDelta);
     }
-
+    
     @Environment(EnvType.CLIENT)
     interface DebugText {
         void gatherText(List strings);
     }
-
+    
     @Environment(EnvType.CLIENT)
     interface ScreenInitPre {
         InteractionResult init(Screen screen, List widgets, List children);
     }
-
+    
     @Environment(EnvType.CLIENT)
     interface ScreenInitPost {
         void init(Screen screen, List widgets, List children);
     }
-
+    
     @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);
     }
+    
+    @Environment(EnvType.CLIENT)
+    interface SetScreen {
+        InteractionResultHolder modifyScreen(Screen screen);
+    }
 }
diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
index f908fea4..20627091 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
@@ -57,13 +57,13 @@ public abstract class MixinMinecraft {
     private @Unique String hostname;
     private @Unique int port;
     // @formatter:on
-
+    
     @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V",
             at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/chat/NarratorChatListener;clear()V"))
     private void handleLogin(Screen screen, CallbackInfo ci) {
         ClientPlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(player);
     }
-
+    
     @Inject(method = "startUseItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", ordinal = 1),
             locals = LocalCapture.CAPTURE_FAILHARD)
     private void rightClickAir(CallbackInfo ci, InteractionHand var1[], int var2, int var3, InteractionHand interactionHand, ItemStack itemStack) {
@@ -71,12 +71,12 @@ public abstract class MixinMinecraft {
             InteractionEvent.CLIENT_RIGHT_CLICK_AIR.invoker().click(player, interactionHand);
         }
     }
-
+    
     @Inject(method = "startAttack", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;resetAttackStrengthTicker()V", ordinal = 0))
     private void leftClickAir(CallbackInfo ci) {
         InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(player, InteractionHand.MAIN_HAND);
     }
-
+    
     @ModifyVariable(
             method = "setScreen",
             at = @At(value = "INVOKE",
@@ -88,18 +88,21 @@ public abstract class MixinMinecraft {
     public Screen modifyScreen(Screen screen) {
         Screen old = screen;
         InteractionResultHolder event = GuiEvent.SET_SCREEN.invoker().modifyScreen(screen);
-        if (event.getResult() == InteractionResult.FAIL) {
-            setScreenCancelled = true;
-            return old;
+        switch (event.getResult()) {
+            case FAIL:
+                setScreenCancelled = true;
+                return old;
+            case SUCCESS:
+                screen = event.getObject();
+                if (old != null && screen != old) {
+                    old.removed();
+                }
+            default:
+                setScreenCancelled = false;
+                return screen;
         }
-        setScreenCancelled = false;
-        screen = event.getObject();
-        if (old != null && screen != old) {
-            old.removed();
-        }
-        return screen;
     }
-
+    
     @Inject(
             method = "setScreen",
             at = @At(value = "INVOKE",
@@ -113,7 +116,7 @@ public abstract class MixinMinecraft {
             ci.cancel();
         }
     }
-
+    
     @Redirect(
             method = "",
             at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V"),
@@ -124,7 +127,7 @@ public abstract class MixinMinecraft {
     )
     public void minecraftWhy(Minecraft mc, Screen screen) {
     }
-
+    
     @Inject(
             method = "",
             at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;resizeDisplay()V"),
@@ -134,8 +137,8 @@ public abstract class MixinMinecraft {
         hostname = string2;
         port = j;
     }
-
-    @SuppressWarnings("UnresolvedMixinReference")
+    
+    @SuppressWarnings({"UnresolvedMixinReference", "ConstantConditions"})
     @Inject(
             method = {"method_29338", "lambda$null$1"}, // .lambda$null$1
             at = @At("RETURN")
diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java
index cc70f9f2..ac3eefc1 100644
--- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java
+++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java
@@ -49,7 +49,7 @@ public class EventHandlerImplClient {
     public static void event(ItemTooltipEvent event) {
         TooltipEvent.ITEM.invoker().append(event.getItemStack(), event.getToolTip(), event.getFlags());
     }
-
+    
     @SubscribeEvent
     public static void event(net.minecraftforge.event.TickEvent.ClientTickEvent event) {
         if (event.phase == net.minecraftforge.event.TickEvent.Phase.START)
@@ -57,40 +57,40 @@ public class EventHandlerImplClient {
         else if (event.phase == net.minecraftforge.event.TickEvent.Phase.END)
             ClientTickEvent.CLIENT_POST.invoker().tick(Minecraft.getInstance());
     }
-
+    
     @SubscribeEvent
     public static void event(RenderGameOverlayEvent.Post event) {
         if (event.getType() == RenderGameOverlayEvent.ElementType.ALL)
             GuiEvent.RENDER_HUD.invoker().renderHud(event.getMatrixStack(), event.getPartialTicks());
     }
-
+    
     @SubscribeEvent
     public static void event(ClientPlayerNetworkEvent.LoggedInEvent event) {
         ClientPlayerEvent.CLIENT_PLAYER_JOIN.invoker().join(event.getPlayer());
     }
-
+    
     @SubscribeEvent
     public static void event(ClientPlayerNetworkEvent.LoggedOutEvent event) {
         ClientPlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(event.getPlayer());
     }
-
+    
     @SubscribeEvent
     public static void event(ClientPlayerNetworkEvent.RespawnEvent event) {
         ClientPlayerEvent.CLIENT_PLAYER_RESPAWN.invoker().respawn(event.getOldPlayer(), event.getNewPlayer());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.InitGuiEvent.Pre event) {
         if (GuiEvent.INIT_PRE.invoker().init(event.getGui(), event.getWidgetList(), (List) event.getGui().children()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.InitGuiEvent.Post event) {
         GuiEvent.INIT_POST.invoker().init(event.getGui(), event.getWidgetList(), (List) event.getGui().children());
     }
-
+    
     @SubscribeEvent
     public static void event(RenderGameOverlayEvent.Text event) {
         if (Minecraft.getInstance().options.renderDebug) {
@@ -98,7 +98,7 @@ public class EventHandlerImplClient {
             GuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(event.getRight());
         }
     }
-
+    
     @SubscribeEvent
     public static void event(net.minecraftforge.client.event.ClientChatEvent event) {
         InteractionResultHolder process = ClientChatEvent.CLIENT.invoker().process(event.getMessage());
@@ -107,7 +107,7 @@ public class EventHandlerImplClient {
         if (process.getResult() == InteractionResult.FAIL)
             event.setCanceled(true);
     }
-
+    
     @SubscribeEvent
     public static void event(ClientChatReceivedEvent event) {
         InteractionResultHolder process = ClientChatEvent.CLIENT_RECEIVED.invoker().process(event.getType(), event.getMessage(), event.getSenderUUID());
@@ -116,7 +116,7 @@ public class EventHandlerImplClient {
         if (process.getResult() == InteractionResult.FAIL)
             event.setCanceled(true);
     }
-
+    
     @SubscribeEvent
     public static void event(WorldEvent.Save event) {
         if (event.getWorld() instanceof ClientLevel) {
@@ -124,61 +124,63 @@ public class EventHandlerImplClient {
             ClientLifecycleEvent.CLIENT_WORLD_LOAD.invoker().act(world);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiOpenEvent event) {
         InteractionResultHolder result = GuiEvent.SET_SCREEN.invoker().modifyScreen(event.getGui());
-        if (result.getResult() == InteractionResult.FAIL) {
-            event.setCanceled(true);
-            return;
+        switch (result.getResult()) {
+            case FAIL:
+                event.setCanceled(true);
+                return;
+            case SUCCESS:
+                event.setGui(result.getObject());
         }
-        event.setGui(result.getObject());
     }
-
+    
     @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()) == InteractionResult.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());
     }
-
+    
     @SubscribeEvent
     public static void event(PlayerInteractEvent.RightClickEmpty event) {
         InteractionEvent.CLIENT_RIGHT_CLICK_AIR.invoker().click(event.getPlayer(), event.getHand());
     }
-
+    
     @SubscribeEvent
     public static void event(PlayerInteractEvent.LeftClickEmpty event) {
         InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(event.getPlayer(), event.getHand());
     }
-
+    
     @SubscribeEvent
     public static void event(RecipesUpdatedEvent event) {
         RecipeUpdateEvent.EVENT.invoker().update(event.getRecipeManager());
     }
-
+    
     private static final ThreadLocal tooltipColorContext = ThreadLocal.withInitial(TooltipEventColorContextImpl::new);
     private static final ThreadLocal tooltipPositionContext = ThreadLocal.withInitial(TooltipEventPositionContextImpl::new);
-
+    
     @SubscribeEvent
     public static void event(RenderTooltipEvent.Pre event) {
         if (TooltipEvent.RENDER_FORGE_PRE.invoker().renderTooltip(event.getMatrixStack(), event.getLines(), event.getX(), event.getY()) == InteractionResult.FAIL) {
             event.setCanceled(true);
             return;
         }
-
+        
         TooltipEventPositionContextImpl positionContext = tooltipPositionContext.get();
         positionContext.reset(event.getX(), event.getY());
         TooltipEvent.RENDER_MODIFY_POSITION.invoker().renderTooltip(event.getMatrixStack(), positionContext);
         event.setX(positionContext.getTooltipX());
         event.setY(positionContext.getTooltipY());
     }
-
+    
     @SubscribeEvent
     public static void event(RenderTooltipEvent.Color event) {
         TooltipEventColorContextImpl colorContext = tooltipColorContext.get();
@@ -191,127 +193,127 @@ public class EventHandlerImplClient {
         event.setBorderEnd(colorContext.getOutlineGradientBottomColor());
         event.setBorderStart(colorContext.getOutlineGradientTopColor());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseScrollEvent.Pre event) {
         if (ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getScrollDelta()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseScrollEvent.Post event) {
         ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getScrollDelta());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseClickedEvent.Pre event) {
         if (ClientScreenInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseClickedEvent.Post event) {
         ClientScreenInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseDragEvent.Pre event) {
         if (ClientScreenInputEvent.MOUSE_DRAGGED_PRE.invoker().mouseDragged(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getMouseButton(), event.getDragX(), event.getDragY()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseDragEvent.Post event) {
         ClientScreenInputEvent.MOUSE_DRAGGED_POST.invoker().mouseDragged(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getMouseButton(), event.getDragX(), event.getDragY());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseReleasedEvent.Pre event) {
         if (ClientScreenInputEvent.MOUSE_RELEASED_PRE.invoker().mouseReleased(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.MouseReleasedEvent.Post event) {
         ClientScreenInputEvent.MOUSE_RELEASED_PRE.invoker().mouseReleased(Minecraft.getInstance(), event.getGui(), event.getMouseX(), event.getMouseY(), event.getButton());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.KeyboardCharTypedEvent.Pre event) {
         if (ClientScreenInputEvent.CHAR_TYPED_PRE.invoker().charTyped(Minecraft.getInstance(), event.getGui(), event.getCodePoint(), event.getModifiers()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.KeyboardCharTypedEvent.Post event) {
         ClientScreenInputEvent.CHAR_TYPED_POST.invoker().charTyped(Minecraft.getInstance(), event.getGui(), event.getCodePoint(), event.getModifiers());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.KeyboardKeyPressedEvent.Pre event) {
         if (ClientScreenInputEvent.KEY_PRESSED_PRE.invoker().keyPressed(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.KeyboardKeyPressedEvent.Post event) {
         ClientScreenInputEvent.KEY_PRESSED_POST.invoker().keyPressed(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers());
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.KeyboardKeyReleasedEvent.Pre event) {
         if (ClientScreenInputEvent.KEY_RELEASED_PRE.invoker().keyReleased(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(GuiScreenEvent.KeyboardKeyReleasedEvent.Post event) {
         ClientScreenInputEvent.KEY_RELEASED_POST.invoker().keyReleased(Minecraft.getInstance(), event.getGui(), event.getKeyCode(), event.getScanCode(), event.getModifiers());
     }
-
+    
     @SubscribeEvent
     public static void event(InputEvent.MouseScrollEvent event) {
         if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getScrollDelta()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(InputEvent.RawMouseEvent event) {
         if (ClientRawInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getMods()) == InteractionResult.FAIL) {
             event.setCanceled(true);
         }
     }
-
+    
     @SubscribeEvent
     public static void event(InputEvent.MouseInputEvent event) {
         ClientRawInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getMods());
     }
-
+    
     @SubscribeEvent
     public static void event(InputEvent.KeyInputEvent event) {
         ClientRawInputEvent.KEY_PRESSED.invoker().keyPressed(Minecraft.getInstance(), event.getKey(), event.getScanCode(), event.getAction(), event.getModifiers());
     }
-
+    
     @OnlyIn(Dist.CLIENT)
     public static class ModBasedEventHandler {
         @SubscribeEvent
         public static void event(net.minecraftforge.client.event.TextureStitchEvent.Pre event) {
             TextureStitchEvent.PRE.invoker().stitch(event.getMap(), event::addSprite);
         }
-
+        
         @SubscribeEvent
         public static void event(net.minecraftforge.client.event.TextureStitchEvent.Post event) {
             TextureStitchEvent.POST.invoker().stitch(event.getMap());
         }
-
+        
         @SubscribeEvent
         public static void event(FMLClientSetupEvent event) {
             ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(event.getMinecraftSupplier().get());

From 5e808e52ee4da9872991684f959fdfda3086af13 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 11:56:49 +0800
Subject: [PATCH 17/45] Add PR snapshots

---
 .github/workflows/snapshot.yml | 22 ++++++++++++++++++++++
 build.gradle                   | 18 +++++++++++++++---
 gradle.properties              |  3 ++-
 3 files changed, 39 insertions(+), 4 deletions(-)
 create mode 100644 .github/workflows/snapshot.yml

diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
new file mode 100644
index 00000000..41294bfe
--- /dev/null
+++ b/.github/workflows/snapshot.yml
@@ -0,0 +1,22 @@
+name: Snapshot Compile & Release
+
+on:
+  [pull_request]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v1
+      - name: Set up JDK 1.8
+        uses: actions/setup-java@v1
+        with:
+          java-version: 1.8
+      - name: Upload to Bintray
+        run: ./gradlew bintrayUpload --stacktrace
+        env:
+          BINTRAY_USER: shedaniel
+          BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
+          PR_NUM: ${{github.event.number}}
diff --git a/build.gradle b/build.gradle
index fddfe4ac..4252a8f0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,9 +25,21 @@ allprojects {
     apply plugin: "java"
     apply plugin: "architectury-plugin"
     apply plugin: "org.cadixdev.licenser"
+    
+    ext {
+        isSnapshot = System.getenv("PR_NUM") != null
+    }
+    
+    def runNumber = (System.getenv("GITHUB_RUN_NUMBER") == null ? (((short) new Random().nextInt()).abs() + 1000).toString() : System.getenv("GITHUB_RUN_NUMBER"))
 
-    archivesBaseName = rootProject.archives_base_name
-    version = rootProject.mod_version + "." + (System.getenv("GITHUB_RUN_NUMBER") == null ? "9999" : System.getenv("GITHUB_RUN_NUMBER"))
+    if (!ext.isSnapshot) {
+        version = rootProject.base_version + "." + runNumber
+        archivesBaseName = rootProject.archives_base_name
+    } else {
+        version = rootProject.base_version + "-PR." + System.getenv("PR_NUM") + "." + runNumber
+        archivesBaseName = rootProject.archives_base_name_snapshot
+    }
+    
     group = rootProject.maven_group
 
     tasks.withType(JavaCompile) {
@@ -55,7 +67,7 @@ allprojects {
 
         ignoreFailures = true
     }
-    
+
     ext {
         releaseChangelog = {
             def dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm")
diff --git a/gradle.properties b/gradle.properties
index 21506c73..68623ff7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,7 +5,8 @@ minecraft_version=1.16.4
 supported_version=1.16.4/5
 
 archives_base_name=architectury
-mod_version=1.4
+archives_base_name_snapshot=architectury-snapshot
+base_version=1.4
 maven_group=me.shedaniel
 
 fabric_loader_version=0.10.8

From 107ffe4aa77738738c39af7f7d6c03d793c0549e Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 25 Jan 2021 05:19:40 +0100
Subject: [PATCH 18/45] Set @Unique to be mixin-wide in MixinMinecraft

---
 .../architectury/mixin/fabric/client/MixinMinecraft.java   | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
index 20627091..241449f4 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
@@ -43,6 +43,7 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
 
 import java.io.File;
 
+@Unique
 @Mixin(Minecraft.class)
 public abstract class MixinMinecraft {
     // @formatter:off
@@ -52,10 +53,10 @@ public abstract class MixinMinecraft {
 
     @Shadow public abstract void setScreen(@Nullable Screen screen);
 
-    private @Unique boolean setScreenCancelled;
+    private boolean setScreenCancelled;
 
-    private @Unique String hostname;
-    private @Unique int port;
+    private String hostname;
+    private int port;
     // @formatter:on
     
     @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V",

From c13620fc9c3851b573fd169bc09da11061fec767 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 12:26:38 +0800
Subject: [PATCH 19/45] Expose raw id from registries

---
 .../events/client/ClientLifecycleEvent.java   |  1 -
 .../architectury/registry/Registry.java       |  5 ++++
 .../registry/fabric/RegistriesImpl.java       | 10 ++++++++
 .../registry/forge/RegistriesImpl.java        | 25 +++++++++++++++++--
 4 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java
index ca650224..fb03e34b 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/client/ClientLifecycleEvent.java
@@ -43,7 +43,6 @@ public interface ClientLifecycleEvent {
     Event CLIENT_WORLD_LOAD = EventFactory.createLoop();
     Event CLIENT_SETUP = EventFactory.createLoop();
     
-    @Deprecated
     @Environment(EnvType.CLIENT)
     interface ClientState extends LifecycleEvent.InstanceState {}
     
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/Registry.java b/common/src/main/java/me/shedaniel/architectury/registry/Registry.java
index b412e383..0c512eeb 100644
--- a/common/src/main/java/me/shedaniel/architectury/registry/Registry.java
+++ b/common/src/main/java/me/shedaniel/architectury/registry/Registry.java
@@ -49,11 +49,16 @@ public interface Registry extends Iterable {
     @Nullable
     ResourceLocation getId(T obj);
     
+    int getRawId(T obj);
+    
     Optional> getKey(T obj);
     
     @Nullable
     T get(ResourceLocation id);
     
+    @Nullable
+    T byRawId(int rawId);
+    
     boolean contains(ResourceLocation id);
     
     boolean containsValue(T obj);
diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
index 2287adb8..edbf6590 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
@@ -165,6 +165,11 @@ public class RegistriesImpl {
             return delegate.getKey(obj);
         }
         
+        @Override
+        public int getRawId(T obj) {
+            return delegate.getId(obj);
+        }
+        
         @Override
         public Optional> getKey(T obj) {
             return delegate.getResourceKey(obj);
@@ -175,6 +180,11 @@ public class RegistriesImpl {
             return delegate.get(id);
         }
         
+        @Override
+        public T byRawId(int rawId) {
+            return delegate.byId(rawId);
+        }
+        
         @Override
         public boolean contains(ResourceLocation id) {
             return delegate.containsKey(id);
diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
index cb2f06d1..b57db984 100644
--- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
+++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
@@ -37,6 +37,7 @@ import net.minecraftforge.event.RegistryEvent;
 import net.minecraftforge.eventbus.api.IEventBus;
 import net.minecraftforge.eventbus.api.SubscribeEvent;
 import net.minecraftforge.fml.RegistryObject;
+import net.minecraftforge.registries.ForgeRegistry;
 import net.minecraftforge.registries.IForgeRegistry;
 import net.minecraftforge.registries.IForgeRegistryEntry;
 import net.minecraftforge.registries.RegistryManager;
@@ -82,7 +83,7 @@ public class RegistriesImpl {
         public  Registry get(ResourceKey> registryKey) {
             return get(RegistryManager.ACTIVE.getRegistry(registryKey.location()));
         }
-    
+        
         public  Registry get(IForgeRegistry registry) {
             return new ForgeBackedRegistryImpl<>(this.registry, registry);
         }
@@ -162,7 +163,7 @@ public class RegistriesImpl {
                 public @NotNull ResourceLocation getRegistryId() {
                     return delegate.key().location();
                 }
-    
+                
                 @Override
                 public @NotNull ResourceLocation getId() {
                     return id;
@@ -210,6 +211,11 @@ public class RegistriesImpl {
             return delegate.getKey(obj);
         }
         
+        @Override
+        public int getRawId(T obj) {
+            return delegate.getId(obj);
+        }
+        
         @Override
         public Optional> getKey(T t) {
             return delegate.getResourceKey(t);
@@ -221,6 +227,11 @@ public class RegistriesImpl {
             return delegate.get(id);
         }
         
+        @Override
+        public T byRawId(int rawId) {
+            return delegate.byId(rawId);
+        }
+        
         @Override
         public boolean contains(ResourceLocation resourceLocation) {
             return delegate.containsKey(resourceLocation);
@@ -356,6 +367,11 @@ public class RegistriesImpl {
             return delegate.getKey(obj);
         }
         
+        @Override
+        public int getRawId(T obj) {
+            return ((ForgeRegistry) delegate).getID(obj);
+        }
+        
         @Override
         public Optional> getKey(T t) {
             return Optional.ofNullable(getId(t)).map(id -> ResourceKey.create(key(), id));
@@ -367,6 +383,11 @@ public class RegistriesImpl {
             return delegate.getValue(id);
         }
         
+        @Override
+        public T byRawId(int rawId) {
+            return ((ForgeRegistry) delegate).getValue(rawId);
+        }
+        
         @Override
         public boolean contains(ResourceLocation resourceLocation) {
             return delegate.containsKey(resourceLocation);

From 4acc8341d0bfb41c165aafb6ca83f2a2f8321c09 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 17:32:56 +0800
Subject: [PATCH 20/45] Add ItemStackHooks

---
 .../architectury/hooks/ItemStackHooks.java    | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 common/src/main/java/me/shedaniel/architectury/hooks/ItemStackHooks.java

diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/ItemStackHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/ItemStackHooks.java
new file mode 100644
index 00000000..c6755140
--- /dev/null
+++ b/common/src/main/java/me/shedaniel/architectury/hooks/ItemStackHooks.java
@@ -0,0 +1,56 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.hooks;
+
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.sounds.SoundEvents;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.world.entity.item.ItemEntity;
+import net.minecraft.world.item.ItemStack;
+
+public final class ItemStackHooks {
+    private ItemStackHooks() {}
+    
+    public static ItemStack copyWithCount(ItemStack stack, int count) {
+        ItemStack copy = stack.copy();
+        copy.setCount(count);
+        return copy;
+    }
+    
+    public static void giveItem(ServerPlayer player, ItemStack stack) {
+        boolean bl = player.inventory.add(stack);
+        if (bl && stack.isEmpty()) {
+            stack.setCount(1);
+            ItemEntity entity = player.drop(stack, false);
+            if (entity != null) {
+                entity.makeFakeItem();
+            }
+            
+            player.level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.2F, ((player.getRandom().nextFloat() - player.getRandom().nextFloat()) * 0.7F + 1.0F) * 2.0F);
+            player.inventoryMenu.broadcastChanges();
+        } else {
+            ItemEntity entity = player.drop(stack, false);
+            if (entity != null) {
+                entity.setNoPickUpDelay();
+                entity.setOwner(player.getUUID());
+            }
+        }
+    }
+}

From bb33552a4965f10019c30fb24968bf56c6d309f0 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 17:33:10 +0800
Subject: [PATCH 21/45] Fix #25

---
 .../fabric/client/MixinEffectInstance.java    | 21 ++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java
index 838e8d31..bee02c1e 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java
@@ -1,3 +1,22 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
 package me.shedaniel.architectury.mixin.fabric.client;
 
 import com.mojang.blaze3d.shaders.Program;
@@ -10,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Redirect;
 
 @Unique
-@Mixin(EffectInstance.class)
+@Mixin(value = EffectInstance.class, priority = 1050)
 public class MixinEffectInstance {
     @Redirect(
             method = "",

From 492521dfe3b3893a2fd7022d51841e97e53f0c71 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 17:52:39 +0800
Subject: [PATCH 22/45] Add PlayerEvent#CHANGE_DIMENSION

---
 build.gradle                                               | 6 +++---
 .../shedaniel/architectury/event/events/PlayerEvent.java   | 6 ++++++
 .../architectury/mixin/fabric/MixinServerPlayer.java       | 6 ++++++
 .../architectury/event/forge/EventHandlerImplCommon.java   | 7 +++++++
 .../me/shedaniel/architectury/test/events/DebugEvents.java | 3 +++
 5 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/build.gradle b/build.gradle
index 4252a8f0..3218f0b1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 plugins {
-    id "architectury-plugin" version "2.0.57"
-    id "forgified-fabric-loom" version "0.6.49" apply false
+    id "architectury-plugin" version "2.0.61"
+    id "forgified-fabric-loom" version "0.6.53" apply false
     id "org.cadixdev.licenser" version "0.5.0"
     id "com.jfrog.bintray" version "1.8.4"
     id "com.matthewprenger.cursegradle" version "1.4.0" apply false
@@ -30,7 +30,7 @@ allprojects {
         isSnapshot = System.getenv("PR_NUM") != null
     }
     
-    def runNumber = (System.getenv("GITHUB_RUN_NUMBER") == null ? (((short) new Random().nextInt()).abs() + 1000).toString() : System.getenv("GITHUB_RUN_NUMBER"))
+    def runNumber = (System.getenv("GITHUB_RUN_NUMBER") == null ? "9999" : System.getenv("GITHUB_RUN_NUMBER"))
 
     if (!ext.isSnapshot) {
         version = rootProject.base_version + "." + runNumber
diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java
index e426ca51..451bf92d 100644
--- a/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java
+++ b/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java
@@ -24,6 +24,7 @@ import me.shedaniel.architectury.event.EventFactory;
 import me.shedaniel.architectury.utils.IntValue;
 import net.minecraft.advancements.Advancement;
 import net.minecraft.core.BlockPos;
+import net.minecraft.resources.ResourceKey;
 import net.minecraft.server.level.ServerPlayer;
 import net.minecraft.world.Container;
 import net.minecraft.world.InteractionResult;
@@ -45,6 +46,7 @@ public interface PlayerEvent {
     Event SMELT_ITEM = EventFactory.createLoop();
     Event PICKUP_ITEM_PRE = EventFactory.createInteractionResult();
     Event PICKUP_ITEM_POST = EventFactory.createLoop();
+    Event CHANGE_DIMENSION = EventFactory.createLoop();
     Event DROP_ITEM = EventFactory.createLoop();
     Event OPEN_MENU = EventFactory.createLoop();
     Event CLOSE_MENU = EventFactory.createLoop();
@@ -86,6 +88,10 @@ public interface PlayerEvent {
         void pickup(Player player, ItemEntity entity, ItemStack stack);
     }
     
+    interface ChangeDimension {
+        void change(ServerPlayer player, ResourceKey oldLevel, ResourceKey newLevel);
+    }
+    
     interface DropItem {
         InteractionResult drop(Player player, ItemEntity entity);
     }
diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayer.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayer.java
index c638ca8b..09680175 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayer.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayer.java
@@ -20,6 +20,7 @@
 package me.shedaniel.architectury.mixin.fabric;
 
 import me.shedaniel.architectury.event.events.PlayerEvent;
+import net.minecraft.server.level.ServerLevel;
 import net.minecraft.server.level.ServerPlayer;
 import net.minecraft.world.Container;
 import net.minecraft.world.MenuProvider;
@@ -57,4 +58,9 @@ public class MixinServerPlayer {
     private void doCloseContainer(CallbackInfo ci) {
         PlayerEvent.CLOSE_MENU.invoker().close((ServerPlayer) (Object) this, ((ServerPlayer) (Object) this).containerMenu);
     }
+    
+    @Inject(method = "triggerDimensionChangeTriggers", at = @At("HEAD"))
+    private void changeDimension(ServerLevel serverLevel, CallbackInfo ci) {
+        PlayerEvent.CHANGE_DIMENSION.invoker().change((ServerPlayer) (Object) this, serverLevel.dimension(), ((ServerPlayer) (Object) this).level.dimension());
+    }
 }
diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java
index 95459c66..72309483 100644
--- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java
+++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java
@@ -322,6 +322,13 @@ public class EventHandlerImplCommon {
         LifecycleEvent.SERVER_BEFORE_START.invoker().stateChanged(event.getServer());
     }
     
+    @SubscribeEvent
+    public static void event(PlayerChangedDimensionEvent event) {
+        if (event.getPlayer() instanceof ServerPlayer) {
+            PlayerEvent.CHANGE_DIMENSION.invoker().change((ServerPlayer) event.getPlayer(), event.getFrom(), event.getTo());
+        }
+    }
+    
     public static class ModBasedEventHandler {
         
     }
diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
index 3acf5dc1..2447d448 100644
--- a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
+++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
@@ -165,6 +165,9 @@ public class DebugEvents {
         PlayerEvent.CLOSE_MENU.register((player, menu) -> {
             SINK.accept(player.getScoreboardName() + " closes " + toSimpleName(menu) + logSide(player.level));
         });
+        PlayerEvent.CHANGE_DIMENSION.register((player, oldLevel, newLevel) -> {
+            SINK.accept(player.getScoreboardName() + " switched from " + oldLevel.location() + " to " + newLevel.location() + logSide(player.level));
+        });
     }
     
     public static String toShortString(Vec3i pos) {

From dc702a01e6f4ab99c045599558e0b6707f01dd8b Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 17:53:26 +0800
Subject: [PATCH 23/45] FluidStackHooks in getting the rendering information
 for the fluids (#26)

---
 .../architectury/hooks/FluidStackHooks.java   | 69 ++++++++++++++
 .../hooks/fabric/FluidStackHooksImpl.java     | 90 +++++++++++++++++++
 .../hooks/forge/FluidStackHooksImpl.java      | 79 ++++++++++++++++
 3 files changed, 238 insertions(+)

diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java
index 09a7a819..07328cb2 100644
--- a/common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java
+++ b/common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java
@@ -22,9 +22,18 @@ package me.shedaniel.architectury.hooks;
 import me.shedaniel.architectury.annotations.ExpectPlatform;
 import me.shedaniel.architectury.fluid.FluidStack;
 import me.shedaniel.architectury.utils.Fraction;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.core.BlockPos;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.FriendlyByteBuf;
 import net.minecraft.network.chat.Component;
+import net.minecraft.world.level.BlockAndTintGetter;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraft.world.level.material.FluidState;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 public class FluidStackHooks {
     private FluidStackHooks() {}
@@ -80,4 +89,64 @@ public class FluidStackHooks {
     public static Fraction bucketAmount() {
         throw new AssertionError();
     }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@NotNull FluidStack stack) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@NotNull Fluid fluid) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@NotNull FluidStack stack) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@NotNull Fluid fluid) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    public static int getColor(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    public static int getColor(@NotNull FluidStack stack) {
+        throw new AssertionError();
+    }
+    
+    @ExpectPlatform
+    @Environment(EnvType.CLIENT)
+    public static int getColor(@NotNull Fluid fluid) {
+        throw new AssertionError();
+    }
 }
diff --git a/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/FluidStackHooksImpl.java b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/FluidStackHooksImpl.java
index 860bb2aa..85c02f78 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/FluidStackHooksImpl.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/FluidStackHooksImpl.java
@@ -26,14 +26,22 @@ import me.shedaniel.architectury.utils.Fraction;
 import me.shedaniel.architectury.utils.NbtType;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
+import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
+import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.core.BlockPos;
 import net.minecraft.core.Registry;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.FriendlyByteBuf;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.TranslatableComponent;
 import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.level.BlockAndTintGetter;
 import net.minecraft.world.level.material.Fluid;
+import net.minecraft.world.level.material.FluidState;
 import net.minecraft.world.level.material.Fluids;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Objects;
 
@@ -103,4 +111,86 @@ public class FluidStackHooksImpl {
     public static Fraction bucketAmount() {
         return Fraction.ofWhole(1);
     }
+    
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        if (state.getType() == Fluids.EMPTY) return null;
+        TextureAtlasSprite[] sprites = FluidRenderHandlerRegistry.INSTANCE.get(state.getType()).getFluidSprites(level, pos, state);
+        if (sprites == null) return null;
+        return sprites[0];
+    }
+    
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@NotNull FluidStack stack) {
+        if (stack.getFluid() == Fluids.EMPTY) return null;
+        TextureAtlasSprite[] sprites = FluidRenderHandlerRegistry.INSTANCE.get(stack.getFluid()).getFluidSprites(null, null, stack.getFluid().defaultFluidState());
+        if (sprites == null) return null;
+        return sprites[0];
+    }
+    
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@NotNull Fluid fluid) {
+        if (fluid == Fluids.EMPTY) return null;
+        TextureAtlasSprite[] sprites = FluidRenderHandlerRegistry.INSTANCE.get(fluid).getFluidSprites(null, null, fluid.defaultFluidState());
+        if (sprites == null) return null;
+        return sprites[0];
+    }
+    
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        if (state.getType() == Fluids.EMPTY) return null;
+        FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(state.getType());
+        if (handler == null) return null;
+        TextureAtlasSprite[] sprites = handler.getFluidSprites(level, pos, state);
+        if (sprites == null) return null;
+        return sprites[1];
+    }
+    
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@NotNull FluidStack stack) {
+        if (stack.getFluid() == Fluids.EMPTY) return null;
+        FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(stack.getFluid());
+        if (handler == null) return null;
+        TextureAtlasSprite[] sprites = handler.getFluidSprites(null, null, stack.getFluid().defaultFluidState());
+        if (sprites == null) return null;
+        return sprites[1];
+    }
+    
+    @Environment(EnvType.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@NotNull Fluid fluid) {
+        if (fluid == Fluids.EMPTY) return null;
+        TextureAtlasSprite[] sprites = FluidRenderHandlerRegistry.INSTANCE.get(fluid).getFluidSprites(null, null, fluid.defaultFluidState());
+        if (sprites == null) return null;
+        return sprites[1];
+    }
+    
+    @Environment(EnvType.CLIENT)
+    public static int getColor(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        if (state.getType() == Fluids.EMPTY) return -1;
+        FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(state.getType());
+        if (handler == null) return -1;
+        return handler.getFluidColor(level, pos, state);
+    }
+    
+    @Environment(EnvType.CLIENT)
+    public static int getColor(@NotNull FluidStack stack) {
+        if (stack.getFluid() == Fluids.EMPTY) return -1;
+        FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(stack.getFluid());
+        if (handler == null) return -1;
+        return handler.getFluidColor(null, null, stack.getFluid().defaultFluidState());
+    }
+    
+    @Environment(EnvType.CLIENT)
+    public static int getColor(@NotNull Fluid fluid) {
+        if (fluid == Fluids.EMPTY) return -1;
+        FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluid);
+        if (handler == null) return -1;
+        return handler.getFluidColor(null, null, fluid.defaultFluidState());
+    }
 }
diff --git a/forge/src/main/java/me/shedaniel/architectury/hooks/forge/FluidStackHooksImpl.java b/forge/src/main/java/me/shedaniel/architectury/hooks/forge/FluidStackHooksImpl.java
index fcf7c7a1..7bdef3d3 100644
--- a/forge/src/main/java/me/shedaniel/architectury/hooks/forge/FluidStackHooksImpl.java
+++ b/forge/src/main/java/me/shedaniel/architectury/hooks/forge/FluidStackHooksImpl.java
@@ -21,9 +21,22 @@ package me.shedaniel.architectury.hooks.forge;
 
 import me.shedaniel.architectury.fluid.FluidStack;
 import me.shedaniel.architectury.utils.Fraction;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.TextureAtlas;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.core.BlockPos;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.FriendlyByteBuf;
 import net.minecraft.network.chat.Component;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.level.BlockAndTintGetter;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraft.world.level.material.FluidState;
+import net.minecraft.world.level.material.Fluids;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 public class FluidStackHooksImpl {
     public static Component getName(FluidStack stack) {
@@ -53,4 +66,70 @@ public class FluidStackHooksImpl {
     public static Fraction bucketAmount() {
         return Fraction.ofWhole(1000);
     }
+    
+    @OnlyIn(Dist.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        if (state.getType() == Fluids.EMPTY) return null;
+        ResourceLocation texture = state.getType().getAttributes().getStillTexture(level, pos);
+        return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture);
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@NotNull FluidStack stack) {
+        if (stack.getFluid() == Fluids.EMPTY) return null;
+        ResourceLocation texture = stack.getFluid().getAttributes().getStillTexture(FluidStackHooksForge.toForge(stack));
+        return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture);
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getStillTexture(@NotNull Fluid fluid) {
+        if (fluid == Fluids.EMPTY) return null;
+        ResourceLocation texture = fluid.getAttributes().getStillTexture();
+        return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture);
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        if (state.getType() == Fluids.EMPTY) return null;
+        ResourceLocation texture = state.getType().getAttributes().getFlowingTexture(level, pos);
+        return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture);
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@NotNull FluidStack stack) {
+        if (stack.getFluid() == Fluids.EMPTY) return null;
+        ResourceLocation texture = stack.getFluid().getAttributes().getFlowingTexture(FluidStackHooksForge.toForge(stack));
+        return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture);
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    @Nullable
+    public static TextureAtlasSprite getFlowingTexture(@NotNull Fluid fluid) {
+        if (fluid == Fluids.EMPTY) return null;
+        ResourceLocation texture = fluid.getAttributes().getFlowingTexture();
+        return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture);
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    public static int getColor(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, @NotNull FluidState state) {
+        if (state.getType() == Fluids.EMPTY) return -1;
+        return state.getType().getAttributes().getColor(level, pos);
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    public static int getColor(@NotNull FluidStack stack) {
+        if (stack.getFluid() == Fluids.EMPTY) return -1;
+        return stack.getFluid().getAttributes().getColor(FluidStackHooksForge.toForge(stack));
+    }
+    
+    @OnlyIn(Dist.CLIENT)
+    public static int getColor(@NotNull Fluid fluid) {
+        if (fluid == Fluids.EMPTY) return -1;
+        return fluid.getAttributes().getColor();
+    }
 }

From 9a8194b1f4c25d69497202f2a6501d93b678a1dc Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 18:04:17 +0800
Subject: [PATCH 24/45] Update architectury plugin

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index 3218f0b1..9af372e0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
 plugins {
-    id "architectury-plugin" version "2.0.61"
+    id "architectury-plugin" version "2.0.63"
     id "forgified-fabric-loom" version "0.6.53" apply false
     id "org.cadixdev.licenser" version "0.5.0"
     id "com.jfrog.bintray" version "1.8.4"

From 022383444129da33c81505b35d9bee106f93832f Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 18:43:20 +0800
Subject: [PATCH 25/45] Fix crash registering reload listeners

---
 .../architectury/registry/fabric/ReloadListenersImpl.java       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ReloadListenersImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ReloadListenersImpl.java
index b40299dc..c8769b22 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ReloadListenersImpl.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ReloadListenersImpl.java
@@ -39,7 +39,7 @@ public class ReloadListenersImpl {
     public static void registerReloadListener(PackType type, PreparableReloadListener listener) {
         byte[] bytes = new byte[8];
         RANDOM.nextBytes(bytes);
-        ResourceLocation id = new ResourceLocation("architectury:reload_" + StringUtils.leftPad(Math.abs(Longs.fromByteArray(bytes)) + "", 19));
+        ResourceLocation id = new ResourceLocation("architectury:reload_" + StringUtils.leftPad(Math.abs(Longs.fromByteArray(bytes)) + "", 19, '0'));
         ResourceManagerHelper.get(type).registerReloadListener(new IdentifiableResourceReloadListener() {
             @Override
             public ResourceLocation getFabricId() {

From a3e8131edb06fc1da2ba092c6c613533a557cbcd Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 20:02:14 +0800
Subject: [PATCH 26/45] Update architectury-plugin

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index 9af372e0..ce3baa79 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
 plugins {
-    id "architectury-plugin" version "2.0.63"
+    id "architectury-plugin" version "2.0.64"
     id "forgified-fabric-loom" version "0.6.53" apply false
     id "org.cadixdev.licenser" version "0.5.0"
     id "com.jfrog.bintray" version "1.8.4"

From d6c2772d8e810ea006ca3d267113c88641221cf2 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 25 Jan 2021 20:02:14 +0800
Subject: [PATCH 27/45] Update architectury-plugin

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index 9af372e0..ce3baa79 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
 plugins {
-    id "architectury-plugin" version "2.0.63"
+    id "architectury-plugin" version "2.0.64"
     id "forgified-fabric-loom" version "0.6.53" apply false
     id "org.cadixdev.licenser" version "0.5.0"
     id "com.jfrog.bintray" version "1.8.4"

From a3e8bbd8f497253c67452b420401410dd4307134 Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 25 Jan 2021 18:08:57 +0100
Subject: [PATCH 28/45] Update GitHub workflows to support [ci skip] tag

(It also now only respects "relevant" files like gradle configs and source files by default)
---
 .github/workflows/publish.yml  | 6 ++++++
 .github/workflows/snapshot.yml | 9 ++++++++-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index d06f4862..9b45669a 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -2,6 +2,10 @@ name: Java CI
 
 on:
   push:
+    paths:
+      - '**.gradle'
+      - '**.properties'
+      - '**/src/**'
     branches:
       - "1.16"
       - "1.17"
@@ -17,6 +21,8 @@ jobs:
           java-version: 1.8
       - name: Upload to Bintray
         run: ./gradlew bintrayUpload curseforgePublish --stacktrace
+        if: |
+          !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.pull_request.title, '[ci skip]')
         env:
           BINTRAY_USER: shedaniel
           BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index 41294bfe..23deb1db 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -1,7 +1,12 @@
 name: Snapshot Compile & Release
 
 on:
-  [pull_request]
+  pull_request:
+    paths:
+      - '**.gradle'
+      - '**.properties'
+      - '**/src/**'
+    types: [ opened, synchronize, reopened ]
 
 jobs:
   build:
@@ -16,6 +21,8 @@ jobs:
           java-version: 1.8
       - name: Upload to Bintray
         run: ./gradlew bintrayUpload --stacktrace
+        if: |
+          !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.pull_request.title, '[ci skip]')
         env:
           BINTRAY_USER: shedaniel
           BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}

From 9e733da049e270549690bab64b4dde78bd844db6 Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 25 Jan 2021 18:09:56 +0100
Subject: [PATCH 29/45] Attempted fix for #25 (#28)

* Experiment with low priority and require=0 to try to fix #25

* Revert require = 0 because it didn't work

* (Hopefully) solve things with a mixin plugin

Might be overkill, but we could need it again in the future...

* Replace Platform call with FabricLoader
---
 .../fabric/client/MixinEffectInstance.java    |  6 +--
 .../fabric/ArchitecturyMixinPlugin.java       | 52 +++++++++++++++++++
 .../main/resources/architectury.mixins.json   |  5 +-
 3 files changed, 58 insertions(+), 5 deletions(-)
 create mode 100644 fabric/src/main/java/me/shedaniel/architectury/plugin/fabric/ArchitecturyMixinPlugin.java

diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java
index bee02c1e..1294d5bb 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinEffectInstance.java
@@ -29,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Redirect;
 
 @Unique
-@Mixin(value = EffectInstance.class, priority = 1050)
+@Mixin(value = EffectInstance.class, priority = 950)
 public class MixinEffectInstance {
     @Redirect(
             method = "",
@@ -40,7 +40,7 @@ public class MixinEffectInstance {
     private ResourceLocation mojangPls(String _0, ResourceManager rm, String str) {
         return mojangPls(new ResourceLocation(str), ".json");
     }
-
+    
     @Redirect(
             method = "getOrCreate",
             at = @At(value = "NEW",
@@ -50,7 +50,7 @@ public class MixinEffectInstance {
     private static ResourceLocation mojangPls(String _0, ResourceManager rm, Program.Type type, String str) {
         return mojangPls(new ResourceLocation(str), type.getExtension());
     }
-
+    
     private static ResourceLocation mojangPls(ResourceLocation rl, String ext) {
         return new ResourceLocation(rl.getNamespace(), "shaders/program/" + rl.getPath() + ext);
     }
diff --git a/fabric/src/main/java/me/shedaniel/architectury/plugin/fabric/ArchitecturyMixinPlugin.java b/fabric/src/main/java/me/shedaniel/architectury/plugin/fabric/ArchitecturyMixinPlugin.java
new file mode 100644
index 00000000..a97f2c28
--- /dev/null
+++ b/fabric/src/main/java/me/shedaniel/architectury/plugin/fabric/ArchitecturyMixinPlugin.java
@@ -0,0 +1,52 @@
+package me.shedaniel.architectury.plugin.fabric;
+
+import net.fabricmc.loader.api.FabricLoader;
+import org.objectweb.asm.tree.ClassNode;
+import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
+import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
+
+import java.util.List;
+import java.util.Set;
+
+public class ArchitecturyMixinPlugin implements IMixinConfigPlugin {
+    
+    @Override
+    public void onLoad(String mixinPackage) {
+        // noop
+    }
+    
+    @Override
+    public String getRefMapperConfig() {
+        return null;
+    }
+    
+    @Override
+    public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
+        switch (mixinClassName) {
+            case "me.shedaniel.architectury.mixin.fabric.client.MixinEffectInstance":
+                return !FabricLoader.getInstance().isModLoaded("satin");
+            default:
+                return true;
+        }
+    }
+    
+    @Override
+    public void acceptTargets(Set myTargets, Set otherTargets) {
+        // noop
+    }
+    
+    @Override
+    public List getMixins() {
+        return null;
+    }
+    
+    @Override
+    public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
+        // noop
+    }
+    
+    @Override
+    public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
+        // noop
+    }
+}
diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json
index 9d2fa087..bd47a887 100644
--- a/fabric/src/main/resources/architectury.mixins.json
+++ b/fabric/src/main/resources/architectury.mixins.json
@@ -1,12 +1,14 @@
 {
   "required": true,
   "package": "me.shedaniel.architectury.mixin.fabric",
+  "plugin": "me.shedaniel.architectury.plugin.fabric.ArchitecturyMixinPlugin",
   "compatibilityLevel": "JAVA_8",
   "minVersion": "0.7.11",
   "client": [
     "client.MixinClientLevel",
     "client.MixinClientPacketListener",
     "client.MixinDebugScreenOverlay",
+    "client.MixinEffectInstance",
     "client.MixinGameRenderer",
     "client.MixinIntegratedServer",
     "client.MixinKeyboardHandler",
@@ -14,8 +16,7 @@
     "client.MixinMouseHandler",
     "client.MixinMultiPlayerGameMode",
     "client.MixinScreen",
-    "client.MixinTextureAtlas",
-    "client.MixinEffectInstance"
+    "client.MixinTextureAtlas"
   ],
   "mixins": [
     "ExplosionPreInvoker",

From 69d6ab52551261e3f486956842a709d2feeabd12 Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 25 Jan 2021 18:58:07 +0100
Subject: [PATCH 30/45] Remove unused import

---
 .../architectury/mixin/fabric/client/MixinMinecraft.java         | 1 -
 1 file changed, 1 deletion(-)

diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
index 241449f4..b1c5dd28 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java
@@ -29,7 +29,6 @@ import net.minecraft.client.gui.screens.TitleScreen;
 import net.minecraft.client.main.GameConfig;
 import net.minecraft.client.player.LocalPlayer;
 import net.minecraft.world.InteractionHand;
-import net.minecraft.world.InteractionResult;
 import net.minecraft.world.InteractionResultHolder;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.phys.HitResult;

From 0b7e29cd2dcebc0d26523f19fb1bd6fb1d442443 Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 25 Jan 2021 19:10:27 +0100
Subject: [PATCH 31/45] [ci skip] Add SET_SCREEN test to testmod

---
 .../me/shedaniel/architectury/test/events/DebugEvents.java    | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
index 2447d448..3f8e67b4 100644
--- a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
+++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
@@ -208,6 +208,10 @@ public class DebugEvents {
         ClientPlayerEvent.CLIENT_PLAYER_RESPAWN.register((oldPlayer, newPlayer) -> {
             SINK.accept(newPlayer.getScoreboardName() + " respawned (client)");
         });
+        GuiEvent.SET_SCREEN.register((screen -> {
+            SINK.accept("Screen has been changed to " + toSimpleName(screen));
+            return InteractionResultHolder.pass(screen);
+        }));
         GuiEvent.INIT_PRE.register((screen, widgets, children) -> {
             SINK.accept(toSimpleName(screen) + " initializes");
             return InteractionResult.PASS;

From baf36d02fb46c277a2f32426b8beb8b97b96846a Mon Sep 17 00:00:00 2001
From: Max 
Date: Mon, 25 Jan 2021 19:13:09 +0100
Subject: [PATCH 32/45] Fix mojank stripping containsKey from Registries on
 Server

---
 .../registry/fabric/RegistriesImpl.java       |  2 +-
 .../registry/forge/RegistriesImpl.java        | 38 +++++++++----------
 2 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
index d2218150..bd748e25 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
@@ -140,7 +140,7 @@ public class RegistriesImpl {
         
         @Override
         public boolean contains(ResourceLocation id) {
-            return delegate.containsKey(id);
+            return delegate.keySet().contains(id);
         }
         
         @Override
diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
index 7ac31e48..3ba12d6a 100644
--- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
+++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
@@ -116,27 +116,27 @@ public class RegistriesImpl {
                 public @NotNull ResourceLocation getRegistryId() {
                     return delegate.key().location();
                 }
-    
+                
                 @Override
                 public @NotNull ResourceLocation getId() {
                     return id;
                 }
-    
+                
                 @Override
                 public boolean isPresent() {
                     return contains(id);
                 }
-    
+                
                 @Override
                 public T get() {
                     return value.get();
                 }
-    
+                
                 @Override
                 public int hashCode() {
                     return Objects.hashCode(getRegistryId(), getId());
                 }
-    
+                
                 @Override
                 public boolean equals(Object obj) {
                     if (this == obj) return true;
@@ -144,7 +144,7 @@ public class RegistriesImpl {
                     RegistrySupplier other = (RegistrySupplier) obj;
                     return other.getRegistryId().equals(getRegistryId()) && other.getId().equals(getId());
                 }
-    
+                
                 @Override
                 public String toString() {
                     return getRegistryId().toString() + "@" + id.toString();
@@ -177,7 +177,7 @@ public class RegistriesImpl {
         
         @Override
         public boolean contains(ResourceLocation resourceLocation) {
-            return delegate.containsKey(resourceLocation);
+            return delegate.keySet().contains(resourceLocation);
         }
         
         @Override
@@ -223,27 +223,27 @@ public class RegistriesImpl {
                 public @NotNull ResourceLocation getRegistryId() {
                     return delegate.getRegistryName();
                 }
-    
+                
                 @Override
                 public @NotNull ResourceLocation getId() {
                     return id;
                 }
-    
+                
                 @Override
                 public boolean isPresent() {
                     return contains(id);
                 }
-    
+                
                 @Override
                 public T get() {
                     return value.get();
                 }
-    
+                
                 @Override
                 public int hashCode() {
                     return Objects.hashCode(getRegistryId(), getId());
                 }
-    
+                
                 @Override
                 public boolean equals(Object obj) {
                     if (this == obj) return true;
@@ -251,7 +251,7 @@ public class RegistriesImpl {
                     RegistrySupplier other = (RegistrySupplier) obj;
                     return other.getRegistryId().equals(getRegistryId()) && other.getId().equals(getId());
                 }
-    
+                
                 @Override
                 public String toString() {
                     return getRegistryId().toString() + "@" + id.toString();
@@ -268,27 +268,27 @@ public class RegistriesImpl {
                 public @NotNull ResourceLocation getRegistryId() {
                     return delegate.getRegistryName();
                 }
-    
+                
                 @Override
                 public @NotNull ResourceLocation getId() {
                     return registryObject.getId();
                 }
-    
+                
                 @Override
                 public boolean isPresent() {
                     return registryObject.isPresent();
                 }
-    
+                
                 @Override
                 public T get() {
                     return (T) registryObject.get();
                 }
-    
+                
                 @Override
                 public int hashCode() {
                     return Objects.hashCode(getRegistryId(), getId());
                 }
-    
+                
                 @Override
                 public boolean equals(Object obj) {
                     if (this == obj) return true;
@@ -296,7 +296,7 @@ public class RegistriesImpl {
                     RegistrySupplier other = (RegistrySupplier) obj;
                     return other.getRegistryId().equals(getRegistryId()) && other.getId().equals(getId());
                 }
-    
+                
                 @Override
                 public String toString() {
                     return getRegistryId().toString() + "@" + id.toString();

From d6acb9e78a7a52bf1fd231f35c880ccbc48e9765 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Tue, 26 Jan 2021 09:53:51 +0800
Subject: [PATCH 33/45] Bump base version to 1.5

---
 gradle.properties | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gradle.properties b/gradle.properties
index 68623ff7..dd9b88ff 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@ supported_version=1.16.4/5
 
 archives_base_name=architectury
 archives_base_name_snapshot=architectury-snapshot
-base_version=1.4
+base_version=1.5
 maven_group=me.shedaniel
 
 fabric_loader_version=0.10.8

From 51c9e8b0d85d5edd04879442ae0d3281e75d4e83 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Wed, 27 Jan 2021 19:22:03 +0800
Subject: [PATCH 34/45] [ci skip] This is beautiful

---
 .editorconfig       | 261 ++++++++++++++++++++++++++++++++++++++++++++
 build.gradle        |   1 -
 common/build.gradle |   2 +-
 fabric/build.gradle |   4 +-
 4 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 .editorconfig

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..bf7a05c1
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,261 @@
+# This is beautiful.
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+tab_width = 4
+trim_trailing_whitespace = false
+ij_continuation_indent_size = 8
+
+[*.java]
+ij_java_align_consecutive_assignments = false
+ij_java_align_consecutive_variable_declarations = false
+ij_java_align_group_field_declarations = false
+ij_java_align_multiline_annotation_parameters = true
+ij_java_align_multiline_array_initializer_expression = false
+ij_java_align_multiline_assignment = false
+ij_java_align_multiline_binary_operation = true
+ij_java_align_multiline_chained_methods = false
+ij_java_align_multiline_extends_list = false
+ij_java_align_multiline_for = true
+ij_java_align_multiline_method_parentheses = false
+ij_java_align_multiline_parameters = false
+ij_java_align_multiline_parameters_in_calls = false
+ij_java_align_multiline_parenthesized_expression = false
+ij_java_align_multiline_records = true
+ij_java_align_multiline_resources = true
+ij_java_align_multiline_ternary_operation = false
+ij_java_align_multiline_text_blocks = false
+ij_java_align_multiline_throws_list = false
+ij_java_align_subsequent_simple_methods = false
+ij_java_align_throws_keyword = false
+ij_java_annotation_parameter_wrap = normal
+ij_java_array_initializer_new_line_after_left_brace = false
+ij_java_array_initializer_right_brace_on_new_line = false
+ij_java_array_initializer_wrap = off
+ij_java_assert_statement_colon_on_next_line = false
+ij_java_assert_statement_wrap = off
+ij_java_assignment_wrap = off
+ij_java_binary_operation_sign_on_next_line = false
+ij_java_binary_operation_wrap = off
+ij_java_blank_lines_after_anonymous_class_header = 0
+ij_java_blank_lines_after_class_header = 0
+ij_java_blank_lines_after_imports = 1
+ij_java_blank_lines_after_package = 1
+ij_java_blank_lines_around_class = 1
+ij_java_blank_lines_around_field = 0
+ij_java_blank_lines_around_field_in_interface = 0
+ij_java_blank_lines_around_initializer = 1
+ij_java_blank_lines_around_method = 1
+ij_java_blank_lines_around_method_in_interface = 1
+ij_java_blank_lines_before_class_end = 0
+ij_java_blank_lines_before_imports = 1
+ij_java_blank_lines_before_method_body = 0
+ij_java_blank_lines_before_package = 0
+ij_java_block_brace_style = end_of_line
+ij_java_block_comment_at_first_column = true
+ij_java_call_parameters_new_line_after_left_paren = false
+ij_java_call_parameters_right_paren_on_new_line = false
+ij_java_call_parameters_wrap = off
+ij_java_case_statement_on_separate_line = true
+ij_java_catch_on_new_line = false
+ij_java_class_annotation_wrap = split_into_lines
+ij_java_class_brace_style = end_of_line
+ij_java_class_count_to_use_import_on_demand = 5
+ij_java_class_names_in_javadoc = 1
+ij_java_do_not_indent_top_level_class_members = false
+ij_java_do_not_wrap_after_single_annotation = false
+ij_java_do_while_brace_force = never
+ij_java_doc_add_blank_line_after_description = true
+ij_java_doc_add_blank_line_after_param_comments = false
+ij_java_doc_add_blank_line_after_return = false
+ij_java_doc_add_p_tag_on_empty_lines = true
+ij_java_doc_align_exception_comments = true
+ij_java_doc_align_param_comments = true
+ij_java_doc_do_not_wrap_if_one_line = false
+ij_java_doc_enable_formatting = true
+ij_java_doc_enable_leading_asterisks = true
+ij_java_doc_indent_on_continuation = false
+ij_java_doc_keep_empty_lines = true
+ij_java_doc_keep_empty_parameter_tag = true
+ij_java_doc_keep_empty_return_tag = true
+ij_java_doc_keep_empty_throws_tag = true
+ij_java_doc_keep_invalid_tags = true
+ij_java_doc_param_description_on_new_line = false
+ij_java_doc_preserve_line_breaks = false
+ij_java_doc_use_throws_not_exception_tag = true
+ij_java_else_on_new_line = false
+ij_java_entity_dd_suffix = EJB
+ij_java_entity_eb_suffix = Bean
+ij_java_entity_hi_suffix = Home
+ij_java_entity_lhi_prefix = Local
+ij_java_entity_lhi_suffix = Home
+ij_java_entity_li_prefix = Local
+ij_java_entity_pk_class = java.lang.String
+ij_java_entity_vo_suffix = VO
+ij_java_enum_constants_wrap = split_into_lines
+ij_java_extends_keyword_wrap = off
+ij_java_extends_list_wrap = off
+ij_java_field_annotation_wrap = on_every_item
+ij_java_finally_on_new_line = false
+ij_java_for_brace_force = never
+ij_java_for_statement_new_line_after_left_paren = false
+ij_java_for_statement_right_paren_on_new_line = false
+ij_java_for_statement_wrap = off
+ij_java_generate_final_locals = false
+ij_java_generate_final_parameters = false
+ij_java_if_brace_force = never
+ij_java_imports_layout = *,|,javax.**,java.**,|,$*
+ij_java_indent_case_from_switch = true
+ij_java_insert_inner_class_imports = false
+ij_java_insert_override_annotation = true
+ij_java_keep_blank_lines_before_right_brace = 2
+ij_java_keep_blank_lines_between_package_declaration_and_header = 2
+ij_java_keep_blank_lines_in_code = 2
+ij_java_keep_blank_lines_in_declarations = 2
+ij_java_keep_control_statement_in_one_line = true
+ij_java_keep_first_column_comment = true
+ij_java_keep_indents_on_empty_lines = true
+ij_java_keep_line_breaks = true
+ij_java_keep_multiple_expressions_in_one_line = false
+ij_java_keep_simple_blocks_in_one_line = false
+ij_java_keep_simple_classes_in_one_line = true
+ij_java_keep_simple_lambdas_in_one_line = true
+ij_java_keep_simple_methods_in_one_line = true
+ij_java_label_indent_absolute = false
+ij_java_label_indent_size = 0
+ij_java_lambda_brace_style = end_of_line
+ij_java_layout_static_imports_separately = true
+ij_java_line_comment_add_space = false
+ij_java_line_comment_at_first_column = true
+ij_java_message_dd_suffix = EJB
+ij_java_message_eb_suffix = Bean
+ij_java_method_annotation_wrap = split_into_lines
+ij_java_method_brace_style = end_of_line
+ij_java_method_call_chain_wrap = off
+ij_java_method_parameters_new_line_after_left_paren = false
+ij_java_method_parameters_right_paren_on_new_line = false
+ij_java_method_parameters_wrap = off
+ij_java_modifier_list_wrap = false
+ij_java_names_count_to_use_import_on_demand = 3
+ij_java_new_line_after_lparen_in_record_header = false
+ij_java_parameter_annotation_wrap = normal
+ij_java_parentheses_expression_new_line_after_left_paren = false
+ij_java_parentheses_expression_right_paren_on_new_line = false
+ij_java_place_assignment_sign_on_next_line = false
+ij_java_prefer_longer_names = true
+ij_java_prefer_parameters_wrap = false
+ij_java_record_components_wrap = normal
+ij_java_repeat_synchronized = true
+ij_java_replace_instanceof_and_cast = false
+ij_java_replace_null_check = true
+ij_java_replace_sum_lambda_with_method_ref = true
+ij_java_resource_list_new_line_after_left_paren = false
+ij_java_resource_list_right_paren_on_new_line = false
+ij_java_resource_list_wrap = off
+ij_java_rparen_on_new_line_in_record_header = false
+ij_java_session_dd_suffix = EJB
+ij_java_session_eb_suffix = Bean
+ij_java_session_hi_suffix = Home
+ij_java_session_lhi_prefix = Local
+ij_java_session_lhi_suffix = Home
+ij_java_session_li_prefix = Local
+ij_java_session_si_suffix = Service
+ij_java_space_after_closing_angle_bracket_in_type_argument = false
+ij_java_space_after_colon = true
+ij_java_space_after_comma = true
+ij_java_space_after_comma_in_type_arguments = true
+ij_java_space_after_for_semicolon = true
+ij_java_space_after_quest = true
+ij_java_space_after_type_cast = true
+ij_java_space_before_annotation_array_initializer_left_brace = false
+ij_java_space_before_annotation_parameter_list = false
+ij_java_space_before_array_initializer_left_brace = false
+ij_java_space_before_catch_keyword = true
+ij_java_space_before_catch_left_brace = true
+ij_java_space_before_catch_parentheses = true
+ij_java_space_before_class_left_brace = true
+ij_java_space_before_colon = true
+ij_java_space_before_colon_in_foreach = true
+ij_java_space_before_comma = false
+ij_java_space_before_do_left_brace = true
+ij_java_space_before_else_keyword = true
+ij_java_space_before_else_left_brace = true
+ij_java_space_before_finally_keyword = true
+ij_java_space_before_finally_left_brace = true
+ij_java_space_before_for_left_brace = true
+ij_java_space_before_for_parentheses = true
+ij_java_space_before_for_semicolon = false
+ij_java_space_before_if_left_brace = true
+ij_java_space_before_if_parentheses = true
+ij_java_space_before_method_call_parentheses = false
+ij_java_space_before_method_left_brace = true
+ij_java_space_before_method_parentheses = false
+ij_java_space_before_opening_angle_bracket_in_type_parameter = false
+ij_java_space_before_quest = true
+ij_java_space_before_switch_left_brace = true
+ij_java_space_before_switch_parentheses = true
+ij_java_space_before_synchronized_left_brace = true
+ij_java_space_before_synchronized_parentheses = true
+ij_java_space_before_try_left_brace = true
+ij_java_space_before_try_parentheses = true
+ij_java_space_before_type_parameter_list = false
+ij_java_space_before_while_keyword = true
+ij_java_space_before_while_left_brace = true
+ij_java_space_before_while_parentheses = true
+ij_java_space_inside_one_line_enum_braces = false
+ij_java_space_within_empty_array_initializer_braces = false
+ij_java_space_within_empty_method_call_parentheses = false
+ij_java_space_within_empty_method_parentheses = false
+ij_java_spaces_around_additive_operators = true
+ij_java_spaces_around_assignment_operators = true
+ij_java_spaces_around_bitwise_operators = true
+ij_java_spaces_around_equality_operators = true
+ij_java_spaces_around_lambda_arrow = true
+ij_java_spaces_around_logical_operators = true
+ij_java_spaces_around_method_ref_dbl_colon = false
+ij_java_spaces_around_multiplicative_operators = true
+ij_java_spaces_around_relational_operators = true
+ij_java_spaces_around_shift_operators = true
+ij_java_spaces_around_type_bounds_in_type_parameters = true
+ij_java_spaces_around_unary_operator = false
+ij_java_spaces_within_angle_brackets = false
+ij_java_spaces_within_annotation_parentheses = false
+ij_java_spaces_within_array_initializer_braces = false
+ij_java_spaces_within_braces = false
+ij_java_spaces_within_brackets = false
+ij_java_spaces_within_cast_parentheses = false
+ij_java_spaces_within_catch_parentheses = false
+ij_java_spaces_within_for_parentheses = false
+ij_java_spaces_within_if_parentheses = false
+ij_java_spaces_within_method_call_parentheses = false
+ij_java_spaces_within_method_parentheses = false
+ij_java_spaces_within_parentheses = false
+ij_java_spaces_within_record_header = false
+ij_java_spaces_within_switch_parentheses = false
+ij_java_spaces_within_synchronized_parentheses = false
+ij_java_spaces_within_try_parentheses = false
+ij_java_spaces_within_while_parentheses = false
+ij_java_special_else_if_treatment = true
+ij_java_subclass_name_suffix = Impl
+ij_java_ternary_operation_signs_on_next_line = false
+ij_java_ternary_operation_wrap = off
+ij_java_test_name_suffix = Test
+ij_java_throws_keyword_wrap = normal
+ij_java_throws_list_wrap = off
+ij_java_use_external_annotations = false
+ij_java_use_fq_class_names = false
+ij_java_use_relative_indents = false
+ij_java_use_single_class_imports = true
+ij_java_variable_annotation_wrap = normal
+ij_java_visibility = public
+ij_java_while_brace_force = never
+ij_java_while_on_new_line = false
+ij_java_wrap_comments = false
+ij_java_wrap_first_method_in_call_chain = false
+ij_java_wrap_long_lines = false
diff --git a/build.gradle b/build.gradle
index ce3baa79..065b3df4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,6 @@ plugins {
     id "org.cadixdev.licenser" version "0.5.0"
     id "com.jfrog.bintray" version "1.8.4"
     id "com.matthewprenger.cursegradle" version "1.4.0" apply false
-    id "maven"
     id "maven-publish"
 }
 
diff --git a/common/build.gradle b/common/build.gradle
index 7e64e18b..c45ed292 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     mappings minecraft.officialMojangMappings()
     // We depend on fabric loader here to use the fabric @Environment annotations
     // Do NOT use other classes from fabric loader
-    modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
+    modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
     implementation "net.jodah:typetools:0.6.2"
 }
 
diff --git a/fabric/build.gradle b/fabric/build.gradle
index 1b3e5097..d1c637cb 100644
--- a/fabric/build.gradle
+++ b/fabric/build.gradle
@@ -29,8 +29,8 @@ architectury {
 dependencies {
     minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}"
     mappings minecraft.officialMojangMappings()
-    modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
-    modCompile "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
+    modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
+    modImplementation "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"

From 6de010f71a65f3850cdf16e4570b68cfe4cfd05a Mon Sep 17 00:00:00 2001
From: Juuxel <6596629+Juuxel@users.noreply.github.com>
Date: Wed, 27 Jan 2021 15:14:42 +0200
Subject: [PATCH 35/45] Add game rule API

---
 build.gradle                                  |  4 +-
 .../registry/GameRuleFactory.java             | 53 +++++++++++++++++++
 .../registry/GameRuleRegistry.java            | 35 ++++++++++++
 .../registry/fabric/GameRuleFactoryImpl.java  | 44 +++++++++++++++
 .../registry/fabric/GameRuleRegistryImpl.java | 29 ++++++++++
 .../registry/forge/GameRuleFactoryImpl.java   | 45 ++++++++++++++++
 .../registry/forge/GameRuleRegistryImpl.java  | 28 ++++++++++
 .../resources/META-INF/accesstransformer.cfg  |  6 ++-
 .../shedaniel/architectury/test/TestMod.java  |  2 +
 .../architectury/test/events/DebugEvents.java |  2 +-
 .../test/gamerule/TestGameRules.java          | 38 +++++++++++++
 11 files changed, 282 insertions(+), 4 deletions(-)
 create mode 100644 common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java
 create mode 100644 common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java
 create mode 100644 fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleFactoryImpl.java
 create mode 100644 fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleRegistryImpl.java
 create mode 100644 forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleFactoryImpl.java
 create mode 100644 forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleRegistryImpl.java
 create mode 100644 testmod-common/src/main/java/me/shedaniel/architectury/test/gamerule/TestGameRules.java

diff --git a/build.gradle b/build.gradle
index ce3baa79..6ab5cccb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 plugins {
-    id "architectury-plugin" version "2.0.64"
-    id "forgified-fabric-loom" version "0.6.53" apply false
+    id "architectury-plugin" version "2.0.65"
+    id "forgified-fabric-loom" version "0.6.54" apply false
     id "org.cadixdev.licenser" version "0.5.0"
     id "com.jfrog.bintray" version "1.8.4"
     id "com.matthewprenger.cursegradle" version "1.4.0" apply false
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java
new file mode 100644
index 00000000..a51fdc85
--- /dev/null
+++ b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java
@@ -0,0 +1,53 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry;
+
+import me.shedaniel.architectury.annotations.ExpectPlatform;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.level.GameRules;
+
+import java.util.function.BiConsumer;
+
+/**
+ * A utility class for creating game rule types.
+ */
+public final class GameRuleFactory {
+    private GameRuleFactory() {}
+
+    @ExpectPlatform
+    public static GameRules.Type createBooleanRule(boolean defaultValue) {
+        throw new AssertionError();
+    }
+
+    @ExpectPlatform
+    public static GameRules.Type createBooleanRule(boolean defaultValue, BiConsumer changedCallback) {
+        throw new AssertionError();
+    }
+
+    @ExpectPlatform
+    public static GameRules.Type createIntRule(int defaultValue) {
+        throw new AssertionError();
+    }
+
+    @ExpectPlatform
+    public static GameRules.Type createIntRule(int defaultValue, BiConsumer changedCallback) {
+        throw new AssertionError();
+    }
+}
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java
new file mode 100644
index 00000000..f85744ea
--- /dev/null
+++ b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry;
+
+import me.shedaniel.architectury.annotations.ExpectPlatform;
+import net.minecraft.world.level.GameRules;
+
+/**
+ * A registry for registering game rules.
+ */
+public final class GameRuleRegistry {
+    private GameRuleRegistry() {}
+
+    @ExpectPlatform
+    public static > GameRules.Key register(String name, GameRules.Category category, GameRules.Type type) {
+        throw new AssertionError();
+    }
+}
diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleFactoryImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleFactoryImpl.java
new file mode 100644
index 00000000..ef125401
--- /dev/null
+++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry.fabric;
+
+import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.level.GameRules;
+
+import java.util.function.BiConsumer;
+
+public class GameRuleFactoryImpl {
+    public static GameRules.Type createBooleanRule(boolean defaultValue) {
+        return GameRuleFactory.createBooleanRule(defaultValue);
+    }
+
+    public static GameRules.Type createBooleanRule(boolean defaultValue, BiConsumer changedCallback) {
+        return GameRuleFactory.createBooleanRule(defaultValue, changedCallback);
+    }
+
+    public static GameRules.Type createIntRule(int defaultValue) {
+        return GameRuleFactory.createIntRule(defaultValue);
+    }
+
+    public static GameRules.Type createIntRule(int defaultValue, BiConsumer changedCallback) {
+        return GameRuleFactory.createIntRule(defaultValue, changedCallback);
+    }
+}
diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleRegistryImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleRegistryImpl.java
new file mode 100644
index 00000000..bed77e86
--- /dev/null
+++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/GameRuleRegistryImpl.java
@@ -0,0 +1,29 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry.fabric;
+
+import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry;
+import net.minecraft.world.level.GameRules;
+
+public class GameRuleRegistryImpl {
+    public static > GameRules.Key register(String name, GameRules.Category category, GameRules.Type type) {
+        return GameRuleRegistry.register(name, category, type);
+    }
+}
diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleFactoryImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleFactoryImpl.java
new file mode 100644
index 00000000..c925bf5f
--- /dev/null
+++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleFactoryImpl.java
@@ -0,0 +1,45 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry.forge;
+
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.level.GameRules;
+
+import java.util.function.BiConsumer;
+
+public class GameRuleFactoryImpl {
+    private GameRuleFactoryImpl() {}
+
+    public static GameRules.Type createBooleanRule(boolean defaultValue) {
+        return GameRules.BooleanValue.create(defaultValue);
+    }
+
+    public static GameRules.Type createBooleanRule(boolean defaultValue, BiConsumer changedCallback) {
+        return GameRules.BooleanValue.create(defaultValue, changedCallback);
+    }
+
+    public static GameRules.Type createIntRule(int defaultValue) {
+        return GameRules.IntegerValue.create(defaultValue);
+    }
+
+    public static GameRules.Type createIntRule(int defaultValue, BiConsumer changedCallback) {
+        return GameRules.IntegerValue.create(defaultValue, changedCallback);
+    }
+}
diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleRegistryImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleRegistryImpl.java
new file mode 100644
index 00000000..5fd5de13
--- /dev/null
+++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/GameRuleRegistryImpl.java
@@ -0,0 +1,28 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.registry.forge;
+
+import net.minecraft.world.level.GameRules;
+
+public class GameRuleRegistryImpl {
+    public static > GameRules.Key register(String name, GameRules.Category category, GameRules.Type type) {
+        return GameRules.register(name, category, type);
+    }
+}
diff --git a/forge/src/main/resources/META-INF/accesstransformer.cfg b/forge/src/main/resources/META-INF/accesstransformer.cfg
index d9108c0c..523978ec 100644
--- a/forge/src/main/resources/META-INF/accesstransformer.cfg
+++ b/forge/src/main/resources/META-INF/accesstransformer.cfg
@@ -32,4 +32,8 @@ public-f net.minecraft.world.biome.BiomeAmbience field_242523_e # skyColor
 public-f net.minecraft.world.biome.BiomeAmbience field_242524_f # foliageColor
 public-f net.minecraft.world.biome.BiomeAmbience field_242525_g # grassColor
 public-f net.minecraft.world.biome.BiomeAmbience field_242526_h # grassColorModifier
-public net.minecraft.world.storage.FolderName (Ljava/lang/String;)V
\ No newline at end of file
+public net.minecraft.world.storage.FolderName (Ljava/lang/String;)V
+public net.minecraft.world.GameRules$BooleanValue func_223567_b(ZLjava/util/function/BiConsumer;)Lnet/minecraft/world/GameRules$RuleType; # create
+public net.minecraft.world.GameRules$BooleanValue func_223568_b(Z)Lnet/minecraft/world/GameRules$RuleType; # create
+public net.minecraft.world.GameRules$IntegerValue func_223564_a(ILjava/util/function/BiConsumer;)Lnet/minecraft/world/GameRules$RuleType; # create
+public net.minecraft.world.GameRules$IntegerValue func_223559_b(I)Lnet/minecraft/world/GameRules$RuleType; # create
diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java
index 732322eb..7a53cd3c 100644
--- a/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java
+++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java
@@ -24,6 +24,7 @@ import me.shedaniel.architectury.test.debug.ConsoleMessageSink;
 import me.shedaniel.architectury.test.events.DebugEvents;
 import me.shedaniel.architectury.test.debug.MessageSink;
 import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink;
+import me.shedaniel.architectury.test.gamerule.TestGameRules;
 import me.shedaniel.architectury.test.registry.TestRegistries;
 import me.shedaniel.architectury.test.registry.client.TestKeybinds;
 import me.shedaniel.architectury.utils.Env;
@@ -36,6 +37,7 @@ public class TestMod {
     public static void initialize() {
         DebugEvents.initialize();
         TestRegistries.initialize();
+        TestGameRules.init();
         if (Platform.getEnvironment() == Env.CLIENT)
             TestKeybinds.initialize();
     }
diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
index 3f8e67b4..31bd02a9 100644
--- a/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
+++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/events/DebugEvents.java
@@ -271,6 +271,6 @@ public class DebugEvents {
     }
     
     private static String toSimpleName(Object o) {
-        return o.getClass().getSimpleName() + "@" + Integer.toHexString(o.hashCode());
+        return o == null ? "null" : o.getClass().getSimpleName() + "@" + Integer.toHexString(o.hashCode());
     }
 }
diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/gamerule/TestGameRules.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/gamerule/TestGameRules.java
new file mode 100644
index 00000000..d20aad5f
--- /dev/null
+++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/gamerule/TestGameRules.java
@@ -0,0 +1,38 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021 shedaniel
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package me.shedaniel.architectury.test.gamerule;
+
+import me.shedaniel.architectury.registry.GameRuleFactory;
+import net.minecraft.world.level.GameRules;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static me.shedaniel.architectury.registry.GameRuleRegistry.register;
+
+public class TestGameRules {
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    public static final GameRules.Key SIMPLE_BOOL = register("simpleBool", GameRules.Category.MISC, GameRuleFactory.createBooleanRule(true));
+    public static final GameRules.Key SIMPLE_INT = register("simpleInt", GameRules.Category.MISC, GameRuleFactory.createIntRule(10));
+    public static final GameRules.Key CALLBACK_BOOL = register("callbackBool", GameRules.Category.MISC, GameRuleFactory.createBooleanRule(true, (server, value) -> LOGGER.info("changed to {}", value.get())));
+    public static final GameRules.Key CALLBACK_INT = register("callbackInt", GameRules.Category.MISC, GameRuleFactory.createIntRule(10, (server, value) -> LOGGER.info("changed to {}", value.get())));
+
+    public static void init() {}
+}

From 3d3b8bb55e7a9f21f6356691c38064a54c407144 Mon Sep 17 00:00:00 2001
From: Juuxel <6596629+Juuxel@users.noreply.github.com>
Date: Wed, 27 Jan 2021 15:17:13 +0200
Subject: [PATCH 36/45] Add javadocs

---
 .../registry/GameRuleFactory.java             | 26 +++++++++++++++++++
 .../registry/GameRuleRegistry.java            |  9 +++++++
 2 files changed, 35 insertions(+)

diff --git a/common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java
index a51fdc85..8efef201 100644
--- a/common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java
+++ b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleFactory.java
@@ -31,21 +31,47 @@ import java.util.function.BiConsumer;
 public final class GameRuleFactory {
     private GameRuleFactory() {}
 
+    /**
+     * Creates a boolean rule type.
+     *
+     * @param defaultValue the rule's default value
+     * @return the created type
+     */
     @ExpectPlatform
     public static GameRules.Type createBooleanRule(boolean defaultValue) {
         throw new AssertionError();
     }
 
+    /**
+     * Creates a boolean rule type.
+     *
+     * @param defaultValue    the rule's default value
+     * @param changedCallback a callback that is called when the rule's value is changed
+     * @return the created type
+     */
     @ExpectPlatform
     public static GameRules.Type createBooleanRule(boolean defaultValue, BiConsumer changedCallback) {
         throw new AssertionError();
     }
 
+    /**
+     * Creates an integer rule type.
+     *
+     * @param defaultValue the rule's default value
+     * @return the created type
+     */
     @ExpectPlatform
     public static GameRules.Type createIntRule(int defaultValue) {
         throw new AssertionError();
     }
 
+    /**
+     * Creates an integer rule type.
+     *
+     * @param defaultValue    the rule's default value
+     * @param changedCallback a callback that is called when the rule's value is changed
+     * @return the created type
+     */
     @ExpectPlatform
     public static GameRules.Type createIntRule(int defaultValue, BiConsumer changedCallback) {
         throw new AssertionError();
diff --git a/common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java
index f85744ea..bae13668 100644
--- a/common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java
+++ b/common/src/main/java/me/shedaniel/architectury/registry/GameRuleRegistry.java
@@ -28,6 +28,15 @@ import net.minecraft.world.level.GameRules;
 public final class GameRuleRegistry {
     private GameRuleRegistry() {}
 
+    /**
+     * Registers a game rule.
+     *
+     * @param name     the rule's name
+     * @param category the rule category
+     * @param type     the type of the rule
+     * @param  the type of the rule value
+     * @return a key for the registered rule
+     */
     @ExpectPlatform
     public static > GameRules.Key register(String name, GameRules.Category category, GameRules.Type type) {
         throw new AssertionError();

From 3b16d58ed061f82cba030c1e6abe6c091e2fddb0 Mon Sep 17 00:00:00 2001
From: Max 
Date: Wed, 27 Jan 2021 22:28:36 +0100
Subject: [PATCH 37/45] Remove call to target interfaces in mixin plugin

Fixes IOOBE with Architectury Registries
---
 .../architectury/plugin/forge/ArchitecturyMixinPlugin.java       | 1 -
 1 file changed, 1 deletion(-)

diff --git a/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java
index 4f9aec57..8ae4cd42 100644
--- a/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java
+++ b/forge/src/main/java/me/shedaniel/architectury/plugin/forge/ArchitecturyMixinPlugin.java
@@ -76,7 +76,6 @@ public class ArchitecturyMixinPlugin implements IMixinConfigPlugin {
                         }
                     }
                 }
-                String recipeSerializer = targetClass.interfaces.get(0);
                 targetClass.signature = ";>Lnet/minecraftforge/registries/ForgeRegistryEntry;";
                 break;
         }

From fd3e8ac3c0a729b0648736c9ddc1d4e52c2f2051 Mon Sep 17 00:00:00 2001
From: Max 
Date: Wed, 27 Jan 2021 22:30:39 +0100
Subject: [PATCH 38/45] Fix AbstractRecipeSerializer's signature

The corresponding registry should be for serialisers, not for recipes.
---
 .../shedaniel/architectury/core/AbstractRecipeSerializer.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java
index d67f9d92..c5ec2a13 100644
--- a/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java
+++ b/common/src/main/java/me/shedaniel/architectury/core/AbstractRecipeSerializer.java
@@ -25,5 +25,5 @@ import net.minecraft.world.item.crafting.RecipeSerializer;
 /**
  * The equivalent of {@link RecipeSerializer} to use in common that has forge registry entries extended.
  */
-public abstract class AbstractRecipeSerializer> extends RegistryEntry implements RecipeSerializer {
+public abstract class AbstractRecipeSerializer> extends RegistryEntry> implements RecipeSerializer {
 }

From 2e77a254f689f6625c5a8206989c6c34f3b6bfb0 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 1 Feb 2021 14:34:18 +0800
Subject: [PATCH 39/45] Make DeferredRegister cast better

---
 .../shedaniel/architectury/registry/DeferredRegister.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/common/src/main/java/me/shedaniel/architectury/registry/DeferredRegister.java b/common/src/main/java/me/shedaniel/architectury/registry/DeferredRegister.java
index 2b6550ec..f0945182 100644
--- a/common/src/main/java/me/shedaniel/architectury/registry/DeferredRegister.java
+++ b/common/src/main/java/me/shedaniel/architectury/registry/DeferredRegister.java
@@ -70,7 +70,7 @@ public class DeferredRegister {
         return create(registries::get, key);
     }
     
-    public RegistrySupplier register(String id, Supplier supplier) {
+    public  RegistrySupplier register(String id, Supplier supplier) {
         if (modId == null) {
             throw new NullPointerException("You must create the deferred register with a mod id to register entries without the namespace!");
         }
@@ -78,14 +78,14 @@ public class DeferredRegister {
         return register(new ResourceLocation(modId, id), supplier);
     }
     
-    public RegistrySupplier register(ResourceLocation id, Supplier supplier) {
-        Entry entry = new Entry<>(id, supplier);
+    public  RegistrySupplier register(ResourceLocation id, Supplier supplier) {
+        Entry entry = new Entry<>(id, (Supplier) supplier);
         this.entries.add(entry);
         if (registered) {
             Registry registry = registriesSupplier.get().get(key);
             entry.value = registry.registerSupplied(entry.id, entry.supplier);
         }
-        return entry;
+        return (RegistrySupplier) entry;
     }
     
     public void register() {

From 0a5f606bf454df4c7e705c176c7a4ce21b7bd5d6 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Thu, 4 Feb 2021 23:17:09 +0800
Subject: [PATCH 40/45] Fix ColorHandlers on Forge being on the wrong event bus

---
 .../platform/forge/EventBuses.java            | 21 +++++++++++++----
 .../registry/forge/ColorHandlersImpl.java     | 23 +++++++++++--------
 2 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java b/forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java
index 1edf645b..16993ab0 100644
--- a/forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java
+++ b/forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java
@@ -21,15 +21,14 @@ package me.shedaniel.architectury.platform.forge;
 
 import net.minecraftforge.eventbus.api.IEventBus;
 
-import javax.annotation.Nullable;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
+import java.util.function.Consumer;
 
 public final class EventBuses {
     private EventBuses() {}
     
     private static final Map EVENT_BUS_MAP = new HashMap<>();
+    private static final Map>> ON_REGISTERED = new HashMap<>();
     
     public static void registerModEventBus(String modId, IEventBus bus) {
         IEventBus previous = EVENT_BUS_MAP.put(modId, bus);
@@ -37,6 +36,20 @@ public final class EventBuses {
             EVENT_BUS_MAP.put(modId, previous);
             throw new IllegalStateException("Can't register event bus for mod '" + modId + "' because it was previously registered!");
         }
+        
+        for (Consumer runnable : ON_REGISTERED.getOrDefault(modId, Collections.emptyList())) {
+            runnable.accept(bus);
+        }
+    }
+    
+    public static void onRegistered(String modId, Consumer busConsumer) {
+        if (EVENT_BUS_MAP.containsKey(modId)) {
+            busConsumer.accept(EVENT_BUS_MAP.get(modId));
+        } else {
+            synchronized (ON_REGISTERED) {
+                ON_REGISTERED.computeIfAbsent(modId, s -> new ArrayList<>()).add(busConsumer);
+            }
+        }
     }
     
     public static Optional getModEventBus(String modId) {
diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java
index 8ed2165c..621c4629 100644
--- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java
+++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java
@@ -20,13 +20,14 @@
 package me.shedaniel.architectury.registry.forge;
 
 import com.google.common.collect.Lists;
+import me.shedaniel.architectury.forge.ArchitecturyForge;
+import me.shedaniel.architectury.platform.forge.EventBuses;
 import net.minecraft.client.Minecraft;
 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;
 import net.minecraftforge.client.event.ColorHandlerEvent;
-import net.minecraftforge.common.MinecraftForge;
 import org.apache.commons.lang3.tuple.Pair;
 
 import java.util.List;
@@ -37,15 +38,17 @@ public class ColorHandlersImpl {
     private static final List[]>> BLOCK_COLORS = Lists.newArrayList();
     
     static {
-        MinecraftForge.EVENT_BUS.addListener(event -> {
-            for (Pair[]> pair : ITEM_COLORS) {
-                event.getItemColors().register(pair.getLeft(), unpackItems(pair.getRight()));
-            }
-        });
-        MinecraftForge.EVENT_BUS.addListener(event -> {
-            for (Pair[]> pair : BLOCK_COLORS) {
-                event.getBlockColors().register(pair.getLeft(), unpackBlocks(pair.getRight()));
-            }
+        EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> {
+            bus.addListener(event -> {
+                for (Pair[]> pair : ITEM_COLORS) {
+                    event.getItemColors().register(pair.getLeft(), unpackItems(pair.getRight()));
+                }
+            });
+            bus.addListener(event -> {
+                for (Pair[]> pair : BLOCK_COLORS) {
+                    event.getBlockColors().register(pair.getLeft(), unpackBlocks(pair.getRight()));
+                }
+            });
         });
     }
     

From 38af2f1fc43e530d8b693eb9064a313eb2c53e4f Mon Sep 17 00:00:00 2001
From: Max 
Date: Fri, 5 Feb 2021 21:33:08 +0100
Subject: [PATCH 41/45] Remove (probably incorrect) client annotation from some
 hooks

---
 .../java/me/shedaniel/architectury/hooks/EntityHooks.java    | 3 ---
 .../java/me/shedaniel/architectury/hooks/ExplosionHooks.java | 3 ---
 .../me/shedaniel/architectury/hooks/ItemEntityHooks.java     | 5 +----
 .../java/me/shedaniel/architectury/hooks/PlayerHooks.java    | 3 ---
 4 files changed, 1 insertion(+), 13 deletions(-)

diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/EntityHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/EntityHooks.java
index 52ed4083..b28c74cd 100644
--- a/common/src/main/java/me/shedaniel/architectury/hooks/EntityHooks.java
+++ b/common/src/main/java/me/shedaniel/architectury/hooks/EntityHooks.java
@@ -20,11 +20,8 @@
 package me.shedaniel.architectury.hooks;
 
 import me.shedaniel.architectury.annotations.ExpectPlatform;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
 import net.minecraft.world.entity.Entity;
 
-@Environment(EnvType.CLIENT)
 public final class EntityHooks {
     private EntityHooks() {}
     
diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/ExplosionHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/ExplosionHooks.java
index fa587244..8b8b4b48 100644
--- a/common/src/main/java/me/shedaniel/architectury/hooks/ExplosionHooks.java
+++ b/common/src/main/java/me/shedaniel/architectury/hooks/ExplosionHooks.java
@@ -20,14 +20,11 @@
 package me.shedaniel.architectury.hooks;
 
 import me.shedaniel.architectury.annotations.ExpectPlatform;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
 import net.minecraft.world.entity.Entity;
 import net.minecraft.world.level.Explosion;
 import net.minecraft.world.phys.Vec3;
 import org.jetbrains.annotations.Nullable;
 
-@Environment(EnvType.CLIENT)
 public final class ExplosionHooks {
     private ExplosionHooks() {}
     
diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/ItemEntityHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/ItemEntityHooks.java
index b764b4fb..a3d52cd0 100644
--- a/common/src/main/java/me/shedaniel/architectury/hooks/ItemEntityHooks.java
+++ b/common/src/main/java/me/shedaniel/architectury/hooks/ItemEntityHooks.java
@@ -21,11 +21,8 @@ package me.shedaniel.architectury.hooks;
 
 import me.shedaniel.architectury.annotations.ExpectPlatform;
 import me.shedaniel.architectury.utils.IntValue;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
 import net.minecraft.world.entity.item.ItemEntity;
 
-@Environment(EnvType.CLIENT)
 public final class ItemEntityHooks {
     private ItemEntityHooks() {}
     
@@ -38,4 +35,4 @@ public final class ItemEntityHooks {
     public static IntValue lifespan(ItemEntity entity) {
         throw new AssertionError();
     }
-}
\ No newline at end of file
+}
diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/PlayerHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/PlayerHooks.java
index f6f8fe4a..f775a6bf 100644
--- a/common/src/main/java/me/shedaniel/architectury/hooks/PlayerHooks.java
+++ b/common/src/main/java/me/shedaniel/architectury/hooks/PlayerHooks.java
@@ -20,11 +20,8 @@
 package me.shedaniel.architectury.hooks;
 
 import me.shedaniel.architectury.annotations.ExpectPlatform;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
 import net.minecraft.world.entity.player.Player;
 
-@Environment(EnvType.CLIENT)
 public final class PlayerHooks {
     private PlayerHooks() {}
     

From e66a57bc809fd6270931370932eea4caaa2815b9 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Sat, 6 Feb 2021 22:56:12 +0800
Subject: [PATCH 42/45] Add some null checks

---
 .../me/shedaniel/architectury/registry/ColorHandlers.java  | 6 +++---
 .../architectury/registry/fabric/ColorHandlersImpl.java    | 7 +++++--
 .../architectury/registry/forge/ColorHandlersImpl.java     | 7 +++++--
 3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java
index 48432b29..4be8cbae 100644
--- a/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java
+++ b/common/src/main/java/me/shedaniel/architectury/registry/ColorHandlers.java
@@ -27,7 +27,7 @@ import net.minecraft.client.color.item.ItemColor;
 import net.minecraft.world.level.ItemLike;
 import net.minecraft.world.level.block.Block;
 
-import java.util.Arrays;
+import java.util.Objects;
 import java.util.function.Supplier;
 
 @Environment(EnvType.CLIENT)
@@ -37,7 +37,7 @@ public final class ColorHandlers {
     public static void registerItemColors(ItemColor color, ItemLike... items) {
         Supplier[] array = new Supplier[items.length];
         for (int i = 0; i < items.length; i++) {
-            ItemLike item = items[i];
+            ItemLike item = Objects.requireNonNull(items[i], "items[i] is null!");
             array[i] = () -> item;
         }
         registerItemColors(color, array);
@@ -46,7 +46,7 @@ public final class ColorHandlers {
     public static void registerBlockColors(BlockColor color, Block... blocks) {
         Supplier[] array = new Supplier[blocks.length];
         for (int i = 0; i < blocks.length; i++) {
-            Block block = blocks[i];
+            Block block = Objects.requireNonNull(blocks[i], "blocks[i] is null!");
             array[i] = () -> block;
         }
         registerBlockColors(color, array);
diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java
index 12860dd0..f6f264b1 100644
--- a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java
+++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/ColorHandlersImpl.java
@@ -25,23 +25,26 @@ import net.minecraft.client.color.item.ItemColor;
 import net.minecraft.world.level.ItemLike;
 import net.minecraft.world.level.block.Block;
 
+import java.util.Objects;
 import java.util.function.Supplier;
 
 public class ColorHandlersImpl {
     @SafeVarargs
     public static void registerItemColors(ItemColor itemColor, Supplier... items) {
+        Objects.requireNonNull(itemColor, "color is null!");
         ColorProviderRegistry.ITEM.register(itemColor, unpackItems(items));
     }
     
     @SafeVarargs
     public static void registerBlockColors(BlockColor blockColor, Supplier... blocks) {
+        Objects.requireNonNull(blockColor, "color is null!");
         ColorProviderRegistry.BLOCK.register(blockColor, unpackBlocks(blocks));
     }
     
     private static ItemLike[] unpackItems(Supplier[] items) {
         ItemLike[] array = new ItemLike[items.length];
         for (int i = 0; i < items.length; i++) {
-            array[i] = items[i].get();
+            array[i] = Objects.requireNonNull(items[i].get());
         }
         return array;
     }
@@ -49,7 +52,7 @@ public class ColorHandlersImpl {
     private static Block[] unpackBlocks(Supplier[] blocks) {
         Block[] array = new Block[blocks.length];
         for (int i = 0; i < blocks.length; i++) {
-            array[i] = blocks[i].get();
+            array[i] = Objects.requireNonNull(blocks[i].get());
         }
         return array;
     }
diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java
index 621c4629..44dfa3d2 100644
--- a/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java
+++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/ColorHandlersImpl.java
@@ -31,6 +31,7 @@ import net.minecraftforge.client.event.ColorHandlerEvent;
 import org.apache.commons.lang3.tuple.Pair;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Supplier;
 
 public class ColorHandlersImpl {
@@ -54,6 +55,7 @@ public class ColorHandlersImpl {
     
     @SafeVarargs
     public static void registerItemColors(ItemColor itemColor, Supplier... items) {
+        Objects.requireNonNull(itemColor, "color is null!");
         if (Minecraft.getInstance().getItemColors() == null) {
             ITEM_COLORS.add(Pair.of(itemColor, items));
         } else {
@@ -63,6 +65,7 @@ public class ColorHandlersImpl {
     
     @SafeVarargs
     public static void registerBlockColors(BlockColor blockColor, Supplier... blocks) {
+        Objects.requireNonNull(blockColor, "color is null!");
         if (Minecraft.getInstance().getBlockColors() == null) {
             BLOCK_COLORS.add(Pair.of(blockColor, blocks));
         } else {
@@ -73,7 +76,7 @@ public class ColorHandlersImpl {
     private static ItemLike[] unpackItems(Supplier[] items) {
         ItemLike[] array = new ItemLike[items.length];
         for (int i = 0; i < items.length; i++) {
-            array[i] = items[i].get();
+            array[i] = Objects.requireNonNull(items[i].get());
         }
         return array;
     }
@@ -81,7 +84,7 @@ public class ColorHandlersImpl {
     private static Block[] unpackBlocks(Supplier[] blocks) {
         Block[] array = new Block[blocks.length];
         for (int i = 0; i < blocks.length; i++) {
-            array[i] = blocks[i].get();
+            array[i] = Objects.requireNonNull(blocks[i].get());
         }
         return array;
     }

From 70d5de3186af26f008f3ba41009e68fa634b14f1 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Mon, 8 Feb 2021 21:49:58 +0800
Subject: [PATCH 43/45] Migrate to my maven + make NetworkChannel side-agnostic

---
 .github/workflows/publish.yml                 |  9 ++---
 .github/workflows/snapshot.yml                |  7 ++--
 build.gradle                                  | 19 +--------
 common/build.gradle                           | 14 ++++++-
 .../networking/NetworkChannel.java            | 39 +++++++++++++------
 fabric/build.gradle                           | 12 ++++++
 forge/build.gradle                            | 14 ++++++-
 7 files changed, 73 insertions(+), 41 deletions(-)

diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 9b45669a..f59b91c4 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -19,11 +19,10 @@ jobs:
         uses: actions/setup-java@v1
         with:
           java-version: 1.8
-      - name: Upload to Bintray
-        run: ./gradlew bintrayUpload curseforgePublish --stacktrace
+      - name: Upload to Maven
+        run: ./gradlew publish curseforgePublish --stacktrace
         if: |
           !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.pull_request.title, '[ci skip]')
         env:
-          BINTRAY_USER: shedaniel
-          BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
-          curse_api_key: ${{ secrets.CURSE_API_KEY }}
\ No newline at end of file
+          MAVEN_PASS: ${{ secrets.MAVEN_PASS }}
+          curse_api_key: ${{ secrets.CURSE_API_KEY }}
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index 23deb1db..e5689d22 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -19,11 +19,10 @@ jobs:
         uses: actions/setup-java@v1
         with:
           java-version: 1.8
-      - name: Upload to Bintray
-        run: ./gradlew bintrayUpload --stacktrace
+      - name: Upload to Maven
+        run: ./gradlew publish --stacktrace
         if: |
           !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.pull_request.title, '[ci skip]')
         env:
-          BINTRAY_USER: shedaniel
-          BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
+          MAVEN_PASS: ${{ secrets.MAVEN_PASS }}
           PR_NUM: ${{github.event.number}}
diff --git a/build.gradle b/build.gradle
index 8f417041..4fcac77a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,6 @@ plugins {
     id "architectury-plugin" version "2.0.65"
     id "forgified-fabric-loom" version "0.6.54" apply false
     id "org.cadixdev.licenser" version "0.5.0"
-    id "com.jfrog.bintray" version "1.8.4"
     id "com.matthewprenger.cursegradle" version "1.4.0" apply false
     id "maven-publish"
 }
@@ -100,20 +99,4 @@ allprojects {
 task licenseFormatAll
 subprojects { p -> licenseFormatAll.dependsOn("${p.path}:licenseFormat") }
 
-bintray {
-    user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER')
-    key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_KEY')
-    publications = ["mavenCommon", "mavenFabric", "mavenForge"]
-    publish = true
-    pkg {
-        repo = "cloth"
-        name = "architectury"
-        userOrg = "shedaniel"
-        licenses = ["Apache-2.0"]
-        version {
-            vcsUrl = "https://github.com/shedaniel/architectury.git"
-        }
-    }
-}
-
-task curseforgePublish
\ No newline at end of file
+task curseforgePublish
diff --git a/common/build.gradle b/common/build.gradle
index c45ed292..6afb50d7 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -42,4 +42,16 @@ publishing {
             artifact javadocsJar
         }
     }
-}
\ No newline at end of file
+
+    repositories {
+        if (System.getenv("MAVEN_PASS") != null) {
+            maven {
+                url = "https://deploy.shedaniel.me/"
+                credentials {
+                    username = "shedaniel"
+                    password = System.getenv("MAVEN_PASS")
+                }
+            }
+        }
+    }
+}
diff --git a/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java b/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java
index 73fdc088..a38d23f1 100644
--- a/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java
+++ b/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java
@@ -58,10 +58,12 @@ public final class NetworkChannel {
         return new NetworkChannel(id);
     }
     
+    @Deprecated
     public  void register(NetworkManager.Side side, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
-        register(Optional.ofNullable(side), type, encoder, decoder, messageConsumer);
+        register(Optional.empty(), type, encoder, decoder, messageConsumer);
     }
     
+    @Deprecated
     public  void register(Optional side, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
         for (int i = 0; true; i++) {
             if (!takenIds.contains(i)) {
@@ -71,27 +73,40 @@ public final class NetworkChannel {
         }
     }
     
+    public  void register(Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
+        for (int i = 0; true; i++) {
+            if (!takenIds.contains(i)) {
+                register(i, type, encoder, decoder, messageConsumer);
+                break;
+            }
+        }
+    }
+    
+    public  void register(int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
+        register(Optional.empty(), id, type, encoder, decoder, messageConsumer);
+    }
+    
+    @Deprecated
     public  void register(NetworkManager.Side side, int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
         register(Optional.ofNullable(side), id, type, encoder, decoder, messageConsumer);
     }
     
+    @Deprecated
     public  void register(Optional side, int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
         takenIds.add(id);
         ResourceLocation messageId = new ResourceLocation(this.id.getNamespace(), this.id.getPath() + "_" + id);
-        if (!side.isPresent() || side.get() == NetworkManager.s2c()) {
-            if (Platform.getEnvironment() == Env.CLIENT) {
-                NetworkManager.registerReceiver(NetworkManager.s2c(), messageId, (buf, context) -> {
-                    messageConsumer.accept(decoder.apply(buf), () -> context);
-                });
-            }
-            encoders.put(NetworkManager.s2c(), type, Pair.of(messageId, encoder));
-        }
-        if (!side.isPresent() || side.get() == NetworkManager.c2s()) {
-            NetworkManager.registerReceiver(NetworkManager.c2s(), messageId, (buf, context) -> {
+        
+        if (Platform.getEnvironment() == Env.CLIENT) {
+            NetworkManager.registerReceiver(NetworkManager.s2c(), messageId, (buf, context) -> {
                 messageConsumer.accept(decoder.apply(buf), () -> context);
             });
-            encoders.put(NetworkManager.c2s(), type, Pair.of(messageId, encoder));
         }
+        encoders.put(NetworkManager.s2c(), type, Pair.of(messageId, encoder));
+        
+        NetworkManager.registerReceiver(NetworkManager.c2s(), messageId, (buf, context) -> {
+            messageConsumer.accept(decoder.apply(buf), () -> context);
+        });
+        encoders.put(NetworkManager.c2s(), type, Pair.of(messageId, encoder));
     }
     
     private  Pair encode(NetworkManager.Side side, T message) {
diff --git a/fabric/build.gradle b/fabric/build.gradle
index d1c637cb..8c1f49a6 100644
--- a/fabric/build.gradle
+++ b/fabric/build.gradle
@@ -74,6 +74,18 @@ publishing {
             }
         }
     }
+
+    repositories {
+        if (System.getenv("MAVEN_PASS") != null) {
+            maven {
+                url = "https://deploy.shedaniel.me/"
+                credentials {
+                    username = "shedaniel"
+                    password = System.getenv("MAVEN_PASS")
+                }
+            }
+        }
+    }
 }
 
 curseforge {
diff --git a/forge/build.gradle b/forge/build.gradle
index cfe8a840..4b219c52 100644
--- a/forge/build.gradle
+++ b/forge/build.gradle
@@ -69,6 +69,18 @@ publishing {
             }
         }
     }
+    
+    repositories {
+        if (System.getenv("MAVEN_PASS") != null) {
+            maven {
+                url = "https://deploy.shedaniel.me/"
+                credentials {
+                    username = "shedaniel"
+                    password = System.getenv("MAVEN_PASS")
+                }
+            }
+        }
+    }
 }
 
 curseforge {
@@ -97,4 +109,4 @@ curseforge {
     }
 }
 
-rootProject.tasks.getByName("curseforgePublish").dependsOn tasks.getByName("curseforge")
\ No newline at end of file
+rootProject.tasks.getByName("curseforgePublish").dependsOn tasks.getByName("curseforge")

From 7ac9b8485ebaa28757d13462913507ccfa985db6 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Tue, 9 Feb 2021 01:01:33 +0800
Subject: [PATCH 44/45] Log unknown message IDs

---
 .../networking/forge/NetworkManagerImpl.java             | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java b/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java
index 4d00c6fd..b04c85db 100644
--- a/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java
+++ b/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java
@@ -41,6 +41,8 @@ import net.minecraftforge.fml.network.NetworkEvent;
 import net.minecraftforge.fml.network.NetworkRegistry;
 import net.minecraftforge.fml.network.event.EventNetworkChannel;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import java.util.List;
 import java.util.Map;
@@ -63,6 +65,7 @@ public class NetworkManagerImpl {
         return (side == NetworkManager.Side.C2S ? NetworkDirection.PLAY_TO_SERVER : NetworkDirection.PLAY_TO_CLIENT).buildPacket(Pair.of(packetBuffer, 0), CHANNEL_ID).getThis();
     }
     
+    private static final Logger LOGGER = LogManager.getLogger();
     private static final ResourceLocation CHANNEL_ID = new ResourceLocation("architectury:network");
     static final ResourceLocation SYNC_IDS = new ResourceLocation("architectury:sync_ids");
     static final EventNetworkChannel CHANNEL = NetworkRegistry.newEventChannel(CHANNEL_ID, () -> "1", version -> true, version -> true);
@@ -94,7 +97,8 @@ public class NetworkManagerImpl {
             if (event.getClass() != clazz) return;
             NetworkEvent.Context context = event.getSource().get();
             if (context.getPacketHandled()) return;
-            FriendlyByteBuf buffer = new FriendlyByteBuf(event.getPayload().copy());
+            FriendlyByteBuf buffer = event.getPayload();
+            if (buffer == null) return;
             ResourceLocation type = buffer.readResourceLocation();
             NetworkReceiver receiver = map.get(type);
             
@@ -119,7 +123,10 @@ public class NetworkManagerImpl {
                         return DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::getClientPlayer);
                     }
                 });
+            } else {
+                LOGGER.error("Unknown message ID: " + type);
             }
+            
             context.setPacketHandled(true);
         };
     }

From 77413eb808f3dac33afe61f2115d4b1a51c68345 Mon Sep 17 00:00:00 2001
From: shedaniel 
Date: Tue, 9 Feb 2021 01:02:27 +0800
Subject: [PATCH 45/45] Fix NetworkChannel client-server desync

---
 .../networking/NetworkChannel.java            | 98 ++++++++++---------
 1 file changed, 53 insertions(+), 45 deletions(-)

diff --git a/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java b/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java
index a38d23f1..75d8c282 100644
--- a/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java
+++ b/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java
@@ -19,11 +19,9 @@
 
 package me.shedaniel.architectury.networking;
 
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.Table;
+import com.google.common.collect.Maps;
+import com.mojang.datafixers.util.Pair;
 import io.netty.buffer.Unpooled;
-import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
-import it.unimi.dsi.fastutil.ints.IntSet;
 import me.shedaniel.architectury.networking.NetworkManager.PacketContext;
 import me.shedaniel.architectury.platform.Platform;
 import me.shedaniel.architectury.utils.Env;
@@ -34,8 +32,9 @@ import net.minecraft.network.FriendlyByteBuf;
 import net.minecraft.network.protocol.Packet;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.server.level.ServerPlayer;
-import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.StringUtils;
 
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.function.BiConsumer;
@@ -47,8 +46,7 @@ import java.util.function.Supplier;
  */
 public final class NetworkChannel {
     private final ResourceLocation id;
-    private final IntSet takenIds = new IntOpenHashSet();
-    private final Table, Pair>> encoders = HashBasedTable.create();
+    private final Map, MessageInfo> encoders = Maps.newHashMap();
     
     private NetworkChannel(ResourceLocation id) {
         this.id = id;
@@ -60,70 +58,66 @@ public final class NetworkChannel {
     
     @Deprecated
     public  void register(NetworkManager.Side side, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
-        register(Optional.empty(), type, encoder, decoder, messageConsumer);
+        register(type, encoder, decoder, messageConsumer);
     }
     
     @Deprecated
     public  void register(Optional side, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
-        for (int i = 0; true; i++) {
-            if (!takenIds.contains(i)) {
-                register(side, i, type, encoder, decoder, messageConsumer);
-                break;
-            }
-        }
+        register(type, encoder, decoder, messageConsumer);
     }
     
     public  void register(Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
-        for (int i = 0; true; i++) {
-            if (!takenIds.contains(i)) {
-                register(i, type, encoder, decoder, messageConsumer);
-                break;
-            }
+        String s = StringUtils.leftPad(String.valueOf(hashCodeString(type.toString())), 10, '0');
+        if (s.length() > 10) s = s.substring(0, 10);
+        MessageInfo info = new MessageInfo<>(new ResourceLocation(id + "_" + s), encoder, decoder, messageConsumer);
+        encoders.put(type, info);
+        NetworkManager.NetworkReceiver receiver = (buf, context) -> {
+            info.messageConsumer.accept(info.decoder.apply(buf), () -> context);
+        };
+        NetworkManager.registerReceiver(NetworkManager.clientToServer(), info.packetId, receiver);
+        if (Platform.getEnvironment() == Env.CLIENT) {
+            NetworkManager.registerReceiver(NetworkManager.serverToClient(), info.packetId, receiver);
         }
     }
     
+    public static long hashCodeString(String str) {
+        long h = 0;
+        int length = str.length();
+        for (int i = 0; i < length; i++) {
+            h = 31 * h + str.charAt(i);
+        }
+        return h;
+    }
+    
+    @Deprecated
     public  void register(int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
-        register(Optional.empty(), id, type, encoder, decoder, messageConsumer);
+        register(type, encoder, decoder, messageConsumer);
     }
     
     @Deprecated
     public  void register(NetworkManager.Side side, int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
-        register(Optional.ofNullable(side), id, type, encoder, decoder, messageConsumer);
+        register(type, encoder, decoder, messageConsumer);
     }
     
     @Deprecated
     public  void register(Optional side, int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
-        takenIds.add(id);
-        ResourceLocation messageId = new ResourceLocation(this.id.getNamespace(), this.id.getPath() + "_" + id);
-        
-        if (Platform.getEnvironment() == Env.CLIENT) {
-            NetworkManager.registerReceiver(NetworkManager.s2c(), messageId, (buf, context) -> {
-                messageConsumer.accept(decoder.apply(buf), () -> context);
-            });
-        }
-        encoders.put(NetworkManager.s2c(), type, Pair.of(messageId, encoder));
-        
-        NetworkManager.registerReceiver(NetworkManager.c2s(), messageId, (buf, context) -> {
-            messageConsumer.accept(decoder.apply(buf), () -> context);
-        });
-        encoders.put(NetworkManager.c2s(), type, Pair.of(messageId, encoder));
+        register(type, encoder, decoder, messageConsumer);
     }
     
-    private  Pair encode(NetworkManager.Side side, T message) {
-        Pair> pair = Objects.requireNonNull(encoders.get(side, message.getClass()));
+    private  Pair, FriendlyByteBuf> encode(T message) {
+        MessageInfo messageInfo = (MessageInfo) Objects.requireNonNull(encoders.get(message.getClass()));
         FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
-        ((BiConsumer) pair.getRight()).accept(message, buf);
-        return Pair.of(pair.getLeft(), buf);
+        messageInfo.encoder.accept(message, buf);
+        return new Pair<>(messageInfo, buf);
     }
     
     public  Packet toPacket(NetworkManager.Side side, T message) {
-        Pair encoded = encode(side, message);
-        return NetworkManager.toPacket(side, encoded.getLeft(), encoded.getRight());
+        Pair, FriendlyByteBuf> encoded = encode(message);
+        return NetworkManager.toPacket(side, encoded.getFirst().packetId, encoded.getSecond());
     }
     
     public  void sendToPlayer(ServerPlayer player, T message) {
-        Pair encoded = encode(NetworkManager.s2c(), message);
-        NetworkManager.sendToPlayer(player, encoded.getLeft(), encoded.getRight());
+        player.connection.send(toPacket(NetworkManager.s2c(), message));
     }
     
     public  void sendToPlayers(Iterable players, T message) {
@@ -140,10 +134,24 @@ public final class NetworkChannel {
     
     @Environment(EnvType.CLIENT)
     public  boolean canServerReceive(Class type) {
-        return NetworkManager.canServerReceive(encoders.get(NetworkManager.c2s(), type).getLeft());
+        return NetworkManager.canServerReceive(encoders.get(type).packetId);
     }
     
     public  boolean canPlayerReceive(ServerPlayer player, Class type) {
-        return NetworkManager.canPlayerReceive(player, encoders.get(NetworkManager.s2c(), type).getLeft());
+        return NetworkManager.canPlayerReceive(player, encoders.get(type).packetId);
+    }
+    
+    private static final class MessageInfo {
+        private final ResourceLocation packetId;
+        private final BiConsumer encoder;
+        private final Function decoder;
+        private final BiConsumer> messageConsumer;
+        
+        public MessageInfo(ResourceLocation packetId, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) {
+            this.packetId = packetId;
+            this.encoder = encoder;
+            this.decoder = decoder;
+            this.messageConsumer = messageConsumer;
+        }
     }
 }