mod for the islanders smp

1.0.2 - add logging - fix crown crafting race condition

+88 -23
+1 -1
gradle.properties
··· 10 10 fabric_kotlin_version=1.13.4+kotlin.2.2.0 11 11 12 12 # Mod Properties 13 - mod_version=1.0.1 13 + mod_version=1.0.2 14 14 maven_group=net.radsteve.islanders 15 15 archives_base_name=islanders-smp 16 16
+12
src/main/java/net/radsteve/islanders/mixin/AbstractCraftingScreenHandlerAccessor.java
··· 1 + package net.radsteve.islanders.mixin; 2 + 3 + import net.minecraft.inventory.CraftingResultInventory; 4 + import net.minecraft.screen.AbstractCraftingScreenHandler; 5 + import org.spongepowered.asm.mixin.Mixin; 6 + import org.spongepowered.asm.mixin.gen.Accessor; 7 + 8 + @Mixin(AbstractCraftingScreenHandler.class) 9 + public interface AbstractCraftingScreenHandlerAccessor { 10 + @Accessor 11 + CraftingResultInventory getCraftingResultInventory(); 12 + }
+12 -7
src/main/java/net/radsteve/islanders/mixin/BundleHandlerMixin.java
··· 13 13 import net.minecraft.screen.sync.ItemStackHash; 14 14 import net.minecraft.server.network.ServerPlayNetworkHandler; 15 15 import net.minecraft.server.network.ServerPlayerEntity; 16 + import net.radsteve.islanders.Islanders; 16 17 import org.spongepowered.asm.mixin.Mixin; 17 18 import org.spongepowered.asm.mixin.Shadow; 18 19 import org.spongepowered.asm.mixin.Unique; ··· 35 36 36 37 @Unique 37 38 private Item islanders$lastClickedItem = null; 38 - /// DIRECTION TWO - bundle clicked first and dragged onto item 39 - /// This is a mess. Do not touch :) 40 - 41 - @Unique 42 - private ClickSlotC2SPacket islanders$lastClickPacket$0 = null; 43 - @Unique 44 - private RegistryEntry<Item> islanders$lastClickedItem$0 = null; 45 39 46 40 @Inject(method = "onClickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/ScreenHandler;disableSyncing()V"), cancellable = true) 47 41 private void islanders$onClickSlot(ClickSlotC2SPacket packet, CallbackInfo ci) { ··· 95 89 ci.cancel(); 96 90 97 91 islanders$lastClickPacket = packet; 92 + 93 + Islanders.getLogger().info("{} tried moving special item into bundle", player.getNameForScoreboard()); 98 94 } 99 95 96 + /// DIRECTION TWO - bundle clicked first and dragged onto item 97 + /// This is a mess. Do not touch :) 98 + 99 + @Unique 100 + private ClickSlotC2SPacket islanders$lastClickPacket$0 = null; 101 + @Unique 102 + private RegistryEntry<Item> islanders$lastClickedItem$0 = null; 103 + 100 104 @Inject(method = "onClickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/ScreenHandler;disableSyncing()V"), cancellable = true) 101 105 private void islanders$onClickSlot$0(ClickSlotC2SPacket packet, CallbackInfo ci) { 102 106 if (islanders$lastClickPacket$0 == null) { ··· 162 166 ci.cancel(); 163 167 164 168 islanders$lastClickPacket$0 = packet; 169 + Islanders.getLogger().info("{} tried moving special item into bundle", player.getNameForScoreboard()); 165 170 } 166 171 }
+9
src/main/java/net/radsteve/islanders/mixin/CrafterScreenHandlerMixin.java
··· 1 1 package net.radsteve.islanders.mixin; 2 2 3 3 import com.llamalad7.mixinextras.sugar.Local; 4 + import net.minecraft.entity.player.PlayerEntity; 4 5 import net.minecraft.item.ItemStack; 5 6 import net.minecraft.screen.CrafterScreenHandler; 7 + import net.radsteve.islanders.Islanders; 6 8 import net.radsteve.islanders.SpecialItem; 9 + import org.spongepowered.asm.mixin.Final; 7 10 import org.spongepowered.asm.mixin.Mixin; 11 + import org.spongepowered.asm.mixin.Shadow; 8 12 import org.spongepowered.asm.mixin.injection.At; 9 13 import org.spongepowered.asm.mixin.injection.Inject; 10 14 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 15 12 16 @Mixin(CrafterScreenHandler.class) 13 17 public class CrafterScreenHandlerMixin { 18 + @Shadow 19 + @Final 20 + private PlayerEntity player; 21 + 14 22 @Inject(method = "updateResult", at = @At(value = "INVOKE", target = "Lnet/minecraft/inventory/CraftingResultInventory;setStack(ILnet/minecraft/item/ItemStack;)V"), cancellable = true) 15 23 private void islanders$updateResult(CallbackInfo ci, @Local ItemStack stack) { 16 24 if (SpecialItem.Companion.find(stack) != null) { 25 + Islanders.getLogger().info("{} tried crafting special item via crafter", player.getNameForScoreboard()); 17 26 ci.cancel(); 18 27 } 19 28 }
+2
src/main/java/net/radsteve/islanders/mixin/CraftingResultSlotMixin.java
··· 4 4 import net.minecraft.item.ItemStack; 5 5 import net.minecraft.screen.slot.CraftingResultSlot; 6 6 import net.minecraft.server.network.ServerPlayerEntity; 7 + import net.radsteve.islanders.Islanders; 7 8 import net.radsteve.islanders.SpecialItem; 8 9 import net.radsteve.islanders.crown.CrownAnimation; 9 10 import org.spongepowered.asm.mixin.Final; ··· 22 23 @Inject(method = "onCrafted(Lnet/minecraft/item/ItemStack;)V", at = @At("HEAD")) 23 24 private void islanders$onCrafted(ItemStack stack, CallbackInfo ci) { 24 25 if (SpecialItem.Crown.check(stack)) { 26 + Islanders.getLogger().info("{} crafted the Islanders Crown!", player.getNameForScoreboard()); 25 27 CrownAnimation.INSTANCE.craftingFinished((ServerPlayerEntity) player); 26 28 } 27 29 }
+2
src/main/java/net/radsteve/islanders/mixin/CraftingScreenHandlerMixin.java
··· 10 10 import net.minecraft.screen.CraftingScreenHandler; 11 11 import net.minecraft.screen.ScreenHandler; 12 12 import net.minecraft.server.world.ServerWorld; 13 + import net.radsteve.islanders.Islanders; 13 14 import net.radsteve.islanders.IslandersData; 14 15 import net.radsteve.islanders.SpecialItem; 15 16 import org.jetbrains.annotations.Nullable; ··· 32 33 @Local ItemStack result 33 34 ) { 34 35 if (SpecialItem.Crown.check(result) && IslandersData.INSTANCE.obtained("crown")) { 36 + Islanders.getLogger().info("{} tried crafting crown which was already obtained", player.getNameForScoreboard()); 35 37 ci.cancel(); 36 38 } 37 39 }
+13 -12
src/main/java/net/radsteve/islanders/mixin/GrindstoneScreenHandlerMixin.java
··· 2 2 3 3 import net.minecraft.inventory.Inventory; 4 4 import net.minecraft.item.ItemStack; 5 - import net.minecraft.item.Items; 6 5 import net.minecraft.screen.GrindstoneScreenHandler; 6 + import net.radsteve.islanders.Islanders; 7 7 import net.radsteve.islanders.SpecialItem; 8 8 import org.spongepowered.asm.mixin.Final; 9 9 import org.spongepowered.asm.mixin.Mixin; ··· 14 14 15 15 @Mixin(GrindstoneScreenHandler.class) 16 16 public abstract class GrindstoneScreenHandlerMixin { 17 - @Shadow 18 - @Final 19 - Inventory input; 17 + @Shadow 18 + @Final 19 + Inventory input; 20 20 21 - @Shadow 22 - protected abstract ItemStack getOutputStack(ItemStack firstInput, ItemStack secondInput); 21 + @Shadow 22 + protected abstract ItemStack getOutputStack(ItemStack firstInput, ItemStack secondInput); 23 23 24 - @Inject(method = "updateResult", at = @At("HEAD"), cancellable = true) 25 - private void islanders$updateResult(CallbackInfo ci) { 26 - final ItemStack output = getOutputStack(input.getStack(0), input.getStack(1)); 27 - if (SpecialItem.Companion.find(output) != null) { 28 - ci.cancel(); 29 - } 24 + @Inject(method = "updateResult", at = @At("HEAD"), cancellable = true) 25 + private void islanders$updateResult(CallbackInfo ci) { 26 + final ItemStack output = getOutputStack(input.getStack(0), input.getStack(1)); 27 + if (SpecialItem.Companion.find(output) != null) { 28 + Islanders.getLogger().info("Tried grindstoning special item"); 29 + ci.cancel(); 30 30 } 31 + } 31 32 }
+4 -1
src/main/java/net/radsteve/islanders/mixin/ItemEntityMixin.java
··· 6 6 import net.minecraft.item.ItemStack; 7 7 import net.minecraft.server.network.ServerPlayerEntity; 8 8 import net.minecraft.server.world.ServerWorld; 9 + import net.radsteve.islanders.Islanders; 9 10 import net.radsteve.islanders.SpecialItem; 10 11 import net.radsteve.islanders.event.ItemPickupCallback; 11 12 import net.radsteve.islanders.event.ResizeItemEntityAndAddRemainingStackCallback; ··· 52 53 53 54 @Inject(method = "damage", at = @At("HEAD"), cancellable = true) 54 55 private void islanders$damage(ServerWorld world, DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) { 55 - if (SpecialItem.Companion.find(getStack()) != null) { 56 + final SpecialItem item = SpecialItem.Companion.find(getStack()); 57 + if (item != null) { 58 + Islanders.getLogger().info("Attempted to damage special item {} by {}", item.getId(), source.getType().msgId()); 56 59 cir.setReturnValue(false); 57 60 } 58 61 }
+6 -1
src/main/java/net/radsteve/islanders/mixin/PlayerEntityMixin.java
··· 9 9 import net.minecraft.item.ItemStack; 10 10 import net.minecraft.scoreboard.Scoreboard; 11 11 import net.minecraft.server.world.ServerWorld; 12 + import net.radsteve.islanders.Islanders; 12 13 import net.radsteve.islanders.ItemEffects; 13 14 import net.radsteve.islanders.SpecialItem; 14 15 import org.spongepowered.asm.mixin.Final; ··· 36 37 final DamageSource recentDamageSource = entity.getRecentDamageSource(); 37 38 38 39 if (recentDamageSource != null && entity.getRecentDamageSource().isOf(DamageTypes.OUT_OF_WORLD)) { 40 + Islanders.getLogger().info("{} died to the void with special items on them, dropping items", entity.getNameForScoreboard()); 39 41 return; 40 42 } 41 43 42 - if (entity.getPrimeAdversary() instanceof PlayerEntity) { 44 + if (entity.getPrimeAdversary() instanceof PlayerEntity killer) { 45 + Islanders.getLogger().info("{} died to {} with special items on them, dropping items", entity.getNameForScoreboard(), killer.getNameForScoreboard()); 43 46 return; 44 47 } 48 + 49 + Islanders.getLogger().info("{} died with special items on them without a killer, giving back on respawn", entity.getNameForScoreboard()); 45 50 46 51 for (int idx = 0; idx < this.inventory.size(); idx++) { 47 52 ItemStack stack = this.inventory.getStack(idx);
+3 -1
src/main/kotlin/net/radsteve/islanders/Islanders.kt
··· 13 13 import net.radsteve.islanders.mace.MaceRewards 14 14 import net.radsteve.islanders.rule.DefaultRules 15 15 import net.radsteve.islanders.rule.RuleHandlers 16 + import org.slf4j.Logger 16 17 import org.slf4j.LoggerFactory 17 18 18 19 public object Islanders : ModInitializer, ServerTickEvents.EndTick { 19 - private val logger = LoggerFactory.getLogger("islanders") 20 + @JvmStatic 21 + public val logger: Logger = LoggerFactory.getLogger("islanders") 20 22 public lateinit var server: MinecraftServer 21 23 private set 22 24 public val scheduler: Scheduler = AbsoluteScheduler()
+1
src/main/kotlin/net/radsteve/islanders/ItemEffects.kt
··· 41 41 player.scoreboard.addScoreHolderToTeam(player.nameForScoreboard, SpecialItem.allPowerfulTeam) 42 42 player.addStatusEffect(StatusEffectInstance(StatusEffects.GLOWING, -1, 0, true, false, false)) 43 43 44 + Islanders.logger.info("{} is holding all special items", player.nameForScoreboard) 44 45 player.server.playerManager.broadcast( 45 46 player.displayName!!.copy() 46 47 .formatted(Formatting.RED, Formatting.BOLD)
+3
src/main/kotlin/net/radsteve/islanders/SpecialItem.kt
··· 128 128 } 129 129 130 130 public fun obtained(player: ServerPlayerEntity) { 131 + Islanders.logger.info("{} obtained {}", player.nameForScoreboard, id) 131 132 player.playSoundToPlayer(SoundEvents.ENTITY_ARROW_HIT_PLAYER, SoundCategory.MASTER, 0.5f, 1f) 132 133 IslandersData.obtained(id, player.uuid.toString()) 133 134 criterion.trigger(player) ··· 145 146 } 146 147 } 147 148 149 + Islanders.logger.info("{} picked up {}", player.nameForScoreboard, id) 148 150 player.server.playerManager.broadcast( 149 151 player.displayName!!.copy() 150 152 .formatted(color) ··· 160 162 } 161 163 162 164 public fun dropped(player: ServerPlayerEntity) { 165 + Islanders.logger.info("{} dropped {}", player.nameForScoreboard, id) 163 166 player.server.playerManager.broadcast( 164 167 player.displayName!!.copy() 165 168 .formatted(color)
+19
src/main/kotlin/net/radsteve/islanders/crown/CrownAnimation.kt
··· 3 3 import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents 4 4 import net.mcbrawls.scheduler.duration.TickDuration.ticks 5 5 import net.minecraft.entity.ItemEntity 6 + import net.minecraft.item.ItemStack 7 + import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket 6 8 import net.minecraft.particle.DustParticleEffect 9 + import net.minecraft.screen.CraftingScreenHandler 7 10 import net.minecraft.server.MinecraftServer 8 11 import net.minecraft.server.network.ServerPlayerEntity 9 12 import net.minecraft.server.world.ServerWorld ··· 14 17 import net.radsteve.islanders.Islanders 15 18 import net.radsteve.islanders.IslandersData 16 19 import net.radsteve.islanders.SpecialItem 20 + import net.radsteve.islanders.mixin.AbstractCraftingScreenHandlerAccessor 17 21 import net.radsteve.islanders.mixin.CraftingScreenHandlerAccessor 18 22 import kotlin.jvm.optionals.getOrNull 19 23 import kotlin.math.cos ··· 55 59 } 56 60 57 61 public fun craftingFinished(player: ServerPlayerEntity) { 62 + IslandersData.obtained("crown", "_${player.uuid}") 63 + 64 + (player.server.playerManager.playerList - player).forEach { player -> 65 + val handler = player.currentScreenHandler as? CraftingScreenHandler ?: return@forEach 66 + val resultInventory = (handler as AbstractCraftingScreenHandlerAccessor).craftingResultInventory 67 + 68 + if (SpecialItem.find(resultInventory.getStack(0)) == null) { 69 + return@forEach 70 + } 71 + 72 + resultInventory.setStack(0, ItemStack.EMPTY) 73 + handler.setReceivedStack(0, ItemStack.EMPTY) 74 + player.networkHandler.sendPacket(ScreenHandlerSlotUpdateS2CPacket(handler.syncId, handler.nextRevision(), 0, ItemStack.EMPTY)) 75 + } 76 + 58 77 Islanders.scheduler.schedule(1.ticks) { 59 78 player.closeHandledScreen() 60 79 }
+1
src/main/resources/islanders.mixins.json
··· 3 3 "package": "net.radsteve.islanders.mixin", 4 4 "compatibilityLevel": "JAVA_21", 5 5 "mixins": [ 6 + "AbstractCraftingScreenHandlerAccessor", 6 7 "AnvilScreenHandlerMixin", 7 8 "BundleHandlerMixin", 8 9 "CrafterBlockMixin",