diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java index c9763388..21d5c7a2 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.SpellReference; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.network.track.ObjectTracker; import com.minelittlepony.unicopia.network.track.Trackable; import com.minelittlepony.unicopia.network.track.TrackableObject; @@ -128,4 +129,13 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable { spell.fromNBT(compound); } } + + @Override + public void copyFrom(SpellSlots other, boolean alive) { + if (alive) { + other.stream().forEach(this::put); + } else { + other.stream().filter(SpellType.PLACE_CONTROL_SPELL).forEach(this::put); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java index b1ba5633..73a1c6f2 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java @@ -65,4 +65,9 @@ class SingleSpellSlot implements SpellSlots, NbtSerialisable { public void fromNBT(NbtCompound compound) { entry.readTrackedNbt(compound.getCompound("effect")); } + + @Override + public void copyFrom(SpellSlots other, boolean alive) { + other.get().ifPresent(this::put); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellSlots.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellSlots.java index 97d30882..32a70f87 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellSlots.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellSlots.java @@ -7,9 +7,10 @@ import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.util.Copyable; import com.minelittlepony.unicopia.util.NbtSerialisable; -public interface SpellSlots extends NbtSerialisable { +public interface SpellSlots extends NbtSerialisable, Copyable { static SpellInventory ofUnbounded(Caster caster) { return new SpellInventory(caster, new MultiSpellSlot(caster)); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java index 2721ec83..fbb29e1c 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java @@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.item.EnchantableItem; +import com.minelittlepony.unicopia.util.Copyable; import com.minelittlepony.unicopia.util.NbtSerialisable; import net.minecraft.nbt.NbtCompound; @@ -15,7 +16,7 @@ import net.minecraft.nbt.NbtList; import net.minecraft.util.Hand; import net.minecraft.util.TypedActionResult; -public class PlayerCharmTracker implements NbtSerialisable { +public class PlayerCharmTracker implements NbtSerialisable, Copyable { private final Pony pony; @@ -62,6 +63,13 @@ public class PlayerCharmTracker implements NbtSerialisable { return previous; } + @Override + public void copyFrom(PlayerCharmTracker old, boolean alive) { + for (int i = 0; i < handSpells.length; i++) { + handSpells[i] = old.handSpells[i]; + } + } + @Override public void toNBT(NbtCompound compound) { NbtList equippedSpells = new NbtList(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index afdead15..09ec5ceb 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -63,7 +63,6 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; import net.minecraft.util.math.*; import net.minecraft.world.GameMode; import net.minecraft.world.GameRules; @@ -116,6 +115,7 @@ public class Pony extends Living implements Copyable, Update sender.accept(Channel.SERVER_PLAYER_CAPABILITIES.toPacket(new MsgPlayerCapabilities(this))); } }); + race = this.tracker.startTracking(Race.TRACKABLE_TYPE, Race.UNSET); suppressedRace = this.tracker.startTracking(Race.TRACKABLE_TYPE, Race.UNSET); this.levels = new PlayerLevelStore(this, tracker, true, USounds.Vanilla.ENTITY_PLAYER_LEVELUP); @@ -817,8 +817,6 @@ public class Pony extends Living implements Copyable, Update @Override public void toNBT(NbtCompound compound) { - compound.putString("playerSpecies", Race.REGISTRY.getId(getSpecies()).toString()); - compound.putString("suppressedSpecies", Race.REGISTRY.getId(getSuppressedRace()).toString()); compound.put("mana", mana.toNBT()); compound.putInt("levels", levels.get()); compound.putInt("corruption", corruption.get()); @@ -827,8 +825,6 @@ public class Pony extends Living implements Copyable, Update @Override public void fromNBT(NbtCompound compound) { - setSpecies(Race.fromName(compound.getString("playerSpecies"), Race.HUMAN)); - setSuppressedRace(Race.fromName(compound.getString("suppressedSpecies"), Race.UNSET)); levels.set(compound.getInt("levels")); corruption.set(compound.getInt("corruption")); mana.fromNBT(compound.getCompound("mana")); @@ -837,7 +833,10 @@ public class Pony extends Living implements Copyable, Update @Override public void toSyncronisedNbt(NbtCompound compound) { + System.out.println("toSyncNbt"); super.toSyncronisedNbt(compound); + compound.putString("playerSpecies", Race.REGISTRY.getId(getSpecies()).toString()); + compound.putString("suppressedSpecies", Race.REGISTRY.getId(getSuppressedRace()).toString()); compound.putFloat("magicExhaustion", magicExhaustion); compound.putInt("ticksInSun", ticksInSun); compound.putBoolean("hasShades", hasShades); @@ -857,7 +856,10 @@ public class Pony extends Living implements Copyable, Update @Override public void fromSynchronizedNbt(NbtCompound compound) { + System.out.println("fromSyncNbt"); super.fromSynchronizedNbt(compound); + setSpecies(Race.fromName(compound.getString("playerSpecies"), Race.HUMAN)); + setSuppressedRace(Race.fromName(compound.getString("suppressedSpecies"), Race.UNSET)); powers.fromNBT(compound.getCompound("powers")); gravity.fromNBT(compound.getCompound("gravity")); charms.fromNBT(compound.getCompound("charms")); @@ -877,7 +879,6 @@ public class Pony extends Living implements Copyable, Update @Override public void copyFrom(Pony oldPlayer, boolean alive) { - boolean forcedSwap = (!alive && entity instanceof ServerPlayerEntity && entity.getWorld().getGameRules().getBoolean(UGameRules.SWAP_TRIBE_ON_DEATH) @@ -885,17 +886,20 @@ public class Pony extends Living implements Copyable, Update || oldPlayer.getSpecies().isUnset(); Race oldSuppressedRace = oldPlayer.getSuppressedRace(); + Race newRace = oldPlayer.respawnRace != Race.UNSET && !alive ? oldPlayer.respawnRace : oldPlayer.getSpecies(); - if (alive) { - oldPlayer.getSpellSlot().stream().forEach(getSpellSlot()::put); + if (forcedSwap || !newRace.canCast()) { + getSpellSlot().clear(); } else { - if (forcedSwap) { - oldSuppressedRace = Race.UNSET; - Channel.SERVER_SELECT_TRIBE.sendToPlayer(new MsgTribeSelect(Race.allPermitted(entity), "gui.unicopia.tribe_selection.respawn"), (ServerPlayerEntity)entity); - } else { - oldPlayer.getSpellSlot().stream().filter(SpellType.PLACE_CONTROL_SPELL).forEach(getSpellSlot()::put); - } + getSpellSlot().copyFrom(oldPlayer.getSpellSlot(), alive); + } + if (forcedSwap) { + oldSuppressedRace = Race.UNSET; + Channel.SERVER_SELECT_TRIBE.sendToPlayer(new MsgTribeSelect(Race.allPermitted(entity), "gui.unicopia.tribe_selection.respawn"), (ServerPlayerEntity)entity); + } + + if (!alive) { // putting it here instead of adding another injection point into ServerPlayerEntity.copyFrom() if (!asWorld().getGameRules().getBoolean(GameRules.KEEP_INVENTORY)) { PlayerInventory inventory = oldPlayer.asEntity().getInventory(); @@ -908,14 +912,13 @@ public class Pony extends Living implements Copyable, Update } } - setSpecies(oldPlayer.respawnRace != Race.UNSET && !alive ? oldPlayer.respawnRace : oldPlayer.getSpecies()); + setSpecies(newRace); setSuppressedRace(oldSuppressedRace); getDiscoveries().copyFrom(oldPlayer.getDiscoveries(), alive); getPhysics().copyFrom(oldPlayer.getPhysics(), alive); if (!forcedSwap) { getArmour().copyFrom(oldPlayer.getArmour(), alive); - getCharms().equipSpell(Hand.MAIN_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.MAIN_HAND)); - getCharms().equipSpell(Hand.OFF_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.OFF_HAND)); + getCharms().copyFrom(oldPlayer.getCharms(), alive); corruption.set(oldPlayer.getCorruption().get()); levels.set(oldPlayer.getLevel().get()); } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinClientPlayNetworkHandler.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinClientPlayNetworkHandler.java index e8c1179b..607c7bd9 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinClientPlayNetworkHandler.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinClientPlayNetworkHandler.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.mixin.client; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -10,13 +12,19 @@ import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.behaviour.Disguise; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.network.track.Trackable; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.world.ClientWorld; import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket; +import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket; @Mixin(ClientPlayNetworkHandler.class) abstract class MixinClientPlayNetworkHandler { + @Shadow private @Final MinecraftClient client; @Shadow private ClientWorld world; @Inject(method = "onEntityStatus", at = @At("TAIL")) @@ -32,4 +40,20 @@ abstract class MixinClientPlayNetworkHandler { }); } } + + @Nullable + private ClientPlayerEntity oldPlayer; + + @Inject(method = "onPlayerRespawn", at = @At("HEAD")) + public void beforeOnPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo info) { + oldPlayer = client.player; + } + + @Inject(method = "onPlayerRespawn", at = @At("RETURN")) + public void afterOnPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo info) { + if (oldPlayer != null && oldPlayer != client.player) { + Trackable.of(oldPlayer).getDataTrackers().copyTo(Trackable.of(client.player).getDataTrackers()); + Pony.of(client.player).copyFrom(Pony.of(oldPlayer), true); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayNetworkHandler.java b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayNetworkHandler.java index de2ed3ef..08db3c52 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayNetworkHandler.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/server/MixinServerPlayNetworkHandler.java @@ -43,7 +43,7 @@ abstract class MixinServerPlayNetworkHandler implements EntityTrackingListener, } @Inject(method = "tick()V", at = @At("HEAD")) - private void beforePlayerTick() { + private void beforePlayerTick(CallbackInfo info) { if (Pony.of(player).getPhysics().isFlyingSurvival) { floating = false; floatingTicks = 0; diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java b/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java index 1c99f1cb..bad61f3f 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java @@ -75,6 +75,17 @@ public class DataTracker { } } + @SuppressWarnings("unchecked") + synchronized void copyTo(DataTracker destination) { + for (int i = 0; i < codecs.size(); i++) { + ((Pair)destination.codecs.get(i)).value = codecs.get(i).value; + TrackableObject o = destination.persistentObjects.get(i); + if (o != null) { + o.readTrackedNbt((NbtCompound)codecs.get(i).value); + } + } + } + synchronized Optional getInitialPairs() { initial = false; dirtyIndices = new IntOpenHashSet(); diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java b/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java index 3b199346..b13e9eaa 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java @@ -80,6 +80,16 @@ public class DataTrackerManager { } } + @SuppressWarnings({ "unchecked", "rawtypes" }) + public synchronized void copyTo(DataTrackerManager destination) { + for (int i = 0; i < trackers.size(); i++) { + trackers.get(i).copyTo(i >= destination.trackers.size() ? destination.checkoutTracker() : destination.trackers.get(i)); + } + for (int i = 0; i < objectTrackers.size(); i++) { + ((ObjectTracker)objectTrackers.get(i)).copyTo(destination.objectTrackers.get(i)); + } + } + @SuppressWarnings({ "rawtypes", "unchecked" }) public synchronized void sendInitial(ServerPlayerEntity player, Consumer> sender) { synchronized (this) { diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java b/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java index 6a1e6005..85554c4a 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java @@ -73,6 +73,15 @@ public class ObjectTracker implements NbtSerialisable quickAccess = Map.copyOf(trackedObjects); } + synchronized void copyTo(ObjectTracker destination) { + for (var entry : trackedObjects.entrySet()) { + T copy = destination.constructor.get(); + copy.readTrackedNbt(entry.getValue().toTrackedNbt()); + destination.trackedObjects.put(entry.getKey(), copy); + } + destination.quickAccess = Map.copyOf(destination.trackedObjects); + } + synchronized Optional getInitialPairs() { if (trackedObjects.isEmpty()) { return Optional.empty();