Skip to content

Commit 248e313

Browse files
Update to 1.21.6, refactor player data modifiers, add support for a bunch of new data keys
1 parent e82c74b commit 248e313

12 files changed

+321
-332
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
1-
- Add support for the `equipment` NBT tag.
2-
- Properly read fall distance from the `fall_distance` tag instead of the old `FallDistance` tag.
1+
- Updated to 1.21.6.
2+
- Added support for the following data keys:
3+
- `last_hurt_by_player`
4+
- `last_hurt_by_player_memory_time`
5+
- `last_hurt_by_mob`
6+
- `ticks_since_last_hurt_by_mob`
7+
- `current_explosion_impact_pos`
8+
- `ignore_fall_damage_from_current_explosion`
9+
- `current_impulse_context_reset_grace_time`
10+
- `entered_nether_pos`
11+
- `respawn`
12+
- `spawn_extra_particles_on_fall`
13+
- `raid_omen_position`

README.md

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ For support and/or any questions you may have, feel free to join [my discord](ht
2727

2828
| Minecraft Version | Status |
2929
|-------------------|--------------|
30-
| 1.21.5 | ✅ Current |
30+
| 1.21.6 | ✅ Current |
31+
| 1.21.5 | ✔️ Available |
3132
| 1.21.4 | ✔️ Available |
3233
| 1.21.2+3 | ✔️ Available |
3334
| 1.21+1 | ✅ Current |
@@ -50,80 +51,84 @@ The Fabric API is not required. This mod is not required on clients.
5051

5152
NBT tags currently supported:
5253

53-
- `Air`
54+
- `Pos`
55+
- `Motion`
56+
- `Rotation`
5457
- `fall_distance`
5558
- `Fire`
56-
- `Glowing`
57-
- `HasVisualFire`
59+
- `Air`
5860
- `Invulnerable`
59-
- `Motion`
60-
- `NoGravity`
6161
- `PortalCooldown`
62-
- `Pos`
63-
- `Rotation`
64-
- `Tags`
65-
- `Silent`[^7]
62+
- `Silent`[^1]
63+
- `NoGravity`
64+
- `Glowing`
6665
- `TicksFrozen`
66+
- `HasVisualFire`
67+
- `Tags`
68+
- `data`
69+
- `Health`
70+
- `HurtTime`[^1]
6771
- `AbsorptionAmount`
6872
- `attributes`
69-
- `equipment`
7073
- `FallFlying`
71-
- `Health`
72-
- `HurtTime`[^7]
74+
- `last_hurt_by_player`
75+
- `last_hurt_by_player_memory_time`
76+
- `last_hurt_by_mob`
77+
- `ticks_since_last_hurt_by_mob`
78+
- `equipment`
7379
- `LeftHanded`
74-
- `abilities`
75-
- `EnderItems`
76-
- `foodExhaustionLevel`
77-
- `foodLevel`
78-
- `foodSaturationLevel`
79-
- `foodTickTimer`
8080
- `Inventory`
81-
- `recipeBook`
82-
- `Score`
83-
- `seenCredits`
8481
- `SelectedItem`
8582
- `SelectedItemSlot`
8683
- `SleepTimer`
84+
- `Score`
85+
- `foodLevel`
86+
- `foodTickTimer`
87+
- `foodSaturationLevel`
88+
- `foodExhaustionLevel`
89+
- `abilities`
90+
- `EnderItems`
91+
- `current_explosion_impact_pos`
92+
- `ignore_fall_damage_from_current_explosion`
93+
- `current_impulse_context_reset_grace_time`
8794
- `warden_spawn_tracker`
88-
- `data`
95+
- `entered_nether_pos`
96+
- `seenCredits`
97+
- `respawn`
98+
- `spawn_extra_particles_on_fall`
99+
- `raid_omen_position`
89100

90101
NBT tags I won't add support for:
91102

92-
- `CustomName`
93-
- `CustomNameVisible`
94103
- `OnGround`
95-
- `Passengers`[^1]
96104
- `UUID`
97-
- `active_effects`[^2]
105+
- `CustomName`
106+
- `CustomNameVisible`
107+
- `Passengers`[^2]
98108
- `DeathTime`
99-
- `HurtByTimestamp`[^8]
100-
- `Team`[^5]
109+
- `active_effects`[^3]
101110
- `sleeping_pos`
102111
- `Brain`
103-
- `last_hurt_by_player`[^8]
104-
- `last_hurt_by_player_memory_time`[^8]
105-
- `last_hurt_by_mob`[^8]
106-
- `ticks_since_last_hurt_by_mob`[^8]
107-
- `XpLevel`[^4]
108-
- `XpP`[^4]
109-
- `XpSeed`[^8]
110-
- `XpTotal`[^4]
112+
- `locator_bar_icon`[^4]
113+
- `XpP`[^5]
114+
- `XpLevel`[^5]
115+
- `XpTotal`[^5]
116+
- `XpSeed`[^6]
111117
- `ShoulderEntityLeft`
112118
- `ShoulderEntityRight`
113-
- `LastDeathLocation`[^6]
114-
- `current_explosion_impact_pos` [^8]
115-
- `ignore_fall_damage_from_current_explosion` [^8]
116-
- `current_impulse_context_reset_grace_time` [^8]
117-
- `entered_nether_pos`[^8]
118-
- `respawn` [^3]
119-
- `spawn_extra_particles_on_fall` [^8]
120-
- `raid_omen_position` [^8]
121-
122-
[^1]: Use the `/ride` command.
123-
[^2]: Use the `/effect` command.
124-
[^3]: Use the `/spawnpoint` command.
125-
[^4]: Use the `/xp` command.
126-
[^5]: Use the `/team` command.
127-
[^6]: Unable to implement due to limitations within the vanilla client.
128-
[^7]: Is implemented but does not do much due to limitations within the vanilla client
129-
[^8]: I can't think of any use cases for this tag, but will add support on request.
119+
- `LastDeathLocation`[^7]
120+
- `playerGameType`
121+
- `previousPlayerGameType`[^7]
122+
- `RootVehicle`[^2]
123+
- `recipeBook`[^8]
124+
- `Dimension`
125+
- `ender_pearls`
126+
127+
[^1]: Is implemented but does not do much due to limitations within the vanilla client
128+
[^2]: Use the `/ride` command.
129+
[^3]: Use the `/effect` command.
130+
[^4]: Use the `/waypoint` command.
131+
[^5]: Use the `/xp` command.
132+
[^6]: I can't think of any use cases for this tag, but will add support on request.
133+
[^7]: Unable to implement due to limitations within the vanilla client.
134+
[^8]: Use the `/recipe` command.

gradle.properties

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
org.gradle.jvmargs=-Xmx1G
33

44
# Fabric Properties
5-
minecraft_version=1.21.5
6-
yarn_mappings=1.21.5+build.1
7-
loader_version=0.16.10
5+
minecraft_version=1.21.6
6+
yarn_mappings=1.21.6+build.1
7+
loader_version=0.16.14
88

99
# Mod Properties
10-
mod_version=0.3.1-1.21.5
11-
supported_versions=1.21.5
10+
mod_version=0.3.1-1.21.6
11+
supported_versions=1.21.6
1212
maven_group=xyz.eclipseisoffline
1313
archives_base_name=modifyplayerdata
1414

1515
# Publishing
1616
release_type=STABLE
17-
release_minecraft_versions=1.21.5
17+
release_minecraft_versions=1.21.6
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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

Comments
 (0)