From afe59292c208151b8a0bac570e0d46e44ae05141 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Thu, 11 Dec 2025 12:13:04 +0800 Subject: [PATCH 01/19] update cloth config version to 20.0.149 to fix crash bug while click LAN settings in multiplayer game --- fabric/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 29facc9..fe56e70 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -9,5 +9,5 @@ parchment_minecraft_version=1.21.10 parchment_version=2025.10.12 fabric_permissions_api_version=0.4.0 -cloth_config_version=19.0.147 +cloth_config_version=20.0.149 modmenu_version=16.0.0-rc.1 From 51f7266fd6478b2b40fa5bcc1da10418ed54fa42 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:32:40 +0800 Subject: [PATCH 02/19] =?UTF-8?q?fix=EF=BC=9ARefactor=20CustomPacketCodecs?= =?UTF-8?q?=20to=20use=20OPTIONAL=5FSTREAM=5FCODEC=20for=20ItemStack=20ser?= =?UTF-8?q?ialization=20for=20fixing=20HotBar=20item=20decode=20fail.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fabric/sync/CustomPacketCodecs.java | 62 +++---------------- 1 file changed, 10 insertions(+), 52 deletions(-) diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java index 141c452..3f92c3e 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java @@ -1,17 +1,10 @@ package com.hpfxd.spectatorplus.fabric.sync; +import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.EncoderException; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NbtOps; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.world.item.ItemStack; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - public final class CustomPacketCodecs { private CustomPacketCodecs() { } @@ -21,11 +14,7 @@ public static ItemStack[] readItems(RegistryFriendlyByteBuf buf) { final ItemStack[] items = new ItemStack[len]; for (int slot = 0; slot < len; slot++) { - if (buf.readBoolean()) { - final ItemStack stack = readItem(buf); - - items[slot] = stack; - } + items[slot] = ItemStack.OPTIONAL_STREAM_CODEC.decode(buf); } return items; @@ -35,54 +24,23 @@ public static void writeItems(RegistryFriendlyByteBuf buf, ItemStack[] items) { buf.writeInt(items.length); for (final ItemStack item : items) { - buf.writeBoolean(item != null); - - if (item != null) { - writeItem(buf, item); - } + ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, item != null ? item : ItemStack.EMPTY); } } public static ItemStack readItem(RegistryFriendlyByteBuf buf) { - final int len = buf.readInt(); - if (len == 0) { - return ItemStack.EMPTY; - } - try { - final byte[] in = new byte[len]; - buf.readBytes(in); - - final CompoundTag tag = NbtIo.readCompressed(new ByteArrayInputStream(in), NbtAccounter.unlimitedHeap()); - - var registryOps = buf.registryAccess().createSerializationContext(NbtOps.INSTANCE); - return ItemStack.CODEC.parse(registryOps, tag).resultOrPartial().orElse(ItemStack.EMPTY); - } catch (IOException e) { - throw new EncoderException(e); + return ItemStack.OPTIONAL_STREAM_CODEC.decode(buf); + } catch (Exception e) { + throw new DecoderException("Failed to read ItemStack", e); } } public static void writeItem(RegistryFriendlyByteBuf buf, ItemStack item) { - if (item.isEmpty()) { - buf.writeInt(0); - return; - } - - final byte[] bytes; try { - final CompoundTag tag = new CompoundTag(); - var registryOps = buf.registryAccess().createSerializationContext(NbtOps.INSTANCE); - ItemStack.CODEC.encode(item, registryOps, tag).getOrThrow(); - - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - NbtIo.writeCompressed(tag, out); - - bytes = out.toByteArray(); - } catch (IOException e) { - throw new EncoderException(e); + ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, item != null ? item : ItemStack.EMPTY); + } catch (Exception e) { + throw new EncoderException("Failed to write ItemStack", e); } - - buf.writeInt(bytes.length); - buf.writeBytes(bytes); } -} +} \ No newline at end of file From 0af75b7712678ff61171a03710a76a50847c232a Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:24:00 +0800 Subject: [PATCH 03/19] feat: Implement effects synchronization for players in SpectatorPlus --- .../client/sync/ClientSyncController.java | 50 +++++++------- .../fabric/sync/ServerSyncController.java | 2 + .../sync/handler/EffectsSyncHandler.java | 68 +++++++++++++++++++ 3 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java index de56cb7..717ca99 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java @@ -42,32 +42,30 @@ public static void init() { private static void handle(ClientboundEffectsSyncPacket packet, ClientPlayNetworking.Context context) { setSyncData(packet.playerId()); - syncData.effects = packet.effects(); // Now List - System.out.println("[SpectatorPlus] Synced effects: " + syncData.effects); - - // var client = Minecraft.getInstance(); - // if (client.player != null) { - // // Remove all current effects from the client player - // List> toRemove = new ArrayList<>(client.player.getActiveEffectsMap().keySet()); - // for (Holder effect : toRemove) { - // System.out.println("[SpectatorPlus] Removing effect: " + BuiltInRegistries.MOB_EFFECT.getKey(effect.value())); - // client.player.removeEffect(effect); - // } - // // Add all synced effects to the client player - // for (SyncedEffect synced : syncData.effects) { - // System.out.println("[SpectatorPlus] Syncing effect: " + synced.effectKey + " duration=" + synced.duration + " amplifier=" + synced.amplifier); - // java.util.Optional> optHolder = BuiltInRegistries.MOB_EFFECT.get(ResourceLocation.tryParse(synced.effectKey)); - // if (optHolder.isPresent()) { - // MobEffect effect = optHolder.get().value(); - // Holder holder = Holder.direct(effect); - // MobEffectInstance instance = new MobEffectInstance(holder, synced.duration, synced.amplifier); - // client.player.forceAddEffect(instance, client.player); - // System.out.println("[SpectatorPlus] Added effect: " + synced.effectKey); - // } else { - // System.out.println("[SpectatorPlus] Effect not found in registry: " + synced.effectKey); - // } - // } - // } + syncData.effects = packet.effects(); + + var client = Minecraft.getInstance(); + if (client.player != null) { + // 移除客户端玩家当前的所有效果 + List> toRemove = new ArrayList<>(client.player.getActiveEffectsMap().keySet()); + for (Holder effect : toRemove) { + client.player.removeEffect(effect); + } + + // 添加所有同步的效果到客户端玩家 + for (SyncedEffect synced : syncData.effects) { + ResourceLocation effectLocation = ResourceLocation.tryParse(synced.effectKey); + if (effectLocation != null) { + java.util.Optional> optHolder = BuiltInRegistries.MOB_EFFECT.get(effectLocation); + if (optHolder.isPresent()) { + MobEffect effect = optHolder.get().value(); + Holder holder = optHolder.get(); + MobEffectInstance instance = new MobEffectInstance(holder, synced.duration, synced.amplifier); + client.player.forceAddEffect(instance, client.player); + } + } + } + } } private static void handle(ClientboundExperienceSyncPacket packet, ClientPlayNetworking.Context context) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/ServerSyncController.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/ServerSyncController.java index a60b466..587735e 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/ServerSyncController.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/ServerSyncController.java @@ -1,5 +1,6 @@ package com.hpfxd.spectatorplus.fabric.sync; +import com.hpfxd.spectatorplus.fabric.sync.handler.EffectsSyncHandler; import com.hpfxd.spectatorplus.fabric.sync.handler.HotbarSyncHandler; import com.hpfxd.spectatorplus.fabric.sync.handler.ScreenSyncHandler; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; @@ -15,6 +16,7 @@ public static void init() { HotbarSyncHandler.init(); ScreenSyncHandler.init(); + EffectsSyncHandler.init(); } public static void sendPacket(ServerPlayer serverPlayer, ClientboundSyncPacket packet) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java new file mode 100644 index 0000000..baddb7b --- /dev/null +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java @@ -0,0 +1,68 @@ +package com.hpfxd.spectatorplus.fabric.sync.handler; + +import com.hpfxd.spectatorplus.fabric.sync.ServerSyncController; +import com.hpfxd.spectatorplus.fabric.sync.SyncedEffect; +import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundEffectsSyncPacket; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; + +import java.util.*; + +public class EffectsSyncHandler { + private static final Map> EFFECTS = new HashMap<>(); + + public static void init() { + ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> EFFECTS.remove(handler.getPlayer().getUUID())); + + ServerLifecycleEvents.SERVER_STOPPING.register(server -> EFFECTS.clear()); + + ServerTickEvents.END_WORLD_TICK.register(EffectsSyncHandler::tick); + } + + private static void tick(ServerLevel level) { + for (final ServerPlayer player : level.players()) { + final List cachedEffects = EFFECTS.computeIfAbsent(player.getUUID(), k -> new ArrayList<>()); + + final List currentEffects = new ArrayList<>(); + for (MobEffectInstance effectInstance : player.getActiveEffects()) { + String effectKey = BuiltInRegistries.MOB_EFFECT.getKey(effectInstance.getEffect().value()).toString(); + currentEffects.add(new SyncedEffect( + effectKey, + effectInstance.getAmplifier(), + effectInstance.getDuration() + )); + } + + if (!effectsEqual(currentEffects, cachedEffects)) { + cachedEffects.clear(); + cachedEffects.addAll(currentEffects); + + ServerSyncController.broadcastPacketToSpectators(player, new ClientboundEffectsSyncPacket(player.getUUID(), new ArrayList<>(currentEffects))); + } + } + } + + private static boolean effectsEqual(List list1, List list2) { + if (list1.size() != list2.size()) { + return false; + } + + final Map map1 = new HashMap<>(); + final Map map2 = new HashMap<>(); + + for (SyncedEffect effect : list1) { + map1.put(effect.effectKey, effect); + } + + for (SyncedEffect effect : list2) { + map2.put(effect.effectKey, effect); + } + + return map1.equals(map2); + } +} \ No newline at end of file From b93d50f468da9c0ed0784fd94b08ae75c7f2b017 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Fri, 12 Dec 2025 20:06:36 +0800 Subject: [PATCH 04/19] fix: effect sync and render --- .../fabric/client/mixin/GuiMixin.java | 11 ++-- .../client/mixin/LivingEntityAccessor.java | 16 +++++ .../client/mixin/LivingEntityMixin.java | 21 +++++-- .../client/sync/ClientSyncController.java | 25 +------- .../fabric/client/util/EffectUtil.java | 60 +++++++++++++++++++ .../spectatorplus.client.mixins.json | 5 +- .../fabric/mixin/LivingEntityMixin.java | 40 +++++++++++++ .../fabric/mixin/ServerPlayerMixin.java | 2 + .../sync/handler/EffectsSyncHandler.java | 51 +++++++++------- .../main/resources/spectatorplus.mixins.json | 1 + 10 files changed, 176 insertions(+), 56 deletions(-) create mode 100644 fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityAccessor.java create mode 100644 fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java create mode 100644 fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/LivingEntityMixin.java diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java index cb37db1..6703c46 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java @@ -178,20 +178,19 @@ public abstract class GuiMixin { int effectBaseY = baseY + slots.length * (itemHeight + spacing) + spacing; // start below armor // Render all active effect icons down the right side below armor - LocalPlayer player = this.minecraft.player; - if (player != null && player.getActiveEffects() != null && !player.getActiveEffects().isEmpty()) { + if (ClientSyncController.syncData.effects != null && !ClientSyncController.syncData.effects.isEmpty()) { int effectIndex = 0; - for (var effectInstance : player.getActiveEffects()) { + for (var effectInstance : ClientSyncController.syncData.effects) { int y = effectBaseY + effectIndex * (itemWidth + spacing); // Draw vanilla effect background guiGraphics.blitSprite(RenderPipelines.GUI_TEXTURED, EFFECT_BACKGROUND_SPRITE, baseX, y, itemWidth, itemHeight); - ResourceLocation effectIcon = Gui.getMobEffectSprite(effectInstance.getEffect()); + ResourceLocation effectIcon = GuiMixin.getEffectIcon(effectInstance.effectKey); guiGraphics.blitSprite(RenderPipelines.GUI_TEXTURED, effectIcon, baseX + 2, y + 2, itemWidth - 4, itemHeight - 4); // Draw effect level as a small white number on the top right of the icon - int level = effectInstance.getAmplifier() + 1; + int level = effectInstance.amplifier + 1; String levelText = String.valueOf(level); int levelTextWidth = this.minecraft.font.width(levelText); int levelTextX = baseX + itemWidth - (int)(levelTextWidth * 0.4F) - 3; // right-align inside top-right corner @@ -202,7 +201,7 @@ public abstract class GuiMixin { guiGraphics.pose().popMatrix(); // Draw duration bar (1px wide) to the left of the effect icon, color changes with percent - int duration = effectInstance.getDuration(); + int duration = effectInstance.duration; int maxDuration = 3600; // 3 minutes, adjust as needed float percent = maxDuration > 0 ? (duration / (float)maxDuration) : 1.0F; int maxBarHeight = itemHeight - 2; diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityAccessor.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityAccessor.java new file mode 100644 index 0000000..e318705 --- /dev/null +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityAccessor.java @@ -0,0 +1,16 @@ +package com.hpfxd.spectatorplus.fabric.client.mixin; + +import net.minecraft.core.Holder; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(LivingEntity.class) +public interface LivingEntityAccessor { + @Accessor("activeEffects") + Map, MobEffectInstance> spectatorplus$getActiveEffects(); +} diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java index 2da419c..a69714c 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java @@ -1,5 +1,6 @@ package com.hpfxd.spectatorplus.fabric.client.mixin; +import com.hpfxd.spectatorplus.fabric.client.util.EffectUtil; import com.hpfxd.spectatorplus.fabric.client.util.SpecUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.player.AbstractClientPlayer; @@ -7,23 +8,20 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.phys.HitResult; - -import java.util.Collection; - -import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.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.util.Map; @Mixin(LivingEntity.class) public abstract class LivingEntityMixin extends Entity { @@ -54,4 +52,15 @@ private boolean isLookingAtBlock() { return ((GameRendererAccessor) Minecraft.getInstance().gameRenderer).invokePick(this, player.blockInteractionRange(), player.entityInteractionRange(), 1F).getType() == HitResult.Type.BLOCK; } + + @Redirect(method = {"hasEffect", "getEffect", "getActiveEffects", "tickEffects"}, + at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/LivingEntity;activeEffects:Ljava/util/Map;")) + private Map, MobEffectInstance> spectatorplus$redirectActiveEffects(LivingEntity instance) { + // 只对玩家且满足条件时才重定向 + if (instance instanceof Player && EffectUtil.shouldUseSpectatorData()) { + return EffectUtil.getActiveEffectsMap(); + } + return ((LivingEntityAccessor) instance).spectatorplus$getActiveEffects(); + } + } diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java index 717ca99..00d78de 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java @@ -1,6 +1,7 @@ package com.hpfxd.spectatorplus.fabric.client.sync; import com.hpfxd.spectatorplus.fabric.client.sync.screen.ScreenSyncController; +import com.hpfxd.spectatorplus.fabric.client.util.EffectUtil; import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundExperienceSyncPacket; import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundFoodSyncPacket; import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundHotbarSyncPacket; @@ -43,29 +44,7 @@ public static void init() { private static void handle(ClientboundEffectsSyncPacket packet, ClientPlayNetworking.Context context) { setSyncData(packet.playerId()); syncData.effects = packet.effects(); - - var client = Minecraft.getInstance(); - if (client.player != null) { - // 移除客户端玩家当前的所有效果 - List> toRemove = new ArrayList<>(client.player.getActiveEffectsMap().keySet()); - for (Holder effect : toRemove) { - client.player.removeEffect(effect); - } - - // 添加所有同步的效果到客户端玩家 - for (SyncedEffect synced : syncData.effects) { - ResourceLocation effectLocation = ResourceLocation.tryParse(synced.effectKey); - if (effectLocation != null) { - java.util.Optional> optHolder = BuiltInRegistries.MOB_EFFECT.get(effectLocation); - if (optHolder.isPresent()) { - MobEffect effect = optHolder.get().value(); - Holder holder = optHolder.get(); - MobEffectInstance instance = new MobEffectInstance(holder, synced.duration, synced.amplifier); - client.player.forceAddEffect(instance, client.player); - } - } - } - } + EffectUtil.updateEffectInstances(packet.effects()); } private static void handle(ClientboundExperienceSyncPacket packet, ClientPlayNetworking.Context context) { diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java new file mode 100644 index 0000000..dbc5796 --- /dev/null +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java @@ -0,0 +1,60 @@ +package com.hpfxd.spectatorplus.fabric.client.util; + + +import com.hpfxd.spectatorplus.fabric.client.sync.ClientSyncController; +import com.hpfxd.spectatorplus.fabric.sync.SyncedEffect; +import net.minecraft.client.Minecraft; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; + +import java.util.*; + +public class EffectUtil { + private static final Map, MobEffectInstance> activeEffects = new HashMap<>(); + + public static void updateEffectInstances(List effects) { + // 收集新的效果 + Set> newEffects = new HashSet<>(); + + for (SyncedEffect syncedEffect : effects) { + Holder effect = BuiltInRegistries.MOB_EFFECT.get(ResourceLocation.parse(syncedEffect.effectKey)) + .orElseThrow(() -> new IllegalArgumentException("Unknown effect: " + syncedEffect.effectKey)); + newEffects.add(effect); + } + + // 移除不再存在的效果 + activeEffects.entrySet().removeIf(entry -> !newEffects.contains(entry.getKey())); + + // 添加新效果(保持现有实例的BlendState) + for (SyncedEffect syncedEffect : effects) { + Holder effect = BuiltInRegistries.MOB_EFFECT.get(ResourceLocation.parse(syncedEffect.effectKey)) + .orElseThrow(() -> new IllegalArgumentException("Unknown effect: " + syncedEffect.effectKey)); + + if (!activeEffects.containsKey(effect)) { + MobEffectInstance instance = new MobEffectInstance(effect, syncedEffect.duration, + syncedEffect.amplifier, false, true, true); + activeEffects.put(effect, instance); + } + } + } + + public static boolean hasValidSyncData() { + return ClientSyncController.syncData != null && + ClientSyncController.syncData.effects != null; + } + + public static boolean shouldUseSpectatorData() { + Minecraft mc = Minecraft.getInstance(); + return mc.player != null && + SpecUtil.getCameraPlayer(mc) != null && + hasValidSyncData(); + } + + // 直接返回原版格式的activeEffects + public static Map, MobEffectInstance> getActiveEffectsMap() { + return activeEffects; + } +} \ No newline at end of file diff --git a/fabric/src/client/resources/spectatorplus.client.mixins.json b/fabric/src/client/resources/spectatorplus.client.mixins.json index 59c3082..f39dd46 100644 --- a/fabric/src/client/resources/spectatorplus.client.mixins.json +++ b/fabric/src/client/resources/spectatorplus.client.mixins.json @@ -34,5 +34,8 @@ ], "injectors": { "defaultRequire": 1 - } + }, + "mixins": [ + "LivingEntityAccessor" + ] } diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/LivingEntityMixin.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/LivingEntityMixin.java new file mode 100644 index 0000000..4a263c5 --- /dev/null +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/LivingEntityMixin.java @@ -0,0 +1,40 @@ +package com.hpfxd.spectatorplus.fabric.mixin; + +import com.hpfxd.spectatorplus.fabric.sync.handler.EffectsSyncHandler; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Collection; + +@Mixin(LivingEntity.class) +public class LivingEntityMixin { + + @Inject(method = "onEffectAdded", at = @At("TAIL")) + private void onEffectAdded(MobEffectInstance effectInstance, @Nullable Entity entity, CallbackInfo ci) { + if ((Object) this instanceof ServerPlayer player) { + EffectsSyncHandler.onEffectChanged(player); + } + } + + @Inject(method = "onEffectUpdated", at = @At("TAIL")) + private void onEffectUpdated(MobEffectInstance effectInstance, boolean forced, @Nullable Entity entity, CallbackInfo ci) { + if ((Object) this instanceof ServerPlayer player) { + EffectsSyncHandler.onEffectChanged(player); + } + } + + @Inject(method = "onEffectsRemoved", at = @At("TAIL")) + private void onEffectsRemoved(Collection effects, CallbackInfo ci) { + if ((Object) this instanceof ServerPlayer player) { + EffectsSyncHandler.onEffectChanged(player); + } + } +} \ No newline at end of file diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/ServerPlayerMixin.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/ServerPlayerMixin.java index d03126d..15c7b1f 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/ServerPlayerMixin.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/mixin/ServerPlayerMixin.java @@ -3,6 +3,7 @@ import com.google.common.collect.Lists; import com.hpfxd.spectatorplus.fabric.SpectatorMod; import com.hpfxd.spectatorplus.fabric.sync.ServerSyncController; +import com.hpfxd.spectatorplus.fabric.sync.handler.EffectsSyncHandler; import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundExperienceSyncPacket; import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundFoodSyncPacket; import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundHotbarSyncPacket; @@ -66,6 +67,7 @@ public ServerPlayerMixin(Level level, GameProfile gameProfile) { ServerSyncController.sendPacket(spectator, ClientboundFoodSyncPacket.initializing(target)); ServerSyncController.sendPacket(spectator, ClientboundHotbarSyncPacket.initializing(target)); ServerSyncController.sendPacket(spectator, ClientboundSelectedSlotSyncPacket.initializing(target)); + EffectsSyncHandler.onStartSpectating(spectator, target); // Send initial map data patch packet if the target has a map in inventory for (final ItemStack stack : target.getInventory().getNonEquipmentItems()) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java index baddb7b..ac878d4 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/handler/EffectsSyncHandler.java @@ -4,10 +4,9 @@ import com.hpfxd.spectatorplus.fabric.sync.SyncedEffect; import com.hpfxd.spectatorplus.fabric.sync.packet.ClientboundEffectsSyncPacket; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.effect.MobEffectInstance; @@ -18,31 +17,43 @@ public class EffectsSyncHandler { public static void init() { ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> EFFECTS.remove(handler.getPlayer().getUUID())); - ServerLifecycleEvents.SERVER_STOPPING.register(server -> EFFECTS.clear()); + } - ServerTickEvents.END_WORLD_TICK.register(EffectsSyncHandler::tick); + // 当玩家开始旁观另一个玩家时调用 + public static void onStartSpectating(ServerPlayer spectator, ServerPlayer target) { + syncPlayerEffects(spectator, target); } - private static void tick(ServerLevel level) { - for (final ServerPlayer player : level.players()) { - final List cachedEffects = EFFECTS.computeIfAbsent(player.getUUID(), k -> new ArrayList<>()); + // 当玩家的药水效果改变时调用 + public static void onEffectChanged(ServerPlayer player) { + // 获取所有正在旁观此玩家的观察者 + for (ServerPlayer spectator : ServerSyncController.getSpectators(player)) { + syncPlayerEffects(spectator, player); + } + } - final List currentEffects = new ArrayList<>(); - for (MobEffectInstance effectInstance : player.getActiveEffects()) { - String effectKey = BuiltInRegistries.MOB_EFFECT.getKey(effectInstance.getEffect().value()).toString(); - currentEffects.add(new SyncedEffect( - effectKey, - effectInstance.getAmplifier(), - effectInstance.getDuration() - )); - } + private static void syncPlayerEffects(ServerPlayer spectator, ServerPlayer target) { + final List currentEffects = new ArrayList<>(); + for (MobEffectInstance effectInstance : target.getActiveEffects()) { + String effectKey = BuiltInRegistries.MOB_EFFECT.getKey(effectInstance.getEffect().value()).toString(); + currentEffects.add(new SyncedEffect( + effectKey, + effectInstance.getAmplifier(), + effectInstance.getDuration() + )); + } + + final List cachedEffects = EFFECTS.computeIfAbsent(spectator.getUUID(), k -> new ArrayList<>()); - if (!effectsEqual(currentEffects, cachedEffects)) { - cachedEffects.clear(); - cachedEffects.addAll(currentEffects); + if (!effectsEqual(currentEffects, cachedEffects)) { + cachedEffects.clear(); + cachedEffects.addAll(currentEffects); - ServerSyncController.broadcastPacketToSpectators(player, new ClientboundEffectsSyncPacket(player.getUUID(), new ArrayList<>(currentEffects))); + // 使用ServerPlayNetworking发送包 + ClientboundEffectsSyncPacket packet = new ClientboundEffectsSyncPacket(target.getUUID(), new ArrayList<>(currentEffects)); + if (packet.canSend(spectator)) { + ServerPlayNetworking.send(spectator, packet); } } } diff --git a/fabric/src/main/resources/spectatorplus.mixins.json b/fabric/src/main/resources/spectatorplus.mixins.json index 3499f2b..780cf8a 100644 --- a/fabric/src/main/resources/spectatorplus.mixins.json +++ b/fabric/src/main/resources/spectatorplus.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_21", "mixins": [ "ChunkMapAccessor", + "LivingEntityMixin", "ServerGamePacketListenerImplMixin", "ServerPlayerMixin", "TrackedEntityMixin" From 3751651ced59692ace2bc811e586dadbb03c1bba Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sun, 14 Dec 2025 02:39:32 +0800 Subject: [PATCH 05/19] fix: hand height calculations bug --- .../fabric/client/mixin/ItemInHandRendererMixin.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererMixin.java index 04e4c23..96e0817 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererMixin.java @@ -55,8 +55,10 @@ public abstract class ItemInHandRendererMixin { if (this.spectated == spectated) { float f = spectated.getAttackStrengthScale(1.0F); - this.mainHandHeight += Mth.clamp((this.mainHandItem == mainHandItem ? f * f * f : 0.0F) - this.mainHandHeight, -0.4F, 0.4F); - this.offHandHeight += Mth.clamp((float) (this.offHandItem == offHandItem ? 1 : 0) - this.offHandHeight, -0.4F, 0.4F); + float g = this.mainHandItem != mainHandItem ? 0.0F : f * f * f; + float h = this.offHandItem != offHandItem ? 0.0F : 1.0F; + this.mainHandHeight = this.mainHandHeight + Mth.clamp(g - this.mainHandHeight, -0.4F, 0.4F); + this.offHandHeight = this.offHandHeight + Mth.clamp(h - this.offHandHeight, -0.4F, 0.4F); if (this.mainHandHeight < 0.1F) { this.mainHandItem = mainHandItem; From d243f9c6d14bdfb0d8aef499bca59166d58f2a02 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:01:54 +0800 Subject: [PATCH 06/19] fix: handle null viewingEntity in sync data reset, which case a client crash --- .../spectatorplus/fabric/client/mixin/MinecraftMixin.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java index 6c1d158..1e084e9 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java @@ -23,8 +23,10 @@ public abstract class MinecraftMixin { @Inject(method = "setCameraEntity(Lnet/minecraft/world/entity/Entity;)V", at = @At(value = "TAIL")) private void spectatorplus$resetSyncDataOnCameraSwitch(Entity viewingEntity, CallbackInfo ci) { - if (ClientSyncController.syncData != null && !ClientSyncController.syncData.playerId.equals(viewingEntity.getUUID())) { - ClientSyncController.setSyncData(null); + if (ClientSyncController.syncData != null) { + if (viewingEntity == null || !ClientSyncController.syncData.playerId.equals(viewingEntity.getUUID())) { + ClientSyncController.setSyncData(null); + } } } From 532e1fcb34297ae57a3fca77f68885dbe9f6946d Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sun, 14 Dec 2025 23:53:10 +0800 Subject: [PATCH 07/19] fix: handle null ItemStack serialization in CustomPacketCodecs,which case handler cant recognize null item --- .../fabric/sync/CustomPacketCodecs.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java index 3f92c3e..a1a7dfa 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/CustomPacketCodecs.java @@ -4,6 +4,7 @@ import io.netty.handler.codec.EncoderException; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; public final class CustomPacketCodecs { private CustomPacketCodecs() { @@ -14,7 +15,7 @@ public static ItemStack[] readItems(RegistryFriendlyByteBuf buf) { final ItemStack[] items = new ItemStack[len]; for (int slot = 0; slot < len; slot++) { - items[slot] = ItemStack.OPTIONAL_STREAM_CODEC.decode(buf); + items[slot] = buf.readBoolean() ? ItemStack.OPTIONAL_STREAM_CODEC.decode(buf) : null; } return items; @@ -24,7 +25,10 @@ public static void writeItems(RegistryFriendlyByteBuf buf, ItemStack[] items) { buf.writeInt(items.length); for (final ItemStack item : items) { - ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, item != null ? item : ItemStack.EMPTY); + buf.writeBoolean(item != null); + if (item != null) { + ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, item); + } } } @@ -36,9 +40,10 @@ public static ItemStack readItem(RegistryFriendlyByteBuf buf) { } } - public static void writeItem(RegistryFriendlyByteBuf buf, ItemStack item) { + + public static void writeItem(RegistryFriendlyByteBuf buf, @NotNull ItemStack item) { try { - ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, item != null ? item : ItemStack.EMPTY); + ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, item); } catch (Exception e) { throw new EncoderException("Failed to write ItemStack", e); } From 6db7fdf33c7c6e3d4a2f1d8525d1c15990b4d627 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:14:33 +0800 Subject: [PATCH 08/19] fix: prevent incorrect arm orientation when spectating by skipping mulPose in renderItemInHand --- .../fabric/client/mixin/GameRendererMixin.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java index de281b2..a6f1873 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java @@ -14,10 +14,14 @@ import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.animal.CowVariant; +import net.minecraft.world.entity.player.PlayerModelType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; +import org.joml.Matrix4fc; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -45,6 +49,18 @@ public abstract class GameRendererMixin { @Unique private float xBobO; @Unique private float yBobO; + @Redirect(method = "renderItemInHand", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Matrix4fc;)V", ordinal = 0)) + private void redirectMulPose(PoseStack poseStack, Matrix4fc matrix) { + // In spectator mode, the projection matrix contains rotation data from the spectated player, + // while arm rendering calculations are based on the localPlayer's viewpoint. + // We must skip this mulPose operation when spectating to avoid rendering arms with incorrect orientation. + if (this.minecraft.player == null + || this.minecraft.player.gameMode() != GameType.SPECTATOR + || !this.minecraft.options.getCameraType().isFirstPerson()) { + poseStack.mulPose(matrix); + } + } + @Inject(method = "renderItemInHand", at = @At(value = "INVOKE", target = "Lorg/joml/Matrix4fStack;popMatrix()Lorg/joml/Matrix4fStack;", remap = false)) public void spectatorplus$renderItemInHand(float partialTicks, boolean sleeping, Matrix4f projectionMatrix, CallbackInfo ci, @Local PoseStack poseStackIn) { if (SpectatorClientMod.config.renderArms && this.minecraft.player != null && this.minecraft.options.getCameraType().isFirstPerson() && !this.minecraft.options.hideGui) { From 3116679e4b556da257710729039ce092cbb9ec0c Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Fri, 2 Jan 2026 20:23:37 +0800 Subject: [PATCH 09/19] fix: update fabric-loom plugin version to 1.13-SNAPSHOT --- fabric/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index e1e4187..dcd77b6 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("fabric-loom") version "1.11.7" + id("fabric-loom") version "1.13-SNAPSHOT" id("spectatorplus.platform") } From 8ae3bc68da07cf8a28d666052adfea2cd6509e45 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sat, 3 Jan 2026 15:51:16 +0800 Subject: [PATCH 10/19] update to 1.21.11 --- fabric/build.gradle.kts | 2 +- fabric/gradle.properties | 18 +++++++++--------- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index dcd77b6..ecfe9db 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("fabric-loom") version "1.13-SNAPSHOT" + id("fabric-loom") version "1.14-SNAPSHOT" id("spectatorplus.platform") } diff --git a/fabric/gradle.properties b/fabric/gradle.properties index fe56e70..b932122 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -1,13 +1,13 @@ -minecraft_version=1.21.10 -yarn_mappings=1.21.10+build.1 -loader_version=0.17.2 +minecraft_version=1.21.11 +yarn_mappings=1.21.11+build.3 +loader_version=0.18.4 # Fabric API -fabric_version=0.138.3+1.21.10 +fabric_version=0.140.2+1.21.11 -parchment_minecraft_version=1.21.10 -parchment_version=2025.10.12 +parchment_minecraft_version=1.21.11 +parchment_version=2025.12.20 -fabric_permissions_api_version=0.4.0 -cloth_config_version=20.0.149 -modmenu_version=16.0.0-rc.1 +fabric_permissions_api_version=0.6.1 +cloth_config_version=21.11.153 +modmenu_version=17.0.0-beta.1 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca025c8..23449a2 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.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index af71930..ad01527 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,8 +13,8 @@ plugins { rootProject.name = "spectatorplus" includeBuild("build-logic") -this.setupSubproject("paper") -this.setupSubproject("fabric") +setupSubproject("paper") +setupSubproject("fabric") fun setupSubproject(moduleName: String) { val name = "spectatorplus-$moduleName" From ff06b36a95c945307db7403cf0da48a7764528e5 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sun, 4 Jan 2026 12:44:31 +0800 Subject: [PATCH 11/19] fix: replace ResourceLocation with Identifier for packet type definitions --- .../fabric/sync/packet/ClientboundEffectsSyncPacket.java | 4 ++-- .../fabric/sync/packet/ClientboundExperienceSyncPacket.java | 5 +++-- .../fabric/sync/packet/ClientboundFoodSyncPacket.java | 4 ++-- .../fabric/sync/packet/ClientboundHotbarSyncPacket.java | 4 ++-- .../fabric/sync/packet/ClientboundInventorySyncPacket.java | 5 +++-- .../sync/packet/ClientboundScreenCursorSyncPacket.java | 4 ++-- .../fabric/sync/packet/ClientboundScreenSyncPacket.java | 5 +++-- .../sync/packet/ClientboundSelectedSlotSyncPacket.java | 5 +++-- .../sync/packet/ServerboundOpenedInventorySyncPacket.java | 5 +++-- .../sync/packet/ServerboundRequestInventoryOpenPacket.java | 5 +++-- 10 files changed, 26 insertions(+), 20 deletions(-) diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java index cc9e68c..3468b4e 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java @@ -6,7 +6,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -19,7 +19,7 @@ public record ClientboundEffectsSyncPacket( List effects ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundEffectsSyncPacket::write, ClientboundEffectsSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:effects_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","effects_sync")); private static final String PERMISSION = "spectatorplus.sync.effects"; public ClientboundEffectsSyncPacket(FriendlyByteBuf buf) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java index f866588..ff1205a 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java @@ -5,7 +5,8 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; + +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -18,7 +19,7 @@ public record ClientboundExperienceSyncPacket( int level ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundExperienceSyncPacket::write, ClientboundExperienceSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:experience_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","experience_sync")); private static final String PERMISSION = "spectatorplus.sync.experience"; public static ClientboundExperienceSyncPacket initializing(ServerPlayer target) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java index ec16535..4fd363d 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java @@ -5,7 +5,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -17,7 +17,7 @@ public record ClientboundFoodSyncPacket( float saturation ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundFoodSyncPacket::write, ClientboundFoodSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:food_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","food_sync")); private static final String PERMISSION = "spectatorplus.sync.food"; public static ClientboundFoodSyncPacket initializing(ServerPlayer target) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java index 533410f..4483d52 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java @@ -6,7 +6,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; @@ -18,7 +18,7 @@ public record ClientboundHotbarSyncPacket( ItemStack[] items ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundHotbarSyncPacket::write, ClientboundHotbarSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:hotbar_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","hotbar_sync")); static final String PERMISSION = "spectatorplus.sync.hotbar"; public static ClientboundHotbarSyncPacket initializing(ServerPlayer target) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java index 9741792..7437cbe 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java @@ -6,7 +6,8 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; + +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; @@ -23,7 +24,7 @@ public record ClientboundInventorySyncPacket( ItemStack[] items ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundInventorySyncPacket::write, ClientboundInventorySyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:inventory_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","inventory_sync")); public static final int ITEMS_LENGTH = 4 * 9 + 4 + 1; // 36 main + 4 armor + 1 offhand = 41 private static final String PERMISSION = "spectatorplus.sync.inventory"; diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java index 433b518..c0d907a 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java @@ -5,7 +5,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; @@ -18,7 +18,7 @@ public record ClientboundScreenCursorSyncPacket( int originSlot ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundScreenCursorSyncPacket::write, ClientboundScreenCursorSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:screen_cursor_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","screen_cursor_sync")); public ClientboundScreenCursorSyncPacket(RegistryFriendlyByteBuf buf) { this(buf.readUUID(), CustomPacketCodecs.readItem(buf), buf.readByte()); diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java index ba92dde..0d17635 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java @@ -4,7 +4,8 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; + +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -15,7 +16,7 @@ public record ClientboundScreenSyncPacket( int flags ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundScreenSyncPacket::write, ClientboundScreenSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:screen_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","screen_sync")); public ClientboundScreenSyncPacket(FriendlyByteBuf buf) { this(buf.readUUID(), buf.readByte()); diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java index 756a9fa..623b992 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java @@ -5,7 +5,8 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; + +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -18,7 +19,7 @@ public record ClientboundSelectedSlotSyncPacket( int selectedSlot ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundSelectedSlotSyncPacket::write, ClientboundSelectedSlotSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:selected_slot_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","selected_slot_sync")); public static ClientboundSelectedSlotSyncPacket initializing(ServerPlayer target) { return new ClientboundSelectedSlotSyncPacket(target.getUUID(), target.getInventory().getSelectedSlot()); diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java index e00b7d6..6d3365c 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java @@ -4,11 +4,12 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; + public final class ServerboundOpenedInventorySyncPacket implements ServerboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ServerboundOpenedInventorySyncPacket::write, ServerboundOpenedInventorySyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:opened_inventory_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","opened_inventory_sync")); public ServerboundOpenedInventorySyncPacket() { } diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java index f9f5c7e..10d7f4f 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java @@ -4,7 +4,8 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; + +import net.minecraft.resources.Identifier; import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -13,7 +14,7 @@ public record ServerboundRequestInventoryOpenPacket( UUID playerId ) implements ServerboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ServerboundRequestInventoryOpenPacket::write, ServerboundRequestInventoryOpenPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ResourceLocation.parse("spectatorplus:request_inventory_open")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","request_inventory_open")); public ServerboundRequestInventoryOpenPacket(FriendlyByteBuf buf) { this(buf.readUUID()); From a37be0f37d4e49ad9a4c1631211b4aec79fefbdd Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sat, 4 Jan 2025 13:23:00 +0800 Subject: [PATCH 12/19] beautiful code style --- .../spectatorplus/fabric/client/mixin/MinecraftMixin.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java index 1e084e9..e0cdcc2 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/MinecraftMixin.java @@ -23,10 +23,8 @@ public abstract class MinecraftMixin { @Inject(method = "setCameraEntity(Lnet/minecraft/world/entity/Entity;)V", at = @At(value = "TAIL")) private void spectatorplus$resetSyncDataOnCameraSwitch(Entity viewingEntity, CallbackInfo ci) { - if (ClientSyncController.syncData != null) { - if (viewingEntity == null || !ClientSyncController.syncData.playerId.equals(viewingEntity.getUUID())) { - ClientSyncController.setSyncData(null); - } + if (ClientSyncController.syncData != null && (viewingEntity == null || !ClientSyncController.syncData.playerId.equals(viewingEntity.getUUID()))) { + ClientSyncController.setSyncData(null); } } From ccc204db86cfcc3bbfa7cfc7ce02e441d793cfa7 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sat, 4 Jan 2025 13:35:00 +0800 Subject: [PATCH 13/19] Migrate to 1.21.11 # Conflicts: # fabric/gradle.properties # fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java # fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java # fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java # fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java # paper/gradle.properties # settings.gradle.kts --- fabric/build.gradle.kts | 1 - .../fabric/client/SpectatorKeybinds.java | 2 +- .../fabric/client/mixin/GuiMixin.java | 26 +++++++++---------- .../screen/AbstractContainerScreenMixin.java | 6 ++--- .../client/sync/ClientSyncController.java | 2 +- .../fabric/client/util/SpecUtil.java | 26 +++++++++---------- .../packet/ClientboundEffectsSyncPacket.java | 2 +- .../ClientboundExperienceSyncPacket.java | 3 +-- .../packet/ClientboundFoodSyncPacket.java | 2 +- .../packet/ClientboundHotbarSyncPacket.java | 2 +- .../ClientboundInventorySyncPacket.java | 3 +-- .../ClientboundScreenCursorSyncPacket.java | 2 +- .../packet/ClientboundScreenSyncPacket.java | 3 +-- .../ClientboundSelectedSlotSyncPacket.java | 3 +-- .../ServerboundOpenedInventorySyncPacket.java | 3 +-- ...ServerboundRequestInventoryOpenPacket.java | 3 +-- fabric/src/main/resources/fabric.mod.json | 2 +- paper/build.gradle.kts | 2 +- paper/gradle.properties | 2 +- 19 files changed, 44 insertions(+), 51 deletions(-) diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index ecfe9db..0bfcc35 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -32,7 +32,6 @@ dependencies { officialMojangMappings() parchment("org.parchmentmc.data:parchment-${property("parchment_minecraft_version")}:${property("parchment_version")}@zip") }) - modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_version")}") include(modImplementation("me.lucko:fabric-permissions-api:${property("fabric_permissions_api_version")}")!!) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/SpectatorKeybinds.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/SpectatorKeybinds.java index fc3c452..bccc9d7 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/SpectatorKeybinds.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/SpectatorKeybinds.java @@ -7,7 +7,7 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.ChatFormatting; -import net.minecraft.Util; +import net.minecraft.util.Util; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.spectator.SpectatorGui; diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java index 6703c46..07f0272 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GuiMixin.java @@ -40,7 +40,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.modify.LocalVariableDiscriminator.Context.Local; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.core.Holder; @Mixin(Gui.class) @@ -54,15 +54,15 @@ public abstract class GuiMixin { @Shadow @Final private SpectatorGui spectatorGui; - // Use correct ResourceLocations for vanilla empty armor slot icons from the GUI atlas - private static final ResourceLocation EMPTY_ARMOR_SLOT_HELMET = ResourceLocation.withDefaultNamespace("container/slot/helmet"); - private static final ResourceLocation EMPTY_ARMOR_SLOT_CHESTPLATE = ResourceLocation.withDefaultNamespace("container/slot/chestplate"); - private static final ResourceLocation EMPTY_ARMOR_SLOT_LEGGINGS = ResourceLocation.withDefaultNamespace("container/slot/leggings"); - private static final ResourceLocation EMPTY_ARMOR_SLOT_BOOTS = ResourceLocation.withDefaultNamespace("container/slot/boots"); - private static final ResourceLocation EFFECT_BACKGROUND_AMBIENT_SPRITE = ResourceLocation.withDefaultNamespace("hud/effect_background_ambient"); - private static final ResourceLocation EFFECT_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace("hud/effect_background"); + // Use correct Identifiers for vanilla empty armor slot icons from the GUI atlas + private static final Identifier EMPTY_ARMOR_SLOT_HELMET = Identifier.withDefaultNamespace("container/slot/helmet"); + private static final Identifier EMPTY_ARMOR_SLOT_CHESTPLATE = Identifier.withDefaultNamespace("container/slot/chestplate"); + private static final Identifier EMPTY_ARMOR_SLOT_LEGGINGS = Identifier.withDefaultNamespace("container/slot/leggings"); + private static final Identifier EMPTY_ARMOR_SLOT_BOOTS = Identifier.withDefaultNamespace("container/slot/boots"); + private static final Identifier EFFECT_BACKGROUND_AMBIENT_SPRITE = Identifier.withDefaultNamespace("hud/effect_background_ambient"); + private static final Identifier EFFECT_BACKGROUND_SPRITE = Identifier.withDefaultNamespace("hud/effect_background"); - private static final ResourceLocation[] TEXTURE_EMPTY_SLOTS = new ResourceLocation[]{ + private static final Identifier[] TEXTURE_EMPTY_SLOTS = new Identifier[]{ EMPTY_ARMOR_SLOT_BOOTS, EMPTY_ARMOR_SLOT_LEGGINGS, EMPTY_ARMOR_SLOT_CHESTPLATE, EMPTY_ARMOR_SLOT_HELMET }; @@ -186,7 +186,7 @@ public abstract class GuiMixin { // Draw vanilla effect background guiGraphics.blitSprite(RenderPipelines.GUI_TEXTURED, EFFECT_BACKGROUND_SPRITE, baseX, y, itemWidth, itemHeight); - ResourceLocation effectIcon = GuiMixin.getEffectIcon(effectInstance.effectKey); + Identifier effectIcon = GuiMixin.getEffectIcon(effectInstance.effectKey); guiGraphics.blitSprite(RenderPipelines.GUI_TEXTURED, effectIcon, baseX + 2, y + 2, itemWidth - 4, itemHeight - 4); // Draw effect level as a small white number on the top right of the icon @@ -364,8 +364,8 @@ public abstract class GuiMixin { } return instance; } - // Map EffectType to vanilla effect icon ResourceLocation - private static ResourceLocation getEffectIcon(String effectKey) { + // Map EffectType to vanilla effect icon Identifier + private static Identifier getEffectIcon(String effectKey) { // If effectKey contains a namespace (e.g., minecraft:nausea), strip it String key = effectKey; int colonIdx = key.indexOf(":"); @@ -374,7 +374,7 @@ private static ResourceLocation getEffectIcon(String effectKey) { } // Vanilla effect icons are in the GUI atlas as effect/ // The effectKey should be lowercase, matching the registry name - return ResourceLocation.withDefaultNamespace("mob_effect/" + key.toLowerCase()); + return Identifier.withDefaultNamespace("mob_effect/" + key.toLowerCase()); } } diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/screen/AbstractContainerScreenMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/screen/AbstractContainerScreenMixin.java index 72700ec..248ceb6 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/screen/AbstractContainerScreenMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/screen/AbstractContainerScreenMixin.java @@ -8,7 +8,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ClickType; @@ -46,8 +46,8 @@ public abstract class AbstractContainerScreenMixin { @Unique private boolean mouseMoved; - @Shadow @Final private static ResourceLocation SLOT_HIGHLIGHT_BACK_SPRITE; - @Shadow @Final private static ResourceLocation SLOT_HIGHLIGHT_FRONT_SPRITE; + @Shadow @Final private static Identifier SLOT_HIGHLIGHT_BACK_SPRITE; + @Shadow @Final private static Identifier SLOT_HIGHLIGHT_FRONT_SPRITE; @Shadow protected abstract void renderFloatingItem(GuiGraphics guiGraphics, ItemStack stack, int x, int y, String text); @Shadow @Final protected AbstractContainerMenu menu; @Shadow @Nullable protected abstract Slot getHoveredSlot(double mouseX, double mouseY); diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java index 00d78de..468e659 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/sync/ClientSyncController.java @@ -12,7 +12,7 @@ import com.hpfxd.spectatorplus.fabric.sync.SyncedEffect; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.Holder; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents; diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/SpecUtil.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/SpecUtil.java index 3c7fbfc..4192260 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/SpecUtil.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/SpecUtil.java @@ -21,17 +21,17 @@ public static AbstractClientPlayer getCameraPlayer(Minecraft minecraft) { return null; } - /** - * Returns the synced MobEffectInstance for the given entity and effect, or null if not found. - */ - public static net.minecraft.world.effect.MobEffectInstance getSyncedEffect(LivingEntity entity, Holder effect) { - if (ClientSyncController.syncData == null || ClientSyncController.syncData.effects == null) return null; - String queryKey = effect.unwrapKey().get().location().toString(); - for (com.hpfxd.spectatorplus.fabric.sync.SyncedEffect synced : ClientSyncController.syncData.effects) { - if (queryKey.equals(synced.effectKey)) { - return new net.minecraft.world.effect.MobEffectInstance(effect, synced.duration, synced.amplifier); - } - } - return null; - } + // /** + // * Returns the synced MobEffectInstance for the given entity and effect, or null if not found. + // */ + // public static net.minecraft.world.effect.MobEffectInstance getSyncedEffect(LivingEntity entity, Holder effect) { + // if (ClientSyncController.syncData == null || ClientSyncController.syncData.effects == null) return null; + // String queryKey = effect.unwrapKey().get().identifier().toString(); + // for (com.hpfxd.spectatorplus.fabric.sync.SyncedEffect synced : ClientSyncController.syncData.effects) { + // if (queryKey.equals(synced.effectKey)) { + // return new net.minecraft.world.effect.MobEffectInstance(effect, synced.duration, synced.amplifier); + // } + // } + // return null; + // } } diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java index 3468b4e..b8d820f 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundEffectsSyncPacket.java @@ -19,7 +19,7 @@ public record ClientboundEffectsSyncPacket( List effects ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundEffectsSyncPacket::write, ClientboundEffectsSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","effects_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:effects_sync")); private static final String PERMISSION = "spectatorplus.sync.effects"; public ClientboundEffectsSyncPacket(FriendlyByteBuf buf) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java index ff1205a..2e513fd 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundExperienceSyncPacket.java @@ -5,7 +5,6 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -19,7 +18,7 @@ public record ClientboundExperienceSyncPacket( int level ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundExperienceSyncPacket::write, ClientboundExperienceSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","experience_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:experience_sync")); private static final String PERMISSION = "spectatorplus.sync.experience"; public static ClientboundExperienceSyncPacket initializing(ServerPlayer target) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java index 4fd363d..f6e8327 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundFoodSyncPacket.java @@ -17,7 +17,7 @@ public record ClientboundFoodSyncPacket( float saturation ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundFoodSyncPacket::write, ClientboundFoodSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","food_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:food_sync")); private static final String PERMISSION = "spectatorplus.sync.food"; public static ClientboundFoodSyncPacket initializing(ServerPlayer target) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java index 4483d52..e39b7d0 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundHotbarSyncPacket.java @@ -18,7 +18,7 @@ public record ClientboundHotbarSyncPacket( ItemStack[] items ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundHotbarSyncPacket::write, ClientboundHotbarSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","hotbar_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:hotbar_sync")); static final String PERMISSION = "spectatorplus.sync.hotbar"; public static ClientboundHotbarSyncPacket initializing(ServerPlayer target) { diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java index 7437cbe..88a0aa0 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundInventorySyncPacket.java @@ -6,7 +6,6 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; @@ -24,7 +23,7 @@ public record ClientboundInventorySyncPacket( ItemStack[] items ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundInventorySyncPacket::write, ClientboundInventorySyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","inventory_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:inventory_sync")); public static final int ITEMS_LENGTH = 4 * 9 + 4 + 1; // 36 main + 4 armor + 1 offhand = 41 private static final String PERMISSION = "spectatorplus.sync.inventory"; diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java index c0d907a..157a972 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenCursorSyncPacket.java @@ -18,7 +18,7 @@ public record ClientboundScreenCursorSyncPacket( int originSlot ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundScreenCursorSyncPacket::write, ClientboundScreenCursorSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","screen_cursor_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:screen_cursor_sync")); public ClientboundScreenCursorSyncPacket(RegistryFriendlyByteBuf buf) { this(buf.readUUID(), CustomPacketCodecs.readItem(buf), buf.readByte()); diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java index 0d17635..cb0464d 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundScreenSyncPacket.java @@ -4,7 +4,6 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -16,7 +15,7 @@ public record ClientboundScreenSyncPacket( int flags ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundScreenSyncPacket::write, ClientboundScreenSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","screen_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:screen_sync")); public ClientboundScreenSyncPacket(FriendlyByteBuf buf) { this(buf.readUUID(), buf.readByte()); diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java index 623b992..2999fa2 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ClientboundSelectedSlotSyncPacket.java @@ -5,7 +5,6 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import org.jetbrains.annotations.NotNull; @@ -19,7 +18,7 @@ public record ClientboundSelectedSlotSyncPacket( int selectedSlot ) implements ClientboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundSelectedSlotSyncPacket::write, ClientboundSelectedSlotSyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","selected_slot_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:selected_slot_sync")); public static ClientboundSelectedSlotSyncPacket initializing(ServerPlayer target) { return new ClientboundSelectedSlotSyncPacket(target.getUUID(), target.getInventory().getSelectedSlot()); diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java index 6d3365c..e6aea33 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundOpenedInventorySyncPacket.java @@ -6,10 +6,9 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.Identifier; - public final class ServerboundOpenedInventorySyncPacket implements ServerboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ServerboundOpenedInventorySyncPacket::write, ServerboundOpenedInventorySyncPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","opened_inventory_sync")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:opened_inventory_sync")); public ServerboundOpenedInventorySyncPacket() { } diff --git a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java index 10d7f4f..562d9d8 100644 --- a/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java +++ b/fabric/src/main/java/com/hpfxd/spectatorplus/fabric/sync/packet/ServerboundRequestInventoryOpenPacket.java @@ -4,7 +4,6 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - import net.minecraft.resources.Identifier; import org.jetbrains.annotations.NotNull; @@ -14,7 +13,7 @@ public record ServerboundRequestInventoryOpenPacket( UUID playerId ) implements ServerboundSyncPacket { public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ServerboundRequestInventoryOpenPacket::write, ServerboundRequestInventoryOpenPacket::new); - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("spectatorplus","request_inventory_open")); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse("spectatorplus:request_inventory_open")); public ServerboundRequestInventoryOpenPacket(FriendlyByteBuf buf) { this(buf.readUUID()); diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 30eadde..502fe31 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -51,7 +51,7 @@ ], "depends": { "fabricloader": ">=0.15.0", - "minecraft": "~1.21.10", + "minecraft": "~1.21.11", "java": ">=21", "fabric-api": "*", "fabric-permissions-api-v0": "*" diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts index d068769..c0ee068 100644 --- a/paper/build.gradle.kts +++ b/paper/build.gradle.kts @@ -47,7 +47,7 @@ tasks { } runServer { - minecraftVersion("1.21.10") + minecraftVersion("1.21.11") } named("build") { diff --git a/paper/gradle.properties b/paper/gradle.properties index 9ada3d7..67ca6fa 100644 --- a/paper/gradle.properties +++ b/paper/gradle.properties @@ -1,2 +1,2 @@ -paper_version=1.21.7-R0.1-SNAPSHOT +paper_version=1.21.11-R0.1-SNAPSHOT reflection_remapper_version=0.1.3 \ No newline at end of file From 058dbbaa67b2bbee8e29208f6bd2a2eba970abc5 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sat, 4 Jan 2025 16:52:00 +0800 Subject: [PATCH 14/19] migrate entityInteractionRange() form gameRendererMixin to new localPlayerMixin Connors Fixes # Conflicts: # fabric/gradle.properties # fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java # paper/build.gradle.kts --- .../client/mixin/GameRendererAccessor.java | 13 -------- .../client/mixin/GameRendererMixin.java | 23 ------------- .../client/mixin/LivingEntityMixin.java | 5 ++- .../client/mixin/LocalPlayerAccessor.java | 15 +++++++++ .../fabric/client/mixin/LocalPlayerMixin.java | 32 +++++++++++++++++++ .../spectatorplus.client.mixins.json | 3 +- 6 files changed, 53 insertions(+), 38 deletions(-) delete mode 100644 fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererAccessor.java create mode 100644 fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerAccessor.java create mode 100644 fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerMixin.java diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererAccessor.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererAccessor.java deleted file mode 100644 index 31562dc..0000000 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererAccessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.hpfxd.spectatorplus.fabric.client.mixin; - -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.phys.HitResult; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(GameRenderer.class) -public interface GameRendererAccessor { - @Invoker - HitResult invokePick(Entity entity, double blockRange, double entityRange, float partialTicks); -} diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java index a6f1873..261783e 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java @@ -14,8 +14,6 @@ import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.animal.CowVariant; -import net.minecraft.world.entity.player.PlayerModelType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.GameType; @@ -177,25 +175,4 @@ private static ItemInHandRenderer.HandRenderSelection evaluateWhichHandsToRender if (minecraft.getCameraEntity() == this.minecraft.player) return instance.getInterpolatedBob(partialTick); return Mth.lerp(partialTick, this.bobO, this.bob); } - - @ModifyExpressionValue(method = "pick(F)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;blockInteractionRange()D")) - private double spectatorplus$modifyBlockInteractionRange(double original) { - final AbstractClientPlayer spectated = SpecUtil.getCameraPlayer(this.minecraft); - if (spectated != null) { - return spectated.blockInteractionRange(); - } - - return original; - } - - @ModifyExpressionValue(method = "pick(F)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;entityInteractionRange()D")) - private double spectatorplus$modifyEntityInteractionRange(double original) { - final AbstractClientPlayer spectated = SpecUtil.getCameraPlayer(this.minecraft); - if (spectated != null) { - return spectated.entityInteractionRange(); - } - - return original; - } - } diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java index a69714c..608520b 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java @@ -50,7 +50,10 @@ private boolean isLookingAtBlock() { return false; } - return ((GameRendererAccessor) Minecraft.getInstance().gameRenderer).invokePick(this, player.blockInteractionRange(), player.entityInteractionRange(), 1F).getType() == HitResult.Type.BLOCK; + var spectated = SpecUtil.getCameraPlayer(Minecraft.getInstance()); + var blockRange = spectated == null ? player.blockInteractionRange() : spectated.blockInteractionRange(); + var entityRange = spectated == null ? player.entityInteractionRange() : spectated.entityInteractionRange(); + return LocalPlayerAccessor.invokePick(this, blockRange, entityRange, 1F).getType() == HitResult.Type.BLOCK; } @Redirect(method = {"hasEffect", "getEffect", "getActiveEffects", "tickEffects"}, diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerAccessor.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerAccessor.java new file mode 100644 index 0000000..3667748 --- /dev/null +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerAccessor.java @@ -0,0 +1,15 @@ +package com.hpfxd.spectatorplus.fabric.client.mixin; + +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.HitResult; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(LocalPlayer.class) +public interface LocalPlayerAccessor { + @Invoker + static HitResult invokePick(Entity cameraEntity, double blockRange, double entityRange, float partialTick) { + throw new AssertionError(); + } +} \ No newline at end of file diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerMixin.java new file mode 100644 index 0000000..c5d58ac --- /dev/null +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LocalPlayerMixin.java @@ -0,0 +1,32 @@ +package com.hpfxd.spectatorplus.fabric.client.mixin; + +import com.hpfxd.spectatorplus.fabric.client.util.SpecUtil; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.player.LocalPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(LocalPlayer.class) +public class LocalPlayerMixin { + @ModifyExpressionValue(method = "raycastHitResult", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;blockInteractionRange()D")) + private double spectatorplus$modifyBlockInteractionRange(double original) { + final AbstractClientPlayer spectated = SpecUtil.getCameraPlayer(Minecraft.getInstance()); + if (spectated != null) { + return spectated.blockInteractionRange(); + } + + return original; + } + + @ModifyExpressionValue(method = "raycastHitResult", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;entityInteractionRange()D")) + private double spectatorplus$modifyEntityInteractionRange(double original) { + final AbstractClientPlayer spectated = SpecUtil.getCameraPlayer(Minecraft.getInstance()); + if (spectated != null) { + return spectated.entityInteractionRange(); + } + + return original; + } +} \ No newline at end of file diff --git a/fabric/src/client/resources/spectatorplus.client.mixins.json b/fabric/src/client/resources/spectatorplus.client.mixins.json index f39dd46..d302c21 100644 --- a/fabric/src/client/resources/spectatorplus.client.mixins.json +++ b/fabric/src/client/resources/spectatorplus.client.mixins.json @@ -9,7 +9,6 @@ "EntityMixin", "EntityRendererMixin", "ExperienceBarRendererMixin", - "GameRendererAccessor", "GameRendererMixin", "GuiMixin", "InventoryAccessor", @@ -18,6 +17,8 @@ "LevelRendererAccessor", "LevelRendererMixin", "LivingEntityMixin", + "LocalPlayerAccessor", + "LocalPlayerMixin", "MinecraftMixin", "PlayerMenuItemAccessor", "PlayerMenuItemMixin", From b326e00cf3f08fc7ac71f4f2c62dfd2e0470d9d0 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:51:46 +0800 Subject: [PATCH 15/19] fix: replace ResourceLocation with Identifier in effect handling --- .../hpfxd/spectatorplus/fabric/client/util/EffectUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java index dbc5796..3119807 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/util/EffectUtil.java @@ -6,7 +6,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; @@ -20,7 +20,7 @@ public static void updateEffectInstances(List effects) { Set> newEffects = new HashSet<>(); for (SyncedEffect syncedEffect : effects) { - Holder effect = BuiltInRegistries.MOB_EFFECT.get(ResourceLocation.parse(syncedEffect.effectKey)) + Holder effect = BuiltInRegistries.MOB_EFFECT.get(Identifier.parse(syncedEffect.effectKey)) .orElseThrow(() -> new IllegalArgumentException("Unknown effect: " + syncedEffect.effectKey)); newEffects.add(effect); } @@ -30,7 +30,7 @@ public static void updateEffectInstances(List effects) { // 添加新效果(保持现有实例的BlendState) for (SyncedEffect syncedEffect : effects) { - Holder effect = BuiltInRegistries.MOB_EFFECT.get(ResourceLocation.parse(syncedEffect.effectKey)) + Holder effect = BuiltInRegistries.MOB_EFFECT.get(Identifier.parse(syncedEffect.effectKey)) .orElseThrow(() -> new IllegalArgumentException("Unknown effect: " + syncedEffect.effectKey)); if (!activeEffects.containsKey(effect)) { From 7506b4b59a872e88ed0fcc5e37d4e38ad7ec0a3d Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:22:20 +0800 Subject: [PATCH 16/19] fix: renderItemInHand follow the update of source code --- .../client/mixin/GameRendererMixin.java | 31 ++++++++++--------- .../mixin/ItemInHandRendererAccessor.java | 4 +++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java index 261783e..5165202 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/GameRendererMixin.java @@ -47,24 +47,24 @@ public abstract class GameRendererMixin { @Unique private float xBobO; @Unique private float yBobO; - @Redirect(method = "renderItemInHand", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Matrix4fc;)V", ordinal = 0)) - private void redirectMulPose(PoseStack poseStack, Matrix4fc matrix) { - // In spectator mode, the projection matrix contains rotation data from the spectated player, - // while arm rendering calculations are based on the localPlayer's viewpoint. - // We must skip this mulPose operation when spectating to avoid rendering arms with incorrect orientation. - if (this.minecraft.player == null - || this.minecraft.player.gameMode() != GameType.SPECTATOR - || !this.minecraft.options.getCameraType().isFirstPerson()) { - poseStack.mulPose(matrix); - } - } +// @Redirect(method = "renderItemInHand", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Matrix4fc;)V", ordinal = 0)) +// private void redirectMulPose(PoseStack poseStack, Matrix4fc matrix) { +// // In spectator mode, the projection matrix contains rotation data from the spectated player, +// // while arm rendering calculations are based on the localPlayer's viewpoint. +// // We must skip this mulPose operation when spectating to avoid rendering arms with incorrect orientation. +// if (this.minecraft.player == null +// || this.minecraft.player.gameMode() != GameType.SPECTATOR +// || !this.minecraft.options.getCameraType().isFirstPerson()) { +// poseStack.mulPose(matrix); +// } +// } @Inject(method = "renderItemInHand", at = @At(value = "INVOKE", target = "Lorg/joml/Matrix4fStack;popMatrix()Lorg/joml/Matrix4fStack;", remap = false)) public void spectatorplus$renderItemInHand(float partialTicks, boolean sleeping, Matrix4f projectionMatrix, CallbackInfo ci, @Local PoseStack poseStackIn) { if (SpectatorClientMod.config.renderArms && this.minecraft.player != null && this.minecraft.options.getCameraType().isFirstPerson() && !this.minecraft.options.hideGui) { final AbstractClientPlayer spectated = SpecUtil.getCameraPlayer(this.minecraft); if (spectated != null && !spectated.isSpectator()) { - this.lightTexture.turnOnLightLayer(); + //this.lightTexture.turnOnLightLayer(); float attackAnim = spectated.getAttackAnim(partialTicks); final InteractionHand interactionHand = MoreObjects.firstNonNull(spectated.swingingArm, InteractionHand.MAIN_HAND); @@ -81,7 +81,7 @@ private void redirectMulPose(PoseStack poseStack, Matrix4fc matrix) { if (handRenderSelection.renderMainHand) { final float swingProgress = interactionHand == InteractionHand.MAIN_HAND ? attackAnim : 0.0F; - final float equippedProgress = 1F - Mth.lerp(partialTicks, accessor.getOMainHandHeight(), accessor.getMainHandHeight()); + final float equippedProgress = accessor.getItemModelResolver().swapAnimationScale(accessor.getMainHandItem()) * (1F - Mth.lerp(partialTicks, accessor.getOMainHandHeight(), accessor.getMainHandHeight())); accessor.invokeRenderArmWithItem(spectated, partialTicks, pitch, InteractionHand.MAIN_HAND, swingProgress, accessor.getMainHandItem(), equippedProgress, @@ -90,14 +90,15 @@ private void redirectMulPose(PoseStack poseStack, Matrix4fc matrix) { if (handRenderSelection.renderOffHand) { final float swingProgress = interactionHand == InteractionHand.OFF_HAND ? attackAnim : 0.0F; - final float equippedProgress = 1F - Mth.lerp(partialTicks, accessor.getOOffHandHeight(), accessor.getOffHandHeight()); + final float equippedProgress = accessor.getItemModelResolver().swapAnimationScale(accessor.getOffHandItem()) * (1F - Mth.lerp(partialTicks, accessor.getOOffHandHeight(), accessor.getOffHandHeight())); accessor.invokeRenderArmWithItem(spectated, partialTicks, pitch, InteractionHand.OFF_HAND, swingProgress, accessor.getOffHandItem(), equippedProgress, poseStackIn, submitNodeCollector, packedLightCoords); } - this.lightTexture.turnOffLightLayer(); + //this.lightTexture.turnOffLightLayer(); + this.minecraft.gameRenderer.getFeatureRenderDispatcher().renderAllFeatures(); this.renderBuffers.bufferSource().endBatch(); } } diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererAccessor.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererAccessor.java index 3872649..f8a7663 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererAccessor.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/ItemInHandRendererAccessor.java @@ -4,6 +4,7 @@ import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.renderer.ItemInHandRenderer; import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.item.ItemModelResolver; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -35,6 +36,9 @@ void invokeRenderArmWithItem(AbstractClientPlayer player, float partialTick, flo @Accessor ItemStack getOffHandItem(); + @Accessor + ItemModelResolver getItemModelResolver(); + @Invoker("isChargedCrossbow") static boolean invokeIsChargedCrossbow(ItemStack stack) { throw new AssertionError(); From 6d94a62127e3da8ceeaeb4103adfbb1b0117a8d2 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:25:49 +0800 Subject: [PATCH 17/19] close shadowJar of paper build --- paper/build.gradle.kts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts index c0ee068..0ce70fe 100644 --- a/paper/build.gradle.kts +++ b/paper/build.gradle.kts @@ -32,18 +32,13 @@ tasks { } shadowJar { - archiveVersion = getByName("jar").archiveVersion - archiveClassifier.set("") - from("../LICENSE") - - listOf( - "xyz.jpenilla.reflectionremapper", - "net.fabricmc.mappingio", - ).forEach { relocate(it, "com.hpfxd.spectatorplus.paper.libs.$it") } + enabled = false } jar { - enabled = false // only output shadow jar + enabled = true + archiveClassifier.set("") + from("../LICENSE") } runServer { From e42b4a8f1b18309811b4072eac90a2e5f077efd5 Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:26:42 +0800 Subject: [PATCH 18/19] feat: configure run directories for client and server --- fabric/build.gradle.kts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 0bfcc35..05cd608 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -22,6 +22,20 @@ loom { } } + runs { + getByName("client") { + client() + ideConfigGenerated(true) + runDir("run/client") + } + + getByName("server") { + server() + ideConfigGenerated(true) + runDir("run") + } + } + accessWidenerPath = file("src/main/resources/spectatorplus.accesswidener") } From 0f095c09fc8b23a2f0943d7d12a05a5f801e2fbb Mon Sep 17 00:00:00 2001 From: RCUTANF <110910565+RCUTANF@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:30:03 +0800 Subject: [PATCH 19/19] fix: integrate server will be correctly use original effects data instead of using client side hacking data --- .../spectatorplus/fabric/client/mixin/LivingEntityMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java index 608520b..44a7c14 100644 --- a/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java +++ b/fabric/src/client/java/com/hpfxd/spectatorplus/fabric/client/mixin/LivingEntityMixin.java @@ -60,7 +60,7 @@ private boolean isLookingAtBlock() { at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/LivingEntity;activeEffects:Ljava/util/Map;")) private Map, MobEffectInstance> spectatorplus$redirectActiveEffects(LivingEntity instance) { // 只对玩家且满足条件时才重定向 - if (instance instanceof Player && EffectUtil.shouldUseSpectatorData()) { + if (instance.level().isClientSide() && instance instanceof Player && EffectUtil.shouldUseSpectatorData()) { return EffectUtil.getActiveEffectsMap(); } return ((LivingEntityAccessor) instance).spectatorplus$getActiveEffects();