|
| 1 | +package xyz.eclipseisoffline.modifyplayerdata; |
| 2 | + |
| 3 | +import com.mojang.datafixers.util.Pair; |
| 4 | +import com.mojang.serialization.Codec; |
| 5 | +import net.minecraft.block.entity.SculkShriekerWarningManager; |
| 6 | +import net.minecraft.component.DataComponentTypes; |
| 7 | +import net.minecraft.component.type.NbtComponent; |
| 8 | +import net.minecraft.entity.EntityEquipment; |
| 9 | +import net.minecraft.entity.EquipmentSlot; |
| 10 | +import net.minecraft.entity.LazyEntityReference; |
| 11 | +import net.minecraft.entity.LivingEntity; |
| 12 | +import net.minecraft.entity.attribute.EntityAttributeInstance; |
| 13 | +import net.minecraft.entity.player.PlayerAbilities; |
| 14 | +import net.minecraft.entity.player.PlayerEntity; |
| 15 | +import net.minecraft.inventory.StackWithSlot; |
| 16 | +import net.minecraft.item.ItemStack; |
| 17 | +import net.minecraft.network.packet.s2c.play.EntityEquipmentUpdateS2CPacket; |
| 18 | +import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket; |
| 19 | +import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket; |
| 20 | +import net.minecraft.server.network.ServerPlayerEntity; |
| 21 | +import net.minecraft.storage.ReadView; |
| 22 | +import net.minecraft.storage.WriteView; |
| 23 | +import net.minecraft.util.Arm; |
| 24 | +import net.minecraft.util.math.BlockPos; |
| 25 | +import net.minecraft.util.math.Vec2f; |
| 26 | +import net.minecraft.util.math.Vec3d; |
| 27 | +import xyz.eclipseisoffline.modifyplayerdata.mixin.HungerManagerAccessor; |
| 28 | +import xyz.eclipseisoffline.modifyplayerdata.mixin.LivingEntityAccessor; |
| 29 | +import xyz.eclipseisoffline.modifyplayerdata.mixin.PlayerEntityAccessor; |
| 30 | +import xyz.eclipseisoffline.modifyplayerdata.mixin.ServerPlayerEntityAccessor; |
| 31 | + |
| 32 | +import java.util.HashSet; |
| 33 | +import java.util.List; |
| 34 | +import java.util.Optional; |
| 35 | +import java.util.OptionalDouble; |
| 36 | +import java.util.Set; |
| 37 | + |
| 38 | +public class PlayerData { |
| 39 | + |
| 40 | + public static void read(ServerPlayerEntity player, ReadView view) { |
| 41 | + getOptionalBoolean(view, "LeftHanded").ifPresent(leftHanded -> player.setMainArm(leftHanded ? Arm.LEFT : Arm.RIGHT)); |
| 42 | + |
| 43 | + // Backwards compatibility, does not apply to commands, only to saved data |
| 44 | + view.read("CustomData", NbtComponent.CODEC).ifPresent(data -> player.setComponent(DataComponentTypes.CUSTOM_DATA, data)); |
| 45 | + } |
| 46 | + |
| 47 | + public static void apply(ServerPlayerEntity player, ReadView view) { |
| 48 | + // Entity data |
| 49 | + view.read("Pos", Vec3d.CODEC).ifPresent(pos -> player.teleport(player.getWorld(), pos.x, pos.y, pos.z, Set.of(), |
| 50 | + player.getYaw(), player.getPitch(), false)); |
| 51 | + |
| 52 | + view.read("Motion", Vec3d.CODEC).ifPresent(motion -> { |
| 53 | + player.setVelocity(motion); |
| 54 | + player.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(player)); |
| 55 | + }); |
| 56 | + |
| 57 | + view.read("Rotation", Vec2f.CODEC).ifPresent(rotation -> player.teleport(player.getWorld(), player.getX(), player.getY(), player.getZ(), |
| 58 | + Set.of(), rotation.x, rotation.y, false)); |
| 59 | + |
| 60 | + getOptionalDouble(view, "fall_distance").ifPresent(fallDistance -> player.fallDistance = fallDistance); |
| 61 | + getOptionalShort(view, "Fire").ifPresent(player::setFireTicks); |
| 62 | + view.getOptionalInt("Air").ifPresent(player::setAir); |
| 63 | + getOptionalBoolean(view, "Invulnerable").ifPresent(player::setInvulnerable); |
| 64 | + view.getOptionalInt("PortalCooldown").ifPresent(player::setPortalCooldown); |
| 65 | + getOptionalBoolean(view, "Silent").ifPresent(player::setSilent); |
| 66 | + getOptionalBoolean(view, "NoGravity").ifPresent(player::setNoGravity); |
| 67 | + getOptionalBoolean(view, "Glowing").ifPresent(player::setGlowing); |
| 68 | + view.getOptionalInt("TicksFrozen").ifPresent(player::setFrozenTicks); |
| 69 | + getOptionalBoolean(view, "HasVisualFire").ifPresent(visualFire -> ((VisualFireable) player).modifyPlayerData$setHasVisualFire(visualFire)); |
| 70 | + |
| 71 | + view.getOptionalTypedListView("Tags", Codec.STRING).ifPresent(stream -> { |
| 72 | + List<String> tags = stream.stream().toList(); |
| 73 | + Set<String> currentTags = new HashSet<>(player.getCommandTags()); |
| 74 | + for (String currentTag : currentTags) { |
| 75 | + if (!tags.contains(currentTag)) { |
| 76 | + player.removeCommandTag(currentTag); |
| 77 | + } |
| 78 | + } |
| 79 | + for (String newTag : tags) { |
| 80 | + if (!player.getCommandTags().contains(newTag)) { |
| 81 | + player.addCommandTag(newTag); |
| 82 | + } |
| 83 | + } |
| 84 | + }); |
| 85 | + |
| 86 | + view.read("data", NbtComponent.CODEC).ifPresent(data -> player.setComponent(DataComponentTypes.CUSTOM_DATA, data)); |
| 87 | + |
| 88 | + |
| 89 | + // Living entity data |
| 90 | + getOptionalFloat(view, "Health").ifPresent(player::setHealth); |
| 91 | + getOptionalShort(view, "HurtTime").ifPresent(hurtTime -> { |
| 92 | + player.hurtTime = hurtTime; |
| 93 | + player.maxHurtTime = hurtTime; |
| 94 | + }); |
| 95 | + getOptionalFloat(view, "AbsorptionAmount").ifPresent(player::setAbsorptionAmount); |
| 96 | + view.read("attributes", EntityAttributeInstance.Packed.LIST_CODEC).ifPresent(player.getAttributes()::unpack); |
| 97 | + |
| 98 | + getOptionalBoolean(view, "FallFlying").ifPresent(fallFlying -> { |
| 99 | + if (fallFlying) { |
| 100 | + player.startGliding(); |
| 101 | + } else { |
| 102 | + player.stopGliding(); |
| 103 | + } |
| 104 | + }); |
| 105 | + |
| 106 | + LazyEntityReference<PlayerEntity> attackingPlayer = LazyEntityReference.fromData(view, "last_hurt_by_player"); |
| 107 | + int lastHurtByPlayer = view.getInt("last_hurt_by_player_memory_time", player.getPlayerHitTimer()); |
| 108 | + if (attackingPlayer != null) { |
| 109 | + ((LivingEntityAccessor) player).invokeSetAttacking(attackingPlayer, lastHurtByPlayer); |
| 110 | + } |
| 111 | + |
| 112 | + LazyEntityReference<LivingEntity> attacker = LazyEntityReference.fromData(view, "last_hurt_by_mob"); |
| 113 | + int ticksSinceLastHurt = view.getInt("ticks_since_last_hurt_by_mob", player.age - player.getLastAttackedTime()); |
| 114 | + if (attacker != null) { |
| 115 | + ((LivingEntityAccessor) player).setAttackerReference(attacker); |
| 116 | + ((LivingEntityAccessor) player).setLastAttackedTime(ticksSinceLastHurt + player.age); |
| 117 | + } |
| 118 | + |
| 119 | + view.read("equipment", EntityEquipment.CODEC).ifPresent(equipment -> ((LivingEntityAccessor) player).getEquipment().copyFrom(equipment)); |
| 120 | + |
| 121 | + |
| 122 | + // Technically a mob property, we include it anyway because it's fun |
| 123 | + getOptionalBoolean(view, "LeftHanded").ifPresent(leftHanded -> player.setMainArm(leftHanded ? Arm.LEFT : Arm.RIGHT)); |
| 124 | + |
| 125 | + |
| 126 | + // Player data |
| 127 | + view.getOptionalTypedListView("Inventory", StackWithSlot.CODEC).ifPresent(player.getInventory()::readData); |
| 128 | + |
| 129 | + view.read("SelectedItem", ItemStack.CODEC).ifPresent(stack -> { |
| 130 | + player.getInventory().setStack(player.getInventory().getSelectedSlot(), stack); |
| 131 | + player.networkHandler.sendPacket(new EntityEquipmentUpdateS2CPacket(player.getId(), |
| 132 | + List.of(Pair.of(EquipmentSlot.MAINHAND, stack)))); |
| 133 | + }); |
| 134 | + view.getOptionalInt("SelectedItemSlot").ifPresent(slot -> { |
| 135 | + player.getInventory().setSelectedSlot(slot); |
| 136 | + player.networkHandler.sendPacket(new UpdateSelectedSlotS2CPacket(slot)); |
| 137 | + }); |
| 138 | + |
| 139 | + getOptionalShort(view, "SleepTimer").ifPresent(timer -> ((PlayerEntityAccessor) player).setSleepTimer(timer)); |
| 140 | + view.getOptionalInt("Score").ifPresent(player::setScore); |
| 141 | + |
| 142 | + view.getOptionalInt("foodLevel").ifPresent(player.getHungerManager()::setFoodLevel); |
| 143 | + view.getOptionalInt("foodTickTimer").ifPresent(timer -> ((HungerManagerAccessor) player.getHungerManager()).setFoodTickTimer(timer)); |
| 144 | + getOptionalFloat(view, "foodSaturationLevel").ifPresent(player.getHungerManager()::setSaturationLevel); |
| 145 | + getOptionalFloat(view, "foodExhaustionLevel").ifPresent(level -> ((HungerManagerAccessor) player.getHungerManager()).setExhaustion(level)); |
| 146 | + |
| 147 | + view.read("abilities", PlayerAbilities.Packed.CODEC).ifPresent(packed -> { |
| 148 | + player.getAbilities().unpack(packed); |
| 149 | + player.sendAbilitiesUpdate(); |
| 150 | + }); |
| 151 | + |
| 152 | + view.getOptionalTypedListView("EnderItems", StackWithSlot.CODEC).ifPresent(player.getEnderChestInventory()::readData); |
| 153 | + |
| 154 | + view.read("current_explosion_impact_pos", Vec3d.CODEC).ifPresent(pos -> player.currentExplosionImpactPos = pos); |
| 155 | + getOptionalBoolean(view, "ignore_fall_damage_from_current_explosion").ifPresent(ignoreDamage -> ((PlayerEntityAccessor) player).setIgnoreFallDamageFromCurrentExplosionRaw(ignoreDamage)); |
| 156 | + view.getOptionalInt("current_impulse_context_reset_grace_time").ifPresent(time -> ((PlayerEntityAccessor) player).setCurrentExplosionResetGraceTime(time)); |
| 157 | + |
| 158 | + |
| 159 | + // Server player data |
| 160 | + view.read("warden_spawn_tracker", SculkShriekerWarningManager.CODEC).ifPresent(manager -> ((ServerPlayerEntityAccessor) player).setSculkShriekerWarningManager(manager)); |
| 161 | + view.read("entered_nether_pos", Vec3d.CODEC).ifPresent(pos -> ((ServerPlayerEntityAccessor) player).setEnteredNetherPos(pos)); |
| 162 | + getOptionalBoolean(view, "seenCredits").ifPresent(seenCredits -> ((ServerPlayerEntityAccessor) player).setSeenCredits(seenCredits)); |
| 163 | + view.read("respawn", ServerPlayerEntity.Respawn.CODEC).ifPresent(respawn -> player.setSpawnPoint(respawn, false)); |
| 164 | + getOptionalBoolean(view, "spawn_extra_particles_on_fall").ifPresent(player::setSpawnExtraParticlesOnFall); |
| 165 | + view.read("raid_omen_position", BlockPos.CODEC).ifPresent(player::setStartRaidPos); |
| 166 | + } |
| 167 | + |
| 168 | + public static void write(ServerPlayerEntity player, WriteView view) { |
| 169 | + if (player.getMainArm() == Arm.LEFT) { |
| 170 | + view.putBoolean("LeftHanded", true); |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + private static OptionalDouble getOptionalDouble(ReadView view, String key) { |
| 175 | + return view.read(key, Codec.DOUBLE).stream().mapToDouble(d -> d).findFirst(); |
| 176 | + } |
| 177 | + |
| 178 | + private static Optional<Short> getOptionalShort(ReadView view, String key) { |
| 179 | + return view.read(key, Codec.SHORT); |
| 180 | + } |
| 181 | + |
| 182 | + private static Optional<Boolean> getOptionalBoolean(ReadView view, String key) { |
| 183 | + return view.read(key, Codec.BOOL); |
| 184 | + } |
| 185 | + |
| 186 | + private static Optional<Float> getOptionalFloat(ReadView view, String key) { |
| 187 | + return view.read(key, Codec.FLOAT); |
| 188 | + } |
| 189 | +} |
0 commit comments