From a54283c9a2acb2da8a99b2ee31699f10cd04e5be Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Sat, 10 Jun 2023 17:36:39 +0800
Subject: [PATCH 1/8] 1.20

---
 build.gradle.kts                              |  8 ++---
 .../AsyncChunk_VersionedChunkStorage.java     | 30 -------------------
 ...xinPlayerInteractBlock_NetworkHandler.java |  4 +--
 .../MixinPlayerUpdateSign_NetworkHandler.java | 13 ++++----
 ...Chunk_ChunkPosDistanceLevelPropagator.java |  4 +--
 ...inAsyncChunk_ServerPlayNetworkHandler.java |  5 ++--
 .../MixinCustomBlockEntity_Structure.java     |  4 +--
 .../MixinThrownCountdown_ThrownEntity.java    |  4 +--
 .../realtime/entity/EntityMixin_RealTime.java | 11 +++----
 .../passive/PassiveEntityMixin_RealTime.java  |  4 +--
 ...layerInteractionManagerMixin_RealTime.java | 20 ++++++++++++-
 .../one/oktw/galaxy/block/event/AngelBlock.kt | 10 +++----
 .../oktw/galaxy/block/event/BlockEvents.kt    |  4 +--
 .../one/oktw/galaxy/block/event/Elevator.kt   |  4 +--
 .../one/oktw/galaxy/command/commands/Join.kt  |  7 +++--
 .../one/oktw/galaxy/command/commands/Spawn.kt |  4 +--
 .../galaxy/command/commands/admin/GetItem.kt  |  2 +-
 .../command/commands/admin/RegisterBlock.kt   |  4 +--
 .../one/oktw/galaxy/item/event/Wrench.kt      |  4 +--
 .../kotlin/one/oktw/galaxy/player/Harvest.kt  |  4 +--
 .../kotlin/one/oktw/galaxy/player/Sign.kt     | 13 ++++----
 .../one/oktw/galaxy/recipe/blocks/Elevator.kt |  6 ++--
 .../galaxy/recipe/blocks/HTCraftingTable.kt   |  6 ++--
 .../galaxy/recipe/easyRecipe/BookAndQuill.kt  |  6 ++--
 .../recipe/easyRecipe/CarrotOnAStick.kt       |  6 ++--
 .../oktw/galaxy/recipe/easyRecipe/Chest.kt    |  6 ++--
 .../galaxy/recipe/easyRecipe/Dispenser.kt     |  6 ++--
 .../recipe/easyRecipe/DispenserWithBow.kt     |  6 ++--
 .../oktw/galaxy/recipe/easyRecipe/Glass.kt    |  6 ++--
 .../galaxy/recipe/easyRecipe/GlassPane.kt     |  6 ++--
 .../recipe/easyRecipe/GlassPaneRestore.kt     |  6 ++--
 .../oktw/galaxy/recipe/easyRecipe/Hopper.kt   |  6 ++--
 .../oktw/galaxy/recipe/easyRecipe/Ladder.kt   |  6 ++--
 .../oktw/galaxy/recipe/easyRecipe/Minecart.kt |  6 ++--
 .../galaxy/recipe/easyRecipe/RedStoneLamp.kt  |  6 ++--
 .../recipe/easyRecipe/RedStoneRepeater.kt     |  6 ++--
 .../galaxy/recipe/easyRecipe/SlabRestore.kt   |  6 ++--
 .../easyRecipe/StainedGlassPaneRestore.kt     |  6 ++--
 .../recipe/easyRecipe/StainedGlassRestore.kt  |  6 ++--
 .../oktw/galaxy/recipe/easyRecipe/Stick.kt    |  6 ++--
 .../galaxy/recipe/easyRecipe/TrappedChest.kt  |  6 ++--
 .../recipe/easyRecipe/WarpedFungusOnAStick.kt |  6 ++--
 .../galaxy/recipe/easyRecipe/WoodenSlab.kt    |  6 ++--
 .../one/oktw/galaxy/recipe/tools/Crowbar.kt   |  6 ++--
 .../one/oktw/galaxy/recipe/tools/Wrench.kt    |  6 ++--
 .../oktw/galaxy/recipe/utils/RecipeUtils.kt   | 10 +++----
 src/main/resources/accessor.mixin.json        |  1 -
 src/main/resources/fabric.mod.json            |  6 ++--
 48 files changed, 157 insertions(+), 167 deletions(-)
 delete mode 100644 src/main/java/one/oktw/galaxy/mixin/accessor/AsyncChunk_VersionedChunkStorage.java

diff --git a/build.gradle.kts b/build.gradle.kts
index a14ee91e5..19445e779 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,7 +9,7 @@ plugins {
 val version = "0.0.1"
 val group = "one.oktw"
 
-val fabricVersion = "0.81.0+1.19.4"
+val fabricVersion = "0.83.0+1.20"
 val galaxyLibVersion = "63aec6ee"
 
 repositories {
@@ -40,9 +40,9 @@ loom {
 
 dependencies {
     // Core
-    minecraft(group = "com.mojang", name = "minecraft", version = "1.19.4")
-    mappings(group = "net.fabricmc", name = "yarn", version = "1.19.4+build.2", classifier = "v2")
-    modImplementation(group = "net.fabricmc", name = "fabric-loader", version = "0.14.19")
+    minecraft(group = "com.mojang", name = "minecraft", version = "1.20")
+    mappings(group = "net.fabricmc", name = "yarn", version = "1.20+build.1", classifier = "v2")
+    modImplementation(group = "net.fabricmc", name = "fabric-loader", version = "0.14.21")
 
     // fabric api
     modImplementation(group = "net.fabricmc.fabric-api", name = "fabric-api", version = fabricVersion) {
diff --git a/src/main/java/one/oktw/galaxy/mixin/accessor/AsyncChunk_VersionedChunkStorage.java b/src/main/java/one/oktw/galaxy/mixin/accessor/AsyncChunk_VersionedChunkStorage.java
deleted file mode 100644
index c0ecd350d..000000000
--- a/src/main/java/one/oktw/galaxy/mixin/accessor/AsyncChunk_VersionedChunkStorage.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * OKTW Galaxy Project
- * Copyright (C) 2018-2020
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-package one.oktw.galaxy.mixin.accessor;
-
-import net.minecraft.world.storage.StorageIoWorker;
-import net.minecraft.world.storage.VersionedChunkStorage;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Accessor;
-
-@Mixin(VersionedChunkStorage.class)
-public interface AsyncChunk_VersionedChunkStorage {
-    @Accessor
-    StorageIoWorker getWorker();
-}
diff --git a/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerInteractBlock_NetworkHandler.java b/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerInteractBlock_NetworkHandler.java
index 4e0655cdb..dfd32a92c 100644
--- a/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerInteractBlock_NetworkHandler.java
+++ b/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerInteractBlock_NetworkHandler.java
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -47,7 +47,7 @@ private void onPlayerInteractBlock(PlayerInteractBlockC2SPacket packet, Callback
             info.cancel();
             if (event.getSwing()) player.swingHand(packet.getHand(), true);
             // Re-sync block & inventory
-            ServerWorld world = player.getWorld();
+            ServerWorld world = player.getServerWorld();
             BlockPos blockPos = packet.getBlockHitResult().getBlockPos();
             player.networkHandler.sendPacket(new BlockUpdateS2CPacket(world, blockPos));
             player.networkHandler.sendPacket(new BlockUpdateS2CPacket(world, blockPos.offset(packet.getBlockHitResult().getSide())));
diff --git a/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java b/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java
index 6a2ead9c0..c6703d085 100644
--- a/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java
+++ b/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -18,10 +18,11 @@
 
 package one.oktw.galaxy.mixin.event;
 
-import net.minecraft.block.BlockState;
+import net.minecraft.block.Block;
 import net.minecraft.block.entity.BlockEntity;
 import net.minecraft.block.entity.SignBlockEntity;
 import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket;
+import net.minecraft.server.filter.FilteredMessage;
 import net.minecraft.server.network.ServerPlayNetworkHandler;
 import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.server.world.ServerWorld;
@@ -43,15 +44,15 @@ public class MixinPlayerUpdateSign_NetworkHandler {
     public ServerPlayerEntity player;
 
     @Inject(method = "onSignUpdate(Lnet/minecraft/network/packet/c2s/play/UpdateSignC2SPacket;Ljava/util/List;)V",
-        at = @At(value = "INVOKE", target = "Lnet/minecraft/block/entity/SignBlockEntity;isEditable()Z"),
+        at = @At(value = "INVOKE", target = "Lnet/minecraft/block/entity/SignBlockEntity;tryChangeText(Lnet/minecraft/entity/player/PlayerEntity;ZLjava/util/List;)V"),
         cancellable = true,
         locals = LocalCapture.CAPTURE_FAILSOFT)
-    private void onSignUpdate(UpdateSignC2SPacket updateSignC2SPacket, List<String> list, CallbackInfo ci, ServerWorld serverWorld, BlockPos blockPos, BlockState blockState, BlockEntity blockEntity, SignBlockEntity signBlockEntity) {
-        if (EventManager.safeEmit(new PlayerUpdateSignEvent(updateSignC2SPacket, player, signBlockEntity)).getCancel()) {
+    private void onSignUpdate(UpdateSignC2SPacket packet, List<FilteredMessage> signText, CallbackInfo ci, ServerWorld serverWorld, BlockPos blockPos, BlockEntity blockEntity, SignBlockEntity signBlockEntity) {
+        if (EventManager.safeEmit(new PlayerUpdateSignEvent(packet, player, signBlockEntity)).getCancel()) {
             ci.cancel();
 
             signBlockEntity.markDirty();
-            serverWorld.updateListeners(blockPos, blockState, blockState, 3);
+            serverWorld.updateListeners(blockPos, signBlockEntity.getCachedState(), signBlockEntity.getCachedState(), Block.NOTIFY_ALL);
         }
     }
 }
diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ChunkPosDistanceLevelPropagator.java b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ChunkPosDistanceLevelPropagator.java
index febbffe44..08f353fa7 100644
--- a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ChunkPosDistanceLevelPropagator.java
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ChunkPosDistanceLevelPropagator.java
@@ -26,7 +26,7 @@
 
 @Mixin(ChunkPosDistanceLevelPropagator.class)
 public abstract class MixinAsyncChunk_ChunkPosDistanceLevelPropagator {
-    @Redirect(method = "propagateLevel", at = @At(value = "NEW", target = "net/minecraft/util/math/ChunkPos"))
+    @Redirect(method = "propagateLevel", at = @At(value = "NEW", target = "(J)Lnet/minecraft/util/math/ChunkPos;"))
     private ChunkPos skipCreateChunkPos(long pos) {
         return ChunkPos.ORIGIN;
     }
@@ -41,7 +41,7 @@ private int getZ(ChunkPos instance, long pos, int level, boolean decrease) {
         return (int) (pos >> 32);
     }
 
-    @Redirect(method = "recalculateLevel", at = @At(value = "NEW", target = "net/minecraft/util/math/ChunkPos"))
+    @Redirect(method = "recalculateLevel", at = @At(value = "NEW", target = "(J)Lnet/minecraft/util/math/ChunkPos;"))
     private ChunkPos skipCreateChunkPos2(long pos) {
         return ChunkPos.ORIGIN;
     }
diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java
index 44f6783ef..015864910 100644
--- a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java
@@ -23,7 +23,6 @@
 import net.minecraft.server.network.ServerPlayNetworkHandler;
 import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.server.world.ServerWorld;
-import net.minecraft.util.math.ChunkPos;
 import net.minecraft.util.math.ChunkSectionPos;
 import net.minecraft.util.math.Vec3d;
 import org.spongepowered.asm.mixin.Mixin;
@@ -53,7 +52,7 @@ private void noBlockingMove(PlayerMoveC2SPacket packet, CallbackInfo ci) {
 
         int x = ChunkSectionPos.getSectionCoord(clampHorizontal(packet.getX(this.player.getX())));
         int z = ChunkSectionPos.getSectionCoord(clampHorizontal(packet.getZ(this.player.getZ())));
-        if (!player.getWorld().getChunkManager().isTickingFutureReady(ChunkPos.toLong(x, z))) {
+        if (!player.getWorld().getChunkManager().isChunkLoaded(x, z)) {
             player.setVelocity(Vec3d.ZERO);
             requestTeleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYaw(), this.player.getPitch());
             ci.cancel();
@@ -62,7 +61,7 @@ private void noBlockingMove(PlayerMoveC2SPacket packet, CallbackInfo ci) {
 
     @Inject(method = "requestTeleport(DDDFFLjava/util/Set;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;updatePositionAndAngles(DDDFF)V", shift = At.Shift.AFTER))
     private void onTeleport(double x, double y, double z, float yaw, float pitch, Set<PositionFlag> set, CallbackInfo ci) {
-        ServerWorld world = player.getWorld();
+        ServerWorld world = player.getServerWorld();
         if (!world.getPlayers().contains(player)) return;
         world.getChunkManager().updatePosition(this.player);
     }
diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinCustomBlockEntity_Structure.java b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinCustomBlockEntity_Structure.java
index ecb527965..dd748688e 100644
--- a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinCustomBlockEntity_Structure.java
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinCustomBlockEntity_Structure.java
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -45,7 +45,7 @@ public class MixinCustomBlockEntity_Structure {
         at = @At(value = "INVOKE", target = "Lnet/minecraft/world/ServerWorldAccess;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z", ordinal = 1), locals = LocalCapture.CAPTURE_FAILSOFT)
     private void hackPlace(ServerWorldAccess world, BlockPos pos, BlockPos pivot, StructurePlacementData placementData, Random random, int flags, CallbackInfoReturnable<Boolean> cir, List<StructureTemplate.StructureBlockInfo> list, BlockBox blockBox, List<BlockPos> list2, List<BlockPos> list3, List<Pair<BlockPos, NbtCompound>> list4, int j, int k, int l, int m, int n, int o, List<StructureTemplate.StructureBlockInfo> list5, Iterator<StructureTemplate.StructureBlockInfo> var19, StructureTemplate.StructureBlockInfo structureBlockInfo, BlockPos blockPos2, FluidState fluidState, BlockState blockState) {
         // Workaround structure barrier bug
-        if (structureBlockInfo.state.getBlock() == Blocks.BARRIER) {
+        if (structureBlockInfo.state().getBlock() == Blocks.BARRIER) {
             world.setBlockState(blockPos2, Blocks.AIR.getDefaultState(), Block.NO_REDRAW | Block.FORCE_STATE);
         }
     }
diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinThrownCountdown_ThrownEntity.java b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinThrownCountdown_ThrownEntity.java
index 3e03b356a..99a721383 100644
--- a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinThrownCountdown_ThrownEntity.java
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinThrownCountdown_ThrownEntity.java
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2021
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -41,7 +41,7 @@ private HitResult addWaterHit(Entity entity, Predicate<Entity> predicate) {
         if (hit.getType() == HitResult.Type.MISS && ((IThrownCountdown_Entity) this).getIntoWater() > 10) {
             Vec3d pos = entity.getPos();
             Vec3d velocity = pos.add(entity.getVelocity());
-            BlockHitResult newHit = entity.world.raycast(new RaycastContext(pos, velocity, RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.ANY, entity));
+            BlockHitResult newHit = entity.getWorld().raycast(new RaycastContext(pos, velocity, RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.ANY, entity));
             if (newHit.getType() != HitResult.Type.MISS) {
                 hit = newHit;
             }
diff --git a/src/main/java/org/spongepowered/common/mixin/realtime/entity/EntityMixin_RealTime.java b/src/main/java/org/spongepowered/common/mixin/realtime/entity/EntityMixin_RealTime.java
index 9e154b848..ee2900def 100644
--- a/src/main/java/org/spongepowered/common/mixin/realtime/entity/EntityMixin_RealTime.java
+++ b/src/main/java/org/spongepowered/common/mixin/realtime/entity/EntityMixin_RealTime.java
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -54,8 +54,6 @@
 
 @Mixin(Entity.class)
 public abstract class EntityMixin_RealTime {
-    @Shadow
-    public World world;
     @Shadow
     public int timeUntilRegen;
     @Shadow
@@ -63,6 +61,9 @@ public abstract class EntityMixin_RealTime {
     @Shadow
     protected int netherPortalTime;
 
+    @Shadow
+    public abstract World getWorld();
+
     @Redirect(method = "baseTick",
         at = @At(
             value = "FIELD",
@@ -82,7 +83,7 @@ public abstract class EntityMixin_RealTime {
         )
     )
     private void realTimeImpl$adjustForRealTimeEntityCooldown(final Entity self, final int modifier) {
-        final int ticks = (int) ((RealTimeTrackingBridge) this.world).realTimeBridge$getRealTimeTicks();
+        final int ticks = (int) ((RealTimeTrackingBridge) this.getWorld()).realTimeBridge$getRealTimeTicks();
         this.ridingCooldown = Math.max(0, this.ridingCooldown - ticks);
     }
 
@@ -98,7 +99,7 @@ public abstract class EntityMixin_RealTime {
         )
     )
     private void realTimeImpl$adjustForRealTimePortalCounter(final Entity self, final int modifier) {
-        final int ticks = (int) ((RealTimeTrackingBridge) this.world).realTimeBridge$getRealTimeTicks();
+        final int ticks = (int) ((RealTimeTrackingBridge) this.getWorld()).realTimeBridge$getRealTimeTicks();
         this.netherPortalTime += ticks;
     }
 }
diff --git a/src/main/java/org/spongepowered/common/mixin/realtime/entity/passive/PassiveEntityMixin_RealTime.java b/src/main/java/org/spongepowered/common/mixin/realtime/entity/passive/PassiveEntityMixin_RealTime.java
index 3f9e3c05d..bf5603e56 100644
--- a/src/main/java/org/spongepowered/common/mixin/realtime/entity/passive/PassiveEntityMixin_RealTime.java
+++ b/src/main/java/org/spongepowered/common/mixin/realtime/entity/passive/PassiveEntityMixin_RealTime.java
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2019
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -58,7 +58,7 @@ public abstract class PassiveEntityMixin_RealTime extends EntityMixin_RealTime {
     @Redirect(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/PassiveEntity;setBreedingAge(I)V"))
     private void realTimeImpl$adjustForRealTimeGrowingUp(final PassiveEntity self, final int age) {
         // Subtract the one the original update method added
-        final int diff = (int) ((RealTimeTrackingBridge) this.world).realTimeBridge$getRealTimeTicks() - 1;
+        final int diff = (int) ((RealTimeTrackingBridge) this.getWorld()).realTimeBridge$getRealTimeTicks() - 1;
         this.setBreedingAge(Math.min(0, age + diff));
     }
 }
diff --git a/src/main/java/org/spongepowered/common/mixin/realtime/server/network/ServerPlayerInteractionManagerMixin_RealTime.java b/src/main/java/org/spongepowered/common/mixin/realtime/server/network/ServerPlayerInteractionManagerMixin_RealTime.java
index a504b5b54..5d5f62b7a 100644
--- a/src/main/java/org/spongepowered/common/mixin/realtime/server/network/ServerPlayerInteractionManagerMixin_RealTime.java
+++ b/src/main/java/org/spongepowered/common/mixin/realtime/server/network/ServerPlayerInteractionManagerMixin_RealTime.java
@@ -1,3 +1,21 @@
+/*
+ * OKTW Galaxy Project
+ * Copyright (C) 2018-2023
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
 /*
  * This file is part of Sponge, licensed under the MIT License (MIT).
  *
@@ -36,7 +54,7 @@
 @Mixin(ServerPlayerInteractionManager.class)
 public abstract class ServerPlayerInteractionManagerMixin_RealTime {
     @Shadow
-    public ServerWorld world;
+    protected ServerWorld world;
 
     @Shadow
     private int tickCounter;
diff --git a/src/main/kotlin/one/oktw/galaxy/block/event/AngelBlock.kt b/src/main/kotlin/one/oktw/galaxy/block/event/AngelBlock.kt
index c554732bb..2b9be9fa5 100644
--- a/src/main/kotlin/one/oktw/galaxy/block/event/AngelBlock.kt
+++ b/src/main/kotlin/one/oktw/galaxy/block/event/AngelBlock.kt
@@ -56,8 +56,8 @@ class AngelBlock {
             playerPosition.y + playerLookVec.y * 2 + 1.5,
             playerPosition.z + playerLookVec.z * 2
         )
-        if (allowReplaceBlocks.contains(player.getWorld().getBlockState(BlockPos.ofFloored(placePosition)).block)) {
-            CustomBlockHelper.place(player.getWorld(), BlockPos.ofFloored(placePosition), CustomBlock.ANGEL_BLOCK)
+        if (allowReplaceBlocks.contains(player.world.getBlockState(BlockPos.ofFloored(placePosition)).block)) {
+            CustomBlockHelper.place(player.serverWorld, BlockPos.ofFloored(placePosition), CustomBlock.ANGEL_BLOCK)
                 .run {
                     if (!player.isCreative) player.setStackInHand(hand, player.getStackInHand(hand).also { it.decrement(1) })
                 }
@@ -85,11 +85,11 @@ class AngelBlock {
         val player = event.player
         val blockPos = event.packet.pos
         if (event.packet.action == PlayerActionC2SPacket.Action.START_DESTROY_BLOCK &&
-            (player.getWorld().getBlockEntity(blockPos) as? CustomBlockEntity)?.getId() == CustomBlock.ANGEL_BLOCK.identifier &&
+            (player.world.getBlockEntity(blockPos) as? CustomBlockEntity)?.getId() == CustomBlock.ANGEL_BLOCK.identifier &&
             !justBroke.contains(player)
         ) {
-            CustomBlockHelper.destroyAndDrop(player.getWorld(), blockPos)
-            player.getWorld().playSound(null, blockPos, SoundEvents.BLOCK_METAL_PLACE, SoundCategory.BLOCKS, 1.0F, 1.0F)
+            CustomBlockHelper.destroyAndDrop(player.serverWorld, blockPos)
+            player.serverWorld.playSound(null, blockPos, SoundEvents.BLOCK_METAL_PLACE, SoundCategory.BLOCKS, 1.0F, 1.0F)
             justBroke.add(player)
         }
     }
diff --git a/src/main/kotlin/one/oktw/galaxy/block/event/BlockEvents.kt b/src/main/kotlin/one/oktw/galaxy/block/event/BlockEvents.kt
index da7d476e9..915ed4498 100644
--- a/src/main/kotlin/one/oktw/galaxy/block/event/BlockEvents.kt
+++ b/src/main/kotlin/one/oktw/galaxy/block/event/BlockEvents.kt
@@ -58,7 +58,7 @@ class BlockEvents {
         if (!player.shouldCancelInteraction() || player.mainHandStack.isEmpty && player.offHandStack.isEmpty) {
             val packet = event.packet
             val hitResult = packet.blockHitResult
-            val blockEntity = player.getWorld().getBlockEntity(hitResult.blockPos) as? CustomBlockClickListener ?: return
+            val blockEntity = player.world.getBlockEntity(hitResult.blockPos) as? CustomBlockClickListener ?: return
             val result = blockEntity.onClick(player, packet.hand, hitResult)
             if (result.isAccepted) {
                 Criteria.ITEM_USED_ON_BLOCK.trigger(player, hitResult.blockPos, player.getStackInHand(packet.hand))
@@ -82,7 +82,7 @@ class BlockEvents {
 
         // Crowbar
         if (player.isSneaking && CustomItemHelper.getItem(item) == Tool.CROWBAR) {
-            val world = player.getWorld()
+            val world = player.serverWorld
             val blockPos = event.context.blockPos
             if (world.getBlockEntity(blockPos) !is ModelCustomBlockEntity) return // Check is custom block
             CustomBlockHelper.destroyAndDrop(world, blockPos)
diff --git a/src/main/kotlin/one/oktw/galaxy/block/event/Elevator.kt b/src/main/kotlin/one/oktw/galaxy/block/event/Elevator.kt
index 75fc120f1..77dd19dc2 100644
--- a/src/main/kotlin/one/oktw/galaxy/block/event/Elevator.kt
+++ b/src/main/kotlin/one/oktw/galaxy/block/event/Elevator.kt
@@ -53,7 +53,7 @@ class Elevator {
     @EventListener(sync = true)
     fun onJump(event: PlayerJumpEvent) {
         val player = event.player
-        val playerWorld = player.getWorld()
+        val playerWorld = player.serverWorld
         val blockPos = BlockPos.ofFloored(player.pos)
 
         if (isElevator(playerWorld, blockPos.down()) && isSafe(playerWorld, blockPos)) {
@@ -70,7 +70,7 @@ class Elevator {
     @EventListener(sync = true)
     fun onSneak(event: PlayerSneakEvent) {
         val player = event.player
-        val playerWorld = player.getWorld()
+        val playerWorld = player.serverWorld
         val blockPos = BlockPos.ofFloored(player.pos)
 
         if (isElevator(playerWorld, blockPos.down()) && isSafe(playerWorld, blockPos)) {
diff --git a/src/main/kotlin/one/oktw/galaxy/command/commands/Join.kt b/src/main/kotlin/one/oktw/galaxy/command/commands/Join.kt
index bad4e110e..ab1506481 100644
--- a/src/main/kotlin/one/oktw/galaxy/command/commands/Join.kt
+++ b/src/main/kotlin/one/oktw/galaxy/command/commands/Join.kt
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -87,14 +87,14 @@ class Join : Command, CoroutineScope by CoroutineScope(Dispatchers.Default + Sup
     private fun execute(source: ServerCommandSource, collection: Collection<GameProfile>): Int {
         val sourcePlayer = source.playerOrThrow
         if (!lock.getOrPut(sourcePlayer) { Mutex() }.tryLock()) {
-            source.sendFeedback(Text.of("請稍後..."), false)
+            source.sendFeedback({ Text.of("請稍後...") }, false)
             return com.mojang.brigadier.Command.SINGLE_SUCCESS
         }
 
         val targetPlayer = collection.first()
 
         ServerPlayNetworking.send(sourcePlayer, PROXY_IDENTIFIER, PacketByteBuf(wrappedBuffer(encode(CreateGalaxy(targetPlayer.id)))))
-        source.sendFeedback(Text.of(if (sourcePlayer.gameProfile == targetPlayer) "正在加入您的星系" else "正在加入 ${targetPlayer.name} 的星系"), false)
+        source.sendFeedback({ Text.of(if (sourcePlayer.gameProfile == targetPlayer) "正在加入您的星系" else "正在加入 ${targetPlayer.name} 的星系") }, false)
 
         launch {
 
@@ -114,6 +114,7 @@ class Join : Command, CoroutineScope by CoroutineScope(Dispatchers.Default + Sup
                         lock[sourcePlayer]?.unlock()
                         lock.remove(sourcePlayer)
                     }
+
                     Failed -> {
                         sourcePlayer.sendMessage(Text.of("星系載入失敗,請聯絡開發團隊!"), false)
                         lock[source.player]?.unlock()
diff --git a/src/main/kotlin/one/oktw/galaxy/command/commands/Spawn.kt b/src/main/kotlin/one/oktw/galaxy/command/commands/Spawn.kt
index cbc0c1803..d194dee3e 100644
--- a/src/main/kotlin/one/oktw/galaxy/command/commands/Spawn.kt
+++ b/src/main/kotlin/one/oktw/galaxy/command/commands/Spawn.kt
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -52,7 +52,7 @@ class Spawn : Command {
         lock += player.uuid
 
         main?.launch {
-            val world = player.getWorld()
+            val world = player.serverWorld
 
             for (i in 0..4) {
                 player.sendMessage(Text.translatable("Respond.commandCountdown", 5 - i).styled { it.withColor(Formatting.GREEN) }, true)
diff --git a/src/main/kotlin/one/oktw/galaxy/command/commands/admin/GetItem.kt b/src/main/kotlin/one/oktw/galaxy/command/commands/admin/GetItem.kt
index 324957ee5..c6b571bb9 100644
--- a/src/main/kotlin/one/oktw/galaxy/command/commands/admin/GetItem.kt
+++ b/src/main/kotlin/one/oktw/galaxy/command/commands/admin/GetItem.kt
@@ -73,7 +73,7 @@ class GetItem {
                             setOwner(player.uuid)
                         }
                     }
-                    it.source.sendFeedback(Text.translatable("commands.give.success.single", 1, itemStack.toHoverableText(), it.source.displayName), true)
+                    it.source.sendFeedback({ Text.translatable("commands.give.success.single", 1, itemStack.toHoverableText(), it.source.displayName) }, true)
 
                     return@executes Command.SINGLE_SUCCESS
                 }
diff --git a/src/main/kotlin/one/oktw/galaxy/command/commands/admin/RegisterBlock.kt b/src/main/kotlin/one/oktw/galaxy/command/commands/admin/RegisterBlock.kt
index aac04d323..9a0e2fa76 100644
--- a/src/main/kotlin/one/oktw/galaxy/command/commands/admin/RegisterBlock.kt
+++ b/src/main/kotlin/one/oktw/galaxy/command/commands/admin/RegisterBlock.kt
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -59,7 +59,7 @@ class RegisterBlock {
 
                 it.source.world.removeBlockEntity(blockPos)
                 it.source.world.addBlockEntity(block.createBlockEntity(blockPos))
-                it.source.sendFeedback(Text.of("Registered block at ${blockPos.x}, ${blockPos.y}, ${blockPos.z} to ${block.identifier}"), true)
+                it.source.sendFeedback({ Text.of("Registered block at ${blockPos.x}, ${blockPos.y}, ${blockPos.z} to ${block.identifier}") }, true)
 
                 return@executes Command.SINGLE_SUCCESS
             }
diff --git a/src/main/kotlin/one/oktw/galaxy/item/event/Wrench.kt b/src/main/kotlin/one/oktw/galaxy/item/event/Wrench.kt
index a20ec7202..5bfed1886 100644
--- a/src/main/kotlin/one/oktw/galaxy/item/event/Wrench.kt
+++ b/src/main/kotlin/one/oktw/galaxy/item/event/Wrench.kt
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2021
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -195,7 +195,7 @@ class Wrench {
         }
 
         world.setBlockState(blockPos, newState)
-        newState.neighborUpdate(world, blockPos, newState.block, blockPos, true)
+        world.updateNeighbor(newState, blockPos, newState.block, blockPos, true)
         Block.postProcessState(newState, world, blockPos).let { if (!it.isAir) world.setBlockState(blockPos, it, 2) }
 
         return true
diff --git a/src/main/kotlin/one/oktw/galaxy/player/Harvest.kt b/src/main/kotlin/one/oktw/galaxy/player/Harvest.kt
index aabdbfbb4..1ee3f67e9 100644
--- a/src/main/kotlin/one/oktw/galaxy/player/Harvest.kt
+++ b/src/main/kotlin/one/oktw/galaxy/player/Harvest.kt
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2021
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -44,7 +44,7 @@ class Harvest {
         val player = event.player
         if (player in justHarvested) event.cancel = true
 
-        val world = player.getWorld()
+        val world = player.serverWorld
         val blockPos = event.packet.blockHitResult.blockPos
         val blockState = world.getBlockState(blockPos)
 
diff --git a/src/main/kotlin/one/oktw/galaxy/player/Sign.kt b/src/main/kotlin/one/oktw/galaxy/player/Sign.kt
index 412f151c8..63b0b3452 100644
--- a/src/main/kotlin/one/oktw/galaxy/player/Sign.kt
+++ b/src/main/kotlin/one/oktw/galaxy/player/Sign.kt
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2022
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -34,16 +34,17 @@ class Sign {
         val player = event.player
 
         if (player.isSneaking && player.mainHandStack.isEmpty && player.offHandStack.isEmpty) {
-            val entity = player.getWorld().getBlockEntity(event.packet.blockHitResult.blockPos) as? SignBlockEntity ?: return
-            if (entity.editor == null) player.openEditSignScreen(entity)
+            val entity = player.serverWorld.getBlockEntity(event.packet.blockHitResult.blockPos) as? SignBlockEntity ?: return
+            if (entity.editor == null) player.openEditSignScreen(entity, entity.isPlayerFacingFront(player))
         }
     }
 
     @EventListener(sync = true)
     fun onPlayerUpdateSign(event: PlayerUpdateSignEvent) {
-        for (i in 0..3) {
-            event.blockEntity.setTextOnRow(i, Text.of(regex.replace(event.packet.text[i], "§")))
-        }
+        val front = event.packet.isFront
+        val text = event.blockEntity.getText(front)
+        for (i in 0..3) text.withMessage(i, Text.of(regex.replace(event.packet.text[i], "§")))
+        event.blockEntity.setText(text, front)
 
         event.blockEntity.editor = null
         event.cancel = true
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/blocks/Elevator.kt b/src/main/kotlin/one/oktw/galaxy/recipe/blocks/Elevator.kt
index 52ea4fb6f..d5eec01f9 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/blocks/Elevator.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/blocks/Elevator.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.blocks
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -37,9 +37,9 @@ class Elevator : CraftingRecipe {
     private val list =
         listOf(Ingredient(items = listOf(Items.ENDER_PEARL)), Ingredient(items = listOf(Items.IRON_BLOCK)))
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapelessMatches(inv, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapelessMatches(inv, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/blocks/HTCraftingTable.kt b/src/main/kotlin/one/oktw/galaxy/recipe/blocks/HTCraftingTable.kt
index 3127bcea7..59c825243 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/blocks/HTCraftingTable.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/blocks/HTCraftingTable.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.blocks
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -47,9 +47,9 @@ class HTCraftingTable : CraftingRecipe {
         lapisLazuli, obsidian, redStone
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
 
-    override fun craft(inventory: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inventory: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/BookAndQuill.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/BookAndQuill.kt
index 09858ce20..064432731 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/BookAndQuill.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/BookAndQuill.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -40,9 +40,9 @@ class BookAndQuill : CraftingRecipe {
     private val inkSac = Ingredient(items = listOf(Items.INK_SAC))
     private val list = listOf(paper, paper, paper, leather, feather, inkSac)
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapelessMatches(inv, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapelessMatches(inv, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/CarrotOnAStick.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/CarrotOnAStick.kt
index 2a005b08c..40f4ed4cc 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/CarrotOnAStick.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/CarrotOnAStick.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -49,10 +49,10 @@ class CarrotOnAStick : CraftingRecipe {
         stick, carrot, strings
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean =
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean =
         (RecipeUtils.isItemShapedMatches(inv, 3, 3, listLeft) || RecipeUtils.isItemShapedMatches(inv, 3, 3, listRight))
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Chest.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Chest.kt
index fba29f6fa..3de8351e6 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Chest.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Chest.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -43,9 +43,9 @@ class Chest : CraftingRecipe {
         logs, logs, logs
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Dispenser.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Dispenser.kt
index cfd0dd501..94a15f954 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Dispenser.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Dispenser.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -36,9 +36,9 @@ class Dispenser : CraftingRecipe {
     private val list =
         listOf(Ingredient(items = listOf(Items.BOW)), Ingredient(items = listOf(Items.DROPPER)))
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapelessMatches(inv, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapelessMatches(inv, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/DispenserWithBow.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/DispenserWithBow.kt
index 974d8d46e..becb7261f 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/DispenserWithBow.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/DispenserWithBow.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -49,10 +49,10 @@ class DispenserWithBow : CraftingRecipe {
         strings, stick, air
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean =
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean =
         (RecipeUtils.isItemShapedMatches(inv, 3, 3, listLeft) || RecipeUtils.isItemShapedMatches(inv, 3, 3, listRight))
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Glass.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Glass.kt
index 26055066c..de75433ca 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Glass.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Glass.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -71,7 +71,7 @@ class Glass : CraftingRecipe {
         Items.BLACK_STAINED_GLASS
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         dyes.forEach { (recipeItem, _) ->
             val stainedGlass = Ingredient(items = stainedGlass)
@@ -89,7 +89,7 @@ class Glass : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         dyes.forEach { (recipeItem, result) ->
             val stainedGlass = Ingredient(items = stainedGlass)
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPane.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPane.kt
index 028f62929..503a1ae25 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPane.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPane.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -71,7 +71,7 @@ class GlassPane : CraftingRecipe {
         Items.BLACK_STAINED_GLASS_PANE
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         dyes.forEach { (recipeItem, _) ->
             val glassPane = Ingredient(items = stainedGlassPane)
@@ -89,7 +89,7 @@ class GlassPane : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         dyes.forEach { (recipeItem, result) ->
             val glassPane = Ingredient(items = stainedGlassPane)
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPaneRestore.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPaneRestore.kt
index 40b064df2..58d476106 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPaneRestore.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/GlassPaneRestore.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -53,7 +53,7 @@ class GlassPaneRestore : CraftingRecipe {
         Items.WHITE_STAINED_GLASS_PANE to Items.WHITE_STAINED_GLASS
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         glassPane.forEach { (recipeItem, _) ->
             val glassPane = Ingredient(items = listOf(recipeItem))
@@ -70,7 +70,7 @@ class GlassPaneRestore : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         glassPane.forEach { (recipeItem, result) ->
             val glassPane = Ingredient(items = listOf(recipeItem))
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Hopper.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Hopper.kt
index cfbb87871..a50eab18e 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Hopper.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Hopper.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -44,9 +44,9 @@ class Hopper : CraftingRecipe {
         air, ironIngot, air
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Ladder.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Ladder.kt
index 910560c24..ca81c7b64 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Ladder.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Ladder.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -43,9 +43,9 @@ class Ladder : CraftingRecipe {
         logs, air, logs
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Minecart.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Minecart.kt
index 53c6055d6..cdb46f8a3 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Minecart.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Minecart.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -40,7 +40,7 @@ class Minecart : CraftingRecipe {
         Items.TNT to Items.TNT_MINECART
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         minecartType.forEach { (recipeItem, _) ->
             val ironIngot = Ingredient(items = listOf(Items.IRON_INGOT))
@@ -57,7 +57,7 @@ class Minecart : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         minecartType.forEach { (recipeItem, result) ->
             val ironIngot = Ingredient(items = listOf(Items.IRON_INGOT))
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneLamp.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneLamp.kt
index da934da90..3333f15da 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneLamp.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneLamp.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -43,9 +43,9 @@ class RedStoneLamp : CraftingRecipe {
         redStone, glowStoneDust, redStone
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneRepeater.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneRepeater.kt
index a4a74c3c3..fa670eb11 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneRepeater.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/RedStoneRepeater.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -55,7 +55,7 @@ class RedStoneRepeater : CraftingRecipe {
         stone, stone, stone
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean =
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean =
         (RecipeUtils.isItemShapedMatches(inv, 3, 3, noTorch) || RecipeUtils.isItemShapedMatches(inv, 3, 3, oneTorchLeft) || RecipeUtils.isItemShapedMatches(
             inv,
             3,
@@ -63,7 +63,7 @@ class RedStoneRepeater : CraftingRecipe {
             oneTorchRight
         ))
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/SlabRestore.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/SlabRestore.kt
index e27c39fcb..1e888f9be 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/SlabRestore.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/SlabRestore.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -87,7 +87,7 @@ class SlabRestore : CraftingRecipe {
         Items.POLISHED_BLACKSTONE_BRICK_SLAB to Items.POLISHED_BLACKSTONE_BRICKS
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         slabs.forEach { (recipeItem, _) ->
             val list = listOf(
@@ -101,7 +101,7 @@ class SlabRestore : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         slabs.forEach { (recipeItem, result) ->
             val list = listOf(
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassPaneRestore.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassPaneRestore.kt
index e2f45e621..df7592e72 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassPaneRestore.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassPaneRestore.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -52,7 +52,7 @@ class StainedGlassPaneRestore : CraftingRecipe {
         Items.BLACK_STAINED_GLASS_PANE
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         val stainedGlassPane = Ingredient(items = stainedGlassPane)
         val waterBucket = Ingredient(items = listOf(Items.WATER_BUCKET))
@@ -74,7 +74,7 @@ class StainedGlassPaneRestore : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         val stainedGlassPane = Ingredient(items = stainedGlassPane)
         val waterBucket = Ingredient(items = listOf(Items.WATER_BUCKET))
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassRestore.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassRestore.kt
index cd65632b2..e6e3ac6d6 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassRestore.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/StainedGlassRestore.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -52,7 +52,7 @@ class StainedGlassRestore : CraftingRecipe {
         Items.BLACK_STAINED_GLASS
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         val stainedGlass = Ingredient(items = stainedGlass)
         val waterBucket = Ingredient(items = listOf(Items.WATER_BUCKET))
@@ -75,7 +75,7 @@ class StainedGlassRestore : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         val stainedGlass = Ingredient(items = stainedGlass)
         val waterBucket = Ingredient(items = listOf(Items.WATER_BUCKET))
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Stick.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Stick.kt
index 440a70850..188248cae 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Stick.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/Stick.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -39,9 +39,9 @@ class Stick : CraftingRecipe {
         Ingredient(tag = ItemTags.LOGS)
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 1, 2, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 1, 2, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/TrappedChest.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/TrappedChest.kt
index b305288a7..d547bc258 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/TrappedChest.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/TrappedChest.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -43,9 +43,9 @@ class TrappedChest : CraftingRecipe {
         planks, planks, planks
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WarpedFungusOnAStick.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WarpedFungusOnAStick.kt
index bf5304108..8a99827b6 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WarpedFungusOnAStick.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WarpedFungusOnAStick.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -49,10 +49,10 @@ class WarpedFungusOnAStick : CraftingRecipe {
         stick, warpedFungus, strings
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean =
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean =
         (RecipeUtils.isItemShapedMatches(inv, 3, 3, listLeft) || RecipeUtils.isItemShapedMatches(inv, 3, 3, listRight))
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = item.copy()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = item.copy()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WoodenSlab.kt b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WoodenSlab.kt
index 3645eaa4e..44203d864 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WoodenSlab.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/easyRecipe/WoodenSlab.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.easyRecipe
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
@@ -45,7 +45,7 @@ class WoodenSlab : CraftingRecipe {
         ItemTags.WARPED_STEMS to Items.WARPED_SLAB
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean {
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean {
         var match = false
         slabs.forEach { (tag, _) ->
             val list = listOf(
@@ -59,7 +59,7 @@ class WoodenSlab : CraftingRecipe {
         return match
     }
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager): ItemStack {
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager): ItemStack {
         var item = ItemStack.EMPTY
         slabs.forEach { (tag, result) ->
             val list = listOf(
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/tools/Crowbar.kt b/src/main/kotlin/one/oktw/galaxy/recipe/tools/Crowbar.kt
index ebbb65691..81cc79140 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/tools/Crowbar.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/tools/Crowbar.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.tools
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -43,9 +43,9 @@ class Crowbar : CraftingRecipe {
         air, ironIngot,
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 2, 3, list = list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 2, 3, list = list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = Tool.CROWBAR.createItemStack()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = Tool.CROWBAR.createItemStack()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/tools/Wrench.kt b/src/main/kotlin/one/oktw/galaxy/recipe/tools/Wrench.kt
index 3a6b0d5d0..ad12f3675 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/tools/Wrench.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/tools/Wrench.kt
@@ -20,7 +20,7 @@ package one.oktw.galaxy.recipe.tools
 
 import net.fabricmc.api.EnvType
 import net.fabricmc.api.Environment
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.Items
 import net.minecraft.recipe.CraftingRecipe
 import net.minecraft.recipe.RecipeSerializer
@@ -44,9 +44,9 @@ class Wrench : CraftingRecipe {
         air, ironIngot, air
     )
 
-    override fun matches(inv: CraftingInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list = list)
+    override fun matches(inv: RecipeInputInventory, world: World): Boolean = RecipeUtils.isItemShapedMatches(inv, 3, 3, list = list)
 
-    override fun craft(inv: CraftingInventory, registryManager: DynamicRegistryManager) = Tool.WRENCH.createItemStack()
+    override fun craft(inv: RecipeInputInventory, registryManager: DynamicRegistryManager) = Tool.WRENCH.createItemStack()
 
     @Environment(EnvType.CLIENT)
     override fun fits(width: Int, height: Int): Boolean {
diff --git a/src/main/kotlin/one/oktw/galaxy/recipe/utils/RecipeUtils.kt b/src/main/kotlin/one/oktw/galaxy/recipe/utils/RecipeUtils.kt
index 55ae1ecd4..8e0b26ed0 100644
--- a/src/main/kotlin/one/oktw/galaxy/recipe/utils/RecipeUtils.kt
+++ b/src/main/kotlin/one/oktw/galaxy/recipe/utils/RecipeUtils.kt
@@ -1,6 +1,6 @@
 /*
  * OKTW Galaxy Project
- * Copyright (C) 2018-2021
+ * Copyright (C) 2018-2023
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published
@@ -18,12 +18,12 @@
 
 package one.oktw.galaxy.recipe.utils
 
-import net.minecraft.inventory.CraftingInventory
+import net.minecraft.inventory.RecipeInputInventory
 import net.minecraft.item.ItemStack
 import net.minecraft.item.Items
 
 object RecipeUtils {
-    fun isItemShapedMatches(inv: CraftingInventory, width: Int, height: Int, list: List<Ingredient>): Boolean {
+    fun isItemShapedMatches(inv: RecipeInputInventory, width: Int, height: Int, list: List<Ingredient>): Boolean {
         for (x in 0..(inv.width - width)) {
             for (y in 0..(inv.height - height)) {
                 if (shapeMatchesSmall(inv, width, height, x, y, true, list)) return true
@@ -35,7 +35,7 @@ object RecipeUtils {
     }
 
     private fun shapeMatchesSmall(
-        inv: CraftingInventory,
+        inv: RecipeInputInventory,
         width: Int,
         height: Int,
         offsetX: Int,
@@ -61,7 +61,7 @@ object RecipeUtils {
         return true
     }
 
-    fun isItemShapelessMatches(inv: CraftingInventory, list: List<Ingredient>): Boolean {
+    fun isItemShapelessMatches(inv: RecipeInputInventory, list: List<Ingredient>): Boolean {
         val inputItems = mutableListOf<ItemStack>()
 
         for (i in 0 until inv.size()) {
diff --git a/src/main/resources/accessor.mixin.json b/src/main/resources/accessor.mixin.json
index 77bc1414d..f753904e9 100644
--- a/src/main/resources/accessor.mixin.json
+++ b/src/main/resources/accessor.mixin.json
@@ -3,7 +3,6 @@
   "package": "one.oktw.galaxy.mixin.accessor",
   "compatibilityLevel": "JAVA_16",
   "mixins": [
-    "AsyncChunk_VersionedChunkStorage",
     "BeaconLevelAccessor",
     "PlayerAbilitiesAccessor",
     "SerializingRegionBasedStorageAccessor",
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index a45e4a709..4de5be9cc 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -27,9 +27,9 @@
     "recipe.mixin.json"
   ],
   "depends": {
-    "minecraft": "1.19.x",
-    "fabricloader": ">=0.14.6",
-    "fabric": ">=0.55.2"
+    "minecraft": "1.20.x",
+    "fabricloader": ">=0.14.21",
+    "fabric": ">=0.83.0"
   },
   "suggests": {}
 }

From 995afd824a17aabe7b9f55adfe103da659096daa Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Mon, 12 Jun 2023 09:59:49 +0800
Subject: [PATCH 2/8] Fix sign style

---
 .../MixinPlayerUpdateSign_NetworkHandler.java | 58 -------------------
 .../mixin/tweak/SignStyle_NetworkHandler.java | 38 ++++++++++++
 src/main/kotlin/one/oktw/galaxy/Main.kt       |  2 -
 .../kotlin/one/oktw/galaxy/player/Sign.kt     | 52 -----------------
 src/main/resources/galaxy.mixin.json          |  1 -
 src/main/resources/tweak.mixin.json           |  3 +-
 6 files changed, 40 insertions(+), 114 deletions(-)
 delete mode 100644 src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java
 create mode 100644 src/main/java/one/oktw/galaxy/mixin/tweak/SignStyle_NetworkHandler.java
 delete mode 100644 src/main/kotlin/one/oktw/galaxy/player/Sign.kt

diff --git a/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java b/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java
deleted file mode 100644
index c6703d085..000000000
--- a/src/main/java/one/oktw/galaxy/mixin/event/MixinPlayerUpdateSign_NetworkHandler.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * OKTW Galaxy Project
- * Copyright (C) 2018-2023
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-package one.oktw.galaxy.mixin.event;
-
-import net.minecraft.block.Block;
-import net.minecraft.block.entity.BlockEntity;
-import net.minecraft.block.entity.SignBlockEntity;
-import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket;
-import net.minecraft.server.filter.FilteredMessage;
-import net.minecraft.server.network.ServerPlayNetworkHandler;
-import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.server.world.ServerWorld;
-import net.minecraft.util.math.BlockPos;
-import one.oktw.galaxy.event.EventManager;
-import one.oktw.galaxy.event.type.PlayerUpdateSignEvent;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
-
-import java.util.List;
-
-@Mixin(ServerPlayNetworkHandler.class)
-public class MixinPlayerUpdateSign_NetworkHandler {
-    @Shadow
-    public ServerPlayerEntity player;
-
-    @Inject(method = "onSignUpdate(Lnet/minecraft/network/packet/c2s/play/UpdateSignC2SPacket;Ljava/util/List;)V",
-        at = @At(value = "INVOKE", target = "Lnet/minecraft/block/entity/SignBlockEntity;tryChangeText(Lnet/minecraft/entity/player/PlayerEntity;ZLjava/util/List;)V"),
-        cancellable = true,
-        locals = LocalCapture.CAPTURE_FAILSOFT)
-    private void onSignUpdate(UpdateSignC2SPacket packet, List<FilteredMessage> signText, CallbackInfo ci, ServerWorld serverWorld, BlockPos blockPos, BlockEntity blockEntity, SignBlockEntity signBlockEntity) {
-        if (EventManager.safeEmit(new PlayerUpdateSignEvent(packet, player, signBlockEntity)).getCancel()) {
-            ci.cancel();
-
-            signBlockEntity.markDirty();
-            serverWorld.updateListeners(blockPos, signBlockEntity.getCachedState(), signBlockEntity.getCachedState(), Block.NOTIFY_ALL);
-        }
-    }
-}
diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/SignStyle_NetworkHandler.java b/src/main/java/one/oktw/galaxy/mixin/tweak/SignStyle_NetworkHandler.java
new file mode 100644
index 000000000..6e5c073d8
--- /dev/null
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/SignStyle_NetworkHandler.java
@@ -0,0 +1,38 @@
+/*
+ * OKTW Galaxy Project
+ * Copyright (C) 2018-2023
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package one.oktw.galaxy.mixin.tweak;
+
+import net.minecraft.server.network.ServerPlayNetworkHandler;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+@Mixin(ServerPlayNetworkHandler.class)
+public class SignStyle_NetworkHandler {
+    private static final Pattern styles = Pattern.compile("&(?=[a-f0-9k-or])");
+
+    @Redirect(method = "onUpdateSign", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;map(Ljava/util/function/Function;)Ljava/util/stream/Stream;"))
+    private Stream<String> onSignUpdate(Stream<String> instance, Function<String, String> function) {
+        return instance.map(text -> styles.matcher(text).replaceAll("§"));
+    }
+}
diff --git a/src/main/kotlin/one/oktw/galaxy/Main.kt b/src/main/kotlin/one/oktw/galaxy/Main.kt
index 2cc40573b..ab61ee396 100644
--- a/src/main/kotlin/one/oktw/galaxy/Main.kt
+++ b/src/main/kotlin/one/oktw/galaxy/Main.kt
@@ -41,7 +41,6 @@ import one.oktw.galaxy.event.type.ProxyResponseEvent
 import one.oktw.galaxy.item.event.CustomItemEventHandler
 import one.oktw.galaxy.item.event.Wrench
 import one.oktw.galaxy.player.Harvest
-import one.oktw.galaxy.player.Sign
 import one.oktw.galaxy.proxy.api.ProxyAPI
 import one.oktw.galaxy.recipe.RecipeRegistry
 import java.util.*
@@ -96,7 +95,6 @@ class Main : DedicatedServerModInitializer, CoroutineScope {
             eventManager.register(Exchange())
             eventManager.register(Harvest())
             eventManager.register(BlockEvents())
-            eventManager.register(Sign())
             eventManager.register(Wrench())
             eventManager.register(Elevator())
             eventManager.register(AngelBlock())
diff --git a/src/main/kotlin/one/oktw/galaxy/player/Sign.kt b/src/main/kotlin/one/oktw/galaxy/player/Sign.kt
deleted file mode 100644
index 63b0b3452..000000000
--- a/src/main/kotlin/one/oktw/galaxy/player/Sign.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * OKTW Galaxy Project
- * Copyright (C) 2018-2023
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-package one.oktw.galaxy.player
-
-import net.minecraft.block.entity.SignBlockEntity
-import net.minecraft.text.Text
-import one.oktw.galaxy.event.annotation.EventListener
-import one.oktw.galaxy.event.type.PlayerInteractBlockEvent
-import one.oktw.galaxy.event.type.PlayerUpdateSignEvent
-
-class Sign {
-    companion object {
-        private val regex = Regex("&(?=[a-f0-9k-or])")
-    }
-
-    @EventListener(sync = true)
-    fun onPlayerInteractBlock(event: PlayerInteractBlockEvent) {
-        val player = event.player
-
-        if (player.isSneaking && player.mainHandStack.isEmpty && player.offHandStack.isEmpty) {
-            val entity = player.serverWorld.getBlockEntity(event.packet.blockHitResult.blockPos) as? SignBlockEntity ?: return
-            if (entity.editor == null) player.openEditSignScreen(entity, entity.isPlayerFacingFront(player))
-        }
-    }
-
-    @EventListener(sync = true)
-    fun onPlayerUpdateSign(event: PlayerUpdateSignEvent) {
-        val front = event.packet.isFront
-        val text = event.blockEntity.getText(front)
-        for (i in 0..3) text.withMessage(i, Text.of(regex.replace(event.packet.text[i], "§")))
-        event.blockEntity.setText(text, front)
-
-        event.blockEntity.editor = null
-        event.cancel = true
-    }
-}
diff --git a/src/main/resources/galaxy.mixin.json b/src/main/resources/galaxy.mixin.json
index 1d0f2d23d..07f25f563 100644
--- a/src/main/resources/galaxy.mixin.json
+++ b/src/main/resources/galaxy.mixin.json
@@ -11,7 +11,6 @@
     "MixinPlayerInteractItem_NetworkHandler",
     "MixinPlayerJump_NetworkHandler",
     "MixinPlayerSneak_NetworkHandler",
-    "MixinPlayerUpdateSign_NetworkHandler",
     "MixinPlayerUseItemOnBlock_ItemStack"
   ],
   "client": [],
diff --git a/src/main/resources/tweak.mixin.json b/src/main/resources/tweak.mixin.json
index 2086d925a..01ea7b414 100644
--- a/src/main/resources/tweak.mixin.json
+++ b/src/main/resources/tweak.mixin.json
@@ -25,7 +25,8 @@
     "MixinRCON_RconClient",
     "MixinThrownCountdown_Entity",
     "MixinThrownCountdown_ProjectileEntity",
-    "MixinThrownCountdown_ThrownEntity"
+    "MixinThrownCountdown_ThrownEntity",
+    "SignStyle_NetworkHandler"
   ],
   "client": [],
   "injectors": {

From 5f99c67758c9023eefa64634d4a5ab3153a69a7d Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Mon, 12 Jun 2023 11:50:50 +0800
Subject: [PATCH 3/8] Tweak ChunkIO

---
 .../MixinAsyncChunk_StorageIoWorker.java      | 49 ++++++++++++++-----
 src/main/resources/galaxy.accesswidener       |  2 +
 2 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_StorageIoWorker.java b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_StorageIoWorker.java
index 77433d6ea..615339491 100644
--- a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_StorageIoWorker.java
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_StorageIoWorker.java
@@ -18,22 +18,27 @@
 
 package one.oktw.galaxy.mixin.tweak;
 
+import net.minecraft.nbt.NbtCompound;
 import net.minecraft.util.Pair;
 import net.minecraft.util.math.ChunkPos;
 import net.minecraft.util.thread.TaskExecutor;
 import net.minecraft.util.thread.TaskQueue;
 import net.minecraft.world.storage.StorageIoWorker;
+import net.minecraft.world.storage.StorageIoWorker.Priority;
 import one.oktw.galaxy.util.KotlinCoroutineTaskExecutor;
+import org.jetbrains.annotations.Nullable;
 import org.spongepowered.asm.mixin.*;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.Redirect;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -62,7 +67,7 @@ private void parallelExecutor(Path directory, boolean dsync, String name, Callba
 
     /**
      * @author James58899
-     * @reason null check and delay remove
+     * @reason no low priority write & bulk write
      */
     @Overwrite
     private void writeResult() {
@@ -70,25 +75,47 @@ private void writeResult() {
             HashMap<Long, ArrayList<Pair<ChunkPos, StorageIoWorker.Result>>> map = new HashMap<>();
             results.forEach((pos, result) -> map.computeIfAbsent(ChunkPos.toLong(pos.getRegionX(), pos.getRegionZ()), k -> new ArrayList<>()).add(new Pair<>(pos, result)));
             map.values().forEach(list ->
-                executor.send(new TaskQueue.PrioritizedTask(1 /* BACKGROUND */, () -> list.forEach(pair -> write(pair.getLeft(), pair.getRight()))))
+                executor.send(new TaskQueue.PrioritizedTask(Priority.FOREGROUND.ordinal(), () -> list.forEach(pair -> write(pair.getLeft(), pair.getRight()))))
             );
-            this.executor.send(new TaskQueue.PrioritizedTask(2 /* WRITE_DONE */, () -> {
+            this.executor.send(new TaskQueue.PrioritizedTask(Priority.BACKGROUND.ordinal(), () -> {
                 writeLock.set(false);
                 writeResult();
             }));
         }
     }
 
-    @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/storage/RegionBasedStorage;write(Lnet/minecraft/util/math/ChunkPos;Lnet/minecraft/nbt/NbtCompound;)V"), cancellable = true)
+    /**
+     * @author James58899
+     * @reason no low priority write
+     */
+    @Overwrite
+    private void writeRemainingResults() {
+        writeResult();
+    }
+
+    /**
+     * @author James58899
+     * @reason no delay set result
+     */
+    @Overwrite
+    public CompletableFuture<Void> setResult(ChunkPos pos, @Nullable NbtCompound nbt) {
+        StorageIoWorker.Result result = this.results.computeIfAbsent(pos, pos2 -> new StorageIoWorker.Result(nbt));
+        result.nbt = nbt;
+        return result.future;
+    }
+
+    @Inject(method = "readChunkData", at = @At("HEAD"), cancellable = true)
+    private void fastRead(ChunkPos pos, CallbackInfoReturnable<CompletableFuture<Optional<NbtCompound>>> cir) {
+        StorageIoWorker.Result result = this.results.get(pos);
+        if (result != null) {
+            cir.setReturnValue(CompletableFuture.completedFuture(Optional.ofNullable(result.nbt)));
+        }
+    }
+
+    @Inject(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/storage/RegionBasedStorage;write(Lnet/minecraft/util/math/ChunkPos;Lnet/minecraft/nbt/NbtCompound;)V", shift = At.Shift.BEFORE), cancellable = true)
     private void removeResults(ChunkPos pos, StorageIoWorker.Result result, CallbackInfo ci) {
         if (!this.results.remove(pos, result)) { // Only write once
             ci.cancel();
         }
     }
-
-    @SuppressWarnings("UnresolvedMixinReference")
-    @Redirect(method = "method_27938", at = @At(value = "NEW", target = "net/minecraft/util/thread/TaskQueue$PrioritizedTask"))
-    private static TaskQueue.PrioritizedTask changeShutdownPriority(int priority, Runnable runnable) {
-        return new TaskQueue.PrioritizedTask(3 /* SHUTDOWN */, runnable);
-    }
 }
diff --git a/src/main/resources/galaxy.accesswidener b/src/main/resources/galaxy.accesswidener
index d6fde1a5b..bea854198 100644
--- a/src/main/resources/galaxy.accesswidener
+++ b/src/main/resources/galaxy.accesswidener
@@ -7,3 +7,5 @@ accessible    class    net/minecraft/server/command/CloneCommand$BlockInfo
 # Async chunk load
 accessible    class    net/minecraft/world/storage/StorageIoWorker$Result
 accessible    class    net/minecraft/world/storage/StorageIoWorker$Priority
+accessible field net/minecraft/world/storage/StorageIoWorker$Result nbt Lnet/minecraft/nbt/NbtCompound;
+accessible field net/minecraft/world/storage/StorageIoWorker$Result future Ljava/util/concurrent/CompletableFuture;

From 833f684e903970cd8d1f24d799cdd58c034b0a48 Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Mon, 12 Jun 2023 11:54:58 +0800
Subject: [PATCH 4/8] Fix 1.20 migrate

---
 .../mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java
index 015864910..5798022f6 100644
--- a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinAsyncChunk_ServerPlayNetworkHandler.java
@@ -23,6 +23,7 @@
 import net.minecraft.server.network.ServerPlayNetworkHandler;
 import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.server.world.ServerWorld;
+import net.minecraft.util.math.ChunkPos;
 import net.minecraft.util.math.ChunkSectionPos;
 import net.minecraft.util.math.Vec3d;
 import org.spongepowered.asm.mixin.Mixin;
@@ -52,7 +53,7 @@ private void noBlockingMove(PlayerMoveC2SPacket packet, CallbackInfo ci) {
 
         int x = ChunkSectionPos.getSectionCoord(clampHorizontal(packet.getX(this.player.getX())));
         int z = ChunkSectionPos.getSectionCoord(clampHorizontal(packet.getZ(this.player.getZ())));
-        if (!player.getWorld().getChunkManager().isChunkLoaded(x, z)) {
+        if (!player.getServerWorld().getChunkManager().isTickingFutureReady(ChunkPos.toLong(x, z))) {
             player.setVelocity(Vec3d.ZERO);
             requestTeleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYaw(), this.player.getPitch());
             ci.cancel();

From 6ea938e9ac0e0602ebe05536e002d49132351e9b Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Tue, 5 Sep 2023 16:06:46 +0800
Subject: [PATCH 5/8] Update 1.20.1

---
 build.gradle.kts                         |  14 +++++++-------
 gradle/wrapper/gradle-wrapper.jar        | Bin 61608 -> 62076 bytes
 gradle/wrapper/gradle-wrapper.properties |   2 +-
 gradlew                                  |   7 ++++---
 4 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/build.gradle.kts b/build.gradle.kts
index 19445e779..2ef01a6fb 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,15 +2,15 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     //    "maven-publish"
-    kotlin("jvm") version "1.8.21"
-    id("fabric-loom") version "1.2-SNAPSHOT"
+    kotlin("jvm") version "1.9.10"
+    id("fabric-loom") version "1.3-SNAPSHOT"
 }
 
 val version = "0.0.1"
 val group = "one.oktw"
 
-val fabricVersion = "0.83.0+1.20"
-val galaxyLibVersion = "63aec6ee"
+val fabricVersion = "0.88.1+1.20.1"
+val galaxyLibVersion = "adc1ed90"
 
 repositories {
     mavenCentral()
@@ -40,9 +40,9 @@ loom {
 
 dependencies {
     // Core
-    minecraft(group = "com.mojang", name = "minecraft", version = "1.20")
-    mappings(group = "net.fabricmc", name = "yarn", version = "1.20+build.1", classifier = "v2")
-    modImplementation(group = "net.fabricmc", name = "fabric-loader", version = "0.14.21")
+    minecraft(group = "com.mojang", name = "minecraft", version = "1.20.1")
+    mappings(group = "net.fabricmc", name = "yarn", version = "1.20.1+build.10", classifier = "v2")
+    modImplementation(group = "net.fabricmc", name = "fabric-loader", version = "0.14.22")
 
     // fabric api
     modImplementation(group = "net.fabricmc.fabric-api", name = "fabric-api", version = fabricVersion) {
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index ccebba7710deaf9f98673a68957ea02138b60d0a..c1962a79e29d3e0ab67b14947c167a862655af9b 100644
GIT binary patch
delta 8979
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
F{TFmrHLU;u

delta 8469
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

diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 37aef8d3f..c30b486a8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
 networkTimeout=10000
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 79a61d421..aeb74cbb4 100755
--- a/gradlew
+++ b/gradlew
@@ -85,9 +85,6 @@ done
 APP_BASE_NAME=${0##*/}
 APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
 
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
 
@@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then
     done
 fi
 
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
 # Collect all arguments for the java command;
 #   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
 #     shell script including quotes and variable substitutions, so put them in

From dca5137f1a9ae1ef74eef02d9d3ac64413fffedf Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Tue, 5 Sep 2023 16:09:02 +0800
Subject: [PATCH 6/8] Bump docker

---
 docker | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docker b/docker
index f14a66d37..d36eed247 160000
--- a/docker
+++ b/docker
@@ -1 +1 @@
-Subproject commit f14a66d37bc2649fae5bbc8c0de1334c46816ee6
+Subproject commit d36eed2479387c34ad502d7a7cedb8a76f17b8ed

From cdf05b19ec0a308227bf7f1bfcd0e2a8342abb02 Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Tue, 5 Sep 2023 17:46:33 +0800
Subject: [PATCH 7/8] Skip sync registry added by galaxy workaround fabric-api
 force client registry match server

---
 .../mixin/tweak/MixinSkipSyncRegistry.java    | 38 +++++++++++++++++++
 src/main/resources/tweak.mixin.json           |  1 +
 2 files changed, 39 insertions(+)
 create mode 100644 src/main/java/one/oktw/galaxy/mixin/tweak/MixinSkipSyncRegistry.java

diff --git a/src/main/java/one/oktw/galaxy/mixin/tweak/MixinSkipSyncRegistry.java b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinSkipSyncRegistry.java
new file mode 100644
index 000000000..4f135a735
--- /dev/null
+++ b/src/main/java/one/oktw/galaxy/mixin/tweak/MixinSkipSyncRegistry.java
@@ -0,0 +1,38 @@
+/*
+ * OKTW Galaxy Project
+ * Copyright (C) 2018-2023
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package one.oktw.galaxy.mixin.tweak;
+
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import net.fabricmc.fabric.impl.registry.sync.RegistrySyncManager;
+import net.minecraft.util.Identifier;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+@Mixin(value = RegistrySyncManager.class, remap = false)
+public class MixinSkipSyncRegistry {
+    @Redirect(method = "createAndPopulateRegistryMap", at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/Object2IntMap;put(Ljava/lang/Object;I)I", ordinal = 0))
+    private static int skipSyncGalaxyRegistry(Object2IntMap<Identifier> instance, Object o, int i) {
+        Identifier id = (Identifier) o;
+        if (id.getNamespace().equals("galaxy")) {
+            return instance.defaultReturnValue();
+        }
+        return instance.put((Identifier) o, i);
+    }
+}
diff --git a/src/main/resources/tweak.mixin.json b/src/main/resources/tweak.mixin.json
index 01ea7b414..8bde79761 100644
--- a/src/main/resources/tweak.mixin.json
+++ b/src/main/resources/tweak.mixin.json
@@ -23,6 +23,7 @@
     "MixinOptimizeContainer_LootableContainerBlockEntity",
     "MixinRCON_RconBase",
     "MixinRCON_RconClient",
+    "MixinSkipSyncRegistry",
     "MixinThrownCountdown_Entity",
     "MixinThrownCountdown_ProjectileEntity",
     "MixinThrownCountdown_ThrownEntity",

From fe0652bdd4d537658103cc727695397998c02148 Mon Sep 17 00:00:00 2001
From: James58899 <james59988@gmail.com>
Date: Thu, 7 Sep 2023 16:05:36 +0800
Subject: [PATCH 8/8] Bump docker

---
 docker | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docker b/docker
index d36eed247..61e036276 160000
--- a/docker
+++ b/docker
@@ -1 +1 @@
-Subproject commit d36eed2479387c34ad502d7a7cedb8a76f17b8ed
+Subproject commit 61e036276f1f45bb25830a04b8588fffb2176ca1