Improved offline playerdata manipulation

- Ability to change player experience and score
- Handle upgrade of player data (from older Mc version)
This commit is contained in:
Marc Baloup 2023-02-19 16:11:04 +01:00
parent bf59617e19
commit 73d96d0bb7
8 changed files with 164 additions and 18 deletions

View File

@ -3,6 +3,9 @@ package fr.pandacube.lib.paper.players;
import com.google.common.io.Files; import com.google.common.io.Files;
import fr.pandacube.lib.paper.reflect.util.PrimaryWorlds; import fr.pandacube.lib.paper.reflect.util.PrimaryWorlds;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer; import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCDataConverter;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCTypeRegistry;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.SharedConstants;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.NbtIo; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.NbtIo;
import fr.pandacube.lib.paper.util.PlayerDataWrapper; import fr.pandacube.lib.paper.util.PlayerDataWrapper;
@ -150,17 +153,23 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
/** /**
* Gets the NBT data from the playerdata file. * Gets the NBT data from the playerdata file.
* It will not work if the player is online, because the data on the file are not synchronized with real-time values. * It will not work if the player is online, because the data on the file are not synchronized with real-time values.
* @param convertTag true to convert the data to the current MC version, false to keep the saved version
* @return the NBT data from the playerdata file. * @return the NBT data from the playerdata file.
* @throws IllegalStateException if the player is online. * @throws IllegalStateException if the player is online.
*/ */
default CompoundTag getPlayerData() { default CompoundTag getPlayerData(boolean convertTag) {
if (isOnline()) if (isOnline())
throw new IllegalStateException("Cannot access data file of " + getName() + " because theyre online."); throw new IllegalStateException("Cannot access data file of " + getName() + " because theyre online.");
return ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class) CompoundTag data = ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class)
.getServer() .getServer()
.getPlayerList() .getPlayerList()
.playerIo() .playerIo()
.getPlayerData(getUniqueId().toString()); .getPlayerData(getUniqueId().toString());
if (convertTag) {
int i = data.contains("DataVersion", 3) ? data.getInt("DataVersion") : -1;
data = MCDataConverter.convertTag(MCTypeRegistry.PLAYER(), data, i, SharedConstants.getCurrentVersion().getWorldVersion());
}
return data;
} }
/** /**
@ -170,7 +179,7 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
* @throws IllegalStateException if the player is online. * @throws IllegalStateException if the player is online.
*/ */
default PlayerDataWrapper getPlayerDataWrapper() { default PlayerDataWrapper getPlayerDataWrapper() {
return new PlayerDataWrapper(getPlayerData()); return new PlayerDataWrapper(getPlayerData(true));
} }
/** /**

View File

@ -10,7 +10,11 @@ import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftVector;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftWorld; import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftWorld;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.RenderData; import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.RenderData;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.VanillaCommandWrapper; import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.VanillaCommandWrapper;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCDataConverter;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCDataType;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCTypeRegistry;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.DetectedVersion; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.DetectedVersion;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.GameVersion;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.SharedConstants; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.SharedConstants;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.WorldVersion; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.WorldVersion;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.BlockPosArgument; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.BlockPosArgument;
@ -107,6 +111,11 @@ public class PandalibPaperReflect {
initWrapper(RenderData.class, RenderData.REFLECT.get()); initWrapper(RenderData.class, RenderData.REFLECT.get());
initWrapper(VanillaCommandWrapper.class, VanillaCommandWrapper.REFLECT.get()); initWrapper(VanillaCommandWrapper.class, VanillaCommandWrapper.REFLECT.get());
// dataconverter
initWrapper(MCDataConverter.class, MCDataConverter.REFLECT.get());
initWrapper(MCDataType.class, MCDataType.REFLECT.get());
initWrapper(MCTypeRegistry.class, MCTypeRegistry.REFLECT.get());
// minecraft.commands // minecraft.commands
initWrapper(BlockPosArgument.class, BlockPosArgument.MAPPING.runtimeClass()); initWrapper(BlockPosArgument.class, BlockPosArgument.MAPPING.runtimeClass());
initWrapper(Commands.class, Commands.MAPPING.runtimeClass()); initWrapper(Commands.class, Commands.MAPPING.runtimeClass());
@ -170,6 +179,7 @@ public class PandalibPaperReflect {
initWrapper(VoxelShape.class, VoxelShape.MAPPING.runtimeClass()); initWrapper(VoxelShape.class, VoxelShape.MAPPING.runtimeClass());
// minecraft // minecraft
initWrapper(DetectedVersion.class, DetectedVersion.MAPPING.runtimeClass()); initWrapper(DetectedVersion.class, DetectedVersion.MAPPING.runtimeClass());
initWrapper(GameVersion.class, GameVersion.REFLECT.get());
initWrapper(SharedConstants.class, SharedConstants.MAPPING.runtimeClass()); initWrapper(SharedConstants.class, SharedConstants.MAPPING.runtimeClass());
initWrapper(WorldVersion.class, WorldVersion.MAPPING.runtimeClass()); initWrapper(WorldVersion.class, WorldVersion.MAPPING.runtimeClass());

View File

@ -0,0 +1,24 @@
package fr.pandacube.lib.paper.reflect.wrapper.dataconverter;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.chat.Component;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class MCDataConverter extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("ca.spottedleaf.dataconverter.minecraft.MCDataConverter"));
private static final ReflectMethod<?> convertTag = wrapEx(() -> REFLECT.method("convertTag", Component.MAPPING.runtimeClass()));
public static CompoundTag convertTag(MCDataType type, CompoundTag data, int fromVersion, int toVersion) {
return wrap(wrapReflectEx(() -> convertTag.invokeStatic(unwrap(type), unwrap(data), fromVersion, toVersion)), CompoundTag.class);
}
protected MCDataConverter(Object obj) {
super(obj);
}
}

View File

@ -0,0 +1,15 @@
package fr.pandacube.lib.paper.reflect.wrapper.dataconverter;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
public class MCDataType extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("ca.spottedleaf.dataconverter.minecraft.datatypes.MCDataType"));
protected MCDataType(Object obj) {
super(obj);
}
}

View File

@ -0,0 +1,24 @@
package fr.pandacube.lib.paper.reflect.wrapper.dataconverter;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectField;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class MCTypeRegistry extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry"));
private static final ReflectField<?> _PLAYER = wrapEx(() -> REFLECT.field("PLAYER"));
public static MCDataType PLAYER() {
return wrap(wrapReflectEx(_PLAYER::getStaticValue), MCDataType.class);
}
protected MCTypeRegistry(Object obj) {
super(obj);
}
}

View File

@ -0,0 +1,28 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ConcreteWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(GameVersion.__concrete.class)
public interface GameVersion extends ReflectWrapperI {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("com.mojang.bridge.game.GameVersion"));
public static final ReflectMethod<?> getWorldVersion = wrapEx(() -> REFLECT.method("getWorldVersion"));
default int getWorldVersion() {
return (int) wrapReflectEx(() -> getWorldVersion.invoke(__getRuntimeInstance()));
}
class __concrete extends ReflectWrapper implements GameVersion {
private __concrete(Object obj) {
super(obj);
}
}
}

View File

@ -9,7 +9,7 @@ import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
@ConcreteWrapper(WorldVersion.__concrete.class) @ConcreteWrapper(WorldVersion.__concrete.class)
public interface WorldVersion extends ReflectWrapperI { public interface WorldVersion extends GameVersion {
ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.WorldVersion")); ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.WorldVersion"));

View File

@ -91,20 +91,6 @@ public class PlayerDataWrapper {
private int getHeldItemSlot() {
if (!data.contains("SelectedItemSlot"))
return 0;
return data.getInt("SelectedItemSlot");
}
private void setHeldItemSlot(int slot) {
data.putInt("SelectedItemSlot", slot);
}
private Inventory getBukkitInventory(String nbtKey, InventoryType bukkitType, IntUnaryOperator nbtToBukkitSlotConverter) { private Inventory getBukkitInventory(String nbtKey, InventoryType bukkitType, IntUnaryOperator nbtToBukkitSlotConverter) {
Map<Integer, ItemStack> stacks = getRawInvontoryContent(nbtKey); Map<Integer, ItemStack> stacks = getRawInvontoryContent(nbtKey);
Inventory inv = Bukkit.createInventory(null, bukkitType); Inventory inv = Bukkit.createInventory(null, bukkitType);
@ -169,6 +155,56 @@ public class PlayerDataWrapper {
private int getHeldItemSlot() {
if (!data.contains("SelectedItemSlot"))
return 0;
return data.getInt("SelectedItemSlot");
}
private void setHeldItemSlot(int slot) {
data.putInt("SelectedItemSlot", slot);
}
public int getScore() {
if (!data.contains("Score"))
return 0;
return data.getInt("Score");
}
public void setScore(int score) {
data.putInt("Score", score);
}
public int getTotalExperience() {
if (!data.contains("XpTotal"))
return 0;
return data.getInt("XpTotal");
}
public void setTotalExperience(int xp) {
data.putInt("XpTotal", xp);
double levelAndExp = ExperienceUtil.getLevelFromExp(xp);
int level = (int) levelAndExp;
double expProgress = levelAndExp - level;
data.putInt("XPLevel", level);
data.putFloat("XpP", (float) expProgress);
}
private static class DummyPlayerInventory extends InventoryWrapper implements PlayerInventory { private static class DummyPlayerInventory extends InventoryWrapper implements PlayerInventory {
private int heldItemSlot; private int heldItemSlot;