Update reflection in NMS/OBC

This commit is contained in:
Marc Baloup 2024-06-15 13:15:50 +02:00
parent 7f56645ba5
commit cef4af80f0
10 changed files with 110 additions and 53 deletions

View File

@ -2,12 +2,8 @@ package fr.pandacube.lib.paper.players;
import fr.pandacube.lib.paper.reflect.util.PrimaryWorlds;
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.NbtIo;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.Tag;
import fr.pandacube.lib.paper.util.PlayerDataWrapper;
import fr.pandacube.lib.paper.world.WorldUtil;
import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
@ -154,29 +150,17 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
/**
* Gets the NBT data from the player-data file.
* 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 player-data file, or null if the file does not exists.
* @return the NBT data from the player-data file, or null if the file does not exist.
* @throws IllegalStateException if the player is online.
* @throws IOException if an error occurs reading the data.
*/
default CompoundTag getPlayerData(boolean convertTag) throws IOException {
default CompoundTag getPlayerData() {
if (isOnline())
throw new IllegalStateException("Cannot access data file of " + getName() + " because theyre online.");
CompoundTag data = ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class)
return ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class)
.getServer()
.getPlayerList()
.playerIo()
.getPlayerData(getUniqueId().toString());
if (data != null && convertTag) {
int srcVersion = data.contains("DataVersion", Tag.TAG_ANY_NUMERIC()) ? data.getInt("DataVersion") : -1;
int destVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
try {
data = MCDataConverter.convertTag(MCTypeRegistry.PLAYER(), data, srcVersion, destVersion);
} catch (Exception e) {
throw new IOException("Unable to upgrade data format of player " + getName() + " (" + getUniqueId() + ") from version " + destVersion + " to " + destVersion);
}
}
return data;
.load(getName(), getUniqueId().toString()).orElse(null);
}
/**
@ -184,10 +168,9 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
* It will not work if the player is online, because the data on the file are not synchronized with real-time values.
* @return the NBT data from the player-data file.
* @throws IllegalStateException if the player is online.
* @throws IOException if an error occurs reading the data.
*/
default PlayerDataWrapper getPlayerDataWrapper() throws IOException {
return new PlayerDataWrapper(getPlayerData(true));
default PlayerDataWrapper getPlayerDataWrapper() {
return new PlayerDataWrapper(getPlayerData());
}
/**
@ -213,25 +196,23 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
* @return the file where the player-data is stored.
*/
default File getPlayerDataFile(boolean old) {
File playerDataDir = new File(WorldUtil.worldDir(PrimaryWorlds.PRIMARY_WORLDS.get(0)), "playerdata");
File playerDataDir = new File(WorldUtil.worldDir(PrimaryWorlds.PRIMARY_WORLDS.getFirst()), "playerdata");
return new File(playerDataDir, getUniqueId() + (old ? ".dat_old" : ".dat"));
}
/**
* Gets the players inventory.
* @return the players inventory.
* @throws IOException if an error occurs reading the data.
*/
default PlayerInventory getInventory() throws IOException {
default PlayerInventory getInventory() {
return getPlayerDataWrapper().getInventory();
}
/**
* Gets the players enderchest.
* @return the players enderchest.
* @throws IOException if an error occurs reading the data.
*/
default Inventory getEnderChest() throws IOException {
default Inventory getEnderChest() {
return getPlayerDataWrapper().getEnderChest();
}

View File

@ -24,6 +24,8 @@ import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.GameProfileArgu
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.ResourceLocationArgument;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Vec3Argument;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.BlockPos;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.HolderLookupProvider;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.RegistryAccess;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.Vec3i;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CollectionTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
@ -130,6 +132,8 @@ public class PandalibPaperReflect {
thAcc.catchThrowable(() -> initWrapper(Vec3Argument.class, Vec3Argument.REFLECT.get()));
// minecraft.core
thAcc.catchThrowable(() -> initWrapper(BlockPos.class, BlockPos.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(HolderLookupProvider.class, HolderLookupProvider.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(RegistryAccess.class, RegistryAccess.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(Vec3i.class, Vec3i.REFLECT.get()));
// minecraft.nbt
thAcc.catchThrowable(() -> initWrapper(CollectionTag.class, CollectionTag.REFLECT.get()));

View File

@ -1,7 +1,6 @@
package fr.pandacube.lib.paper.reflect.wrapper.craftbukkit;
import fr.pandacube.lib.paper.reflect.OBCReflect;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
@ -20,11 +19,6 @@ public class CraftItemStack extends ReflectWrapperTyped<ItemStack> {
}
public static ItemStack asCraftMirror(CompoundTag nbt) {
return asCraftMirror(fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.of(nbt));
}
public static fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack asNMSCopy(ItemStack original) {
return wrap(wrapReflectEx(() -> asNMSCopy.invokeStatic(original)), fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.class);
}

View File

@ -0,0 +1,18 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.core;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
public interface HolderLookupProvider extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.core.HolderLookup.Provider"));
class __concrete extends ReflectWrapper implements HolderLookupProvider {
protected __concrete(Object obj) {
super(obj);
}
}
}

View File

@ -0,0 +1,18 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.core;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.protocol.custom.CustomPacketPayload;
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 interface RegistryAccess extends HolderLookupProvider {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.core.RegistryAccess"));
class __concrete extends ReflectWrapper implements RegistryAccess {
protected __concrete(Object obj) {
super(obj);
}
}
}

View File

@ -51,7 +51,7 @@ public class CompoundTag extends ReflectWrapper implements Tag {
private static final ReflectMethod<?> get = wrapEx(() -> REFLECT.method("get", String.class));
private static final ReflectMethod<?> getAllKeys = wrapEx(() -> REFLECT.method("getAllKeys"));
private static final ReflectMethod<?> entries = wrapEx(() -> REFLECT.method("entries"));
private static final ReflectMethod<?> entrySet = wrapEx(() -> REFLECT.method("entrySet"));
private static final ReflectMethod<?> size = wrapEx(() -> REFLECT.method("size"));
private static final ReflectMethod<?> contains = wrapEx(() -> REFLECT.method("contains", String.class));
private static final ReflectMethod<?> containsStringInt = wrapEx(() -> REFLECT.method("contains", String.class, int.class));
@ -166,9 +166,9 @@ public class CompoundTag extends ReflectWrapper implements Tag {
* The values in the returned Map are not wrapped.
*/
@SuppressWarnings("unchecked")
public Map<String, ?> entries() {
public Set<Map.Entry<String, ?>> entrySet() {
// we cannot easily wrap every value of the map without being able to synchronize the returned map with the wrapped map
return (Map<String, ?>) wrapReflectEx(() -> entries.invoke(__getRuntimeInstance()));
return (Set<Map.Entry<String, ?>>) wrapReflectEx(() -> entrySet.invoke(__getRuntimeInstance()));
}
public int size() {
return (int) wrapReflectEx(() -> size.invoke(__getRuntimeInstance()));

View File

@ -1,5 +1,6 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.server;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.RegistryAccess;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
@ -11,11 +12,16 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class MinecraftServer extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.server.MinecraftServer"));
private static final ReflectMethod<?> getPlayerList = wrapEx(() -> REFLECT.method("getPlayerList"));
public static final ReflectMethod<?> registryAccess = wrapEx(() -> REFLECT.method("registryAccess"));
public PlayerList getPlayerList() {
return wrap(wrapReflectEx(() -> getPlayerList.invoke(__getRuntimeInstance())), PlayerList.class);
}
public RegistryAccess registryAccess() {
return wrap(wrapReflectEx(() -> registryAccess.invoke(__getRuntimeInstance())), RegistryAccess.class);
}
protected MinecraftServer(Object obj) {
super(obj);
}

View File

@ -1,21 +1,35 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.HolderLookupProvider;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.RegistryAccess;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.Tag;
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 org.bukkit.Bukkit;
import java.util.Optional;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class ItemStack extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.item.ItemStack"));
private static final ReflectMethod<?> of = wrapEx(() -> REFLECT.method("of", CompoundTag.REFLECT.get()));
private static final ReflectMethod<?> save = wrapEx(() -> REFLECT.method("save", CompoundTag.REFLECT.get()));
private static final ReflectMethod<?> parse = wrapEx(() -> REFLECT.method("parse", HolderLookupProvider.REFLECT.get(), Tag.REFLECT.get()));
private static final ReflectMethod<?> saveWithPrefix = wrapEx(() -> REFLECT.method("save", HolderLookupProvider.REFLECT.get(), Tag.REFLECT.get()));
private static final ReflectMethod<?> save = wrapEx(() -> REFLECT.method("save", HolderLookupProvider.REFLECT.get()));
public static ItemStack of(CompoundTag nbt) {
return wrap(wrapReflectEx(() -> of.invokeStatic(unwrap(nbt))), ItemStack.class);
@SuppressWarnings("unchecked")
public static Optional<ItemStack> parse(HolderLookupProvider registries, Tag nbt) {
return ((Optional<Object>) wrapReflectEx(() -> parse.invokeStatic(unwrap(registries), unwrap(nbt))))
.map(o -> wrap(o, ItemStack.class));
}
public static Optional<ItemStack> parse(Tag nbt) {
return parse(getRegistries(), nbt);
}
@ -24,7 +38,26 @@ public class ItemStack extends ReflectWrapper {
}
public CompoundTag save(CompoundTag nbt) {
return wrap(wrapReflectEx(() -> save.invoke(__getRuntimeInstance(), unwrap(nbt))), CompoundTag.class);
public Tag save(HolderLookupProvider registries, Tag prefix) {
return wrap(wrapReflectEx(() -> saveWithPrefix.invoke(__getRuntimeInstance(), unwrap(registries), unwrap(prefix))), Tag.class);
}
public Tag save(HolderLookupProvider registries) {
return wrap(wrapReflectEx(() -> save.invoke(__getRuntimeInstance(), unwrap(registries))), Tag.class);
}
public Tag save(Tag prefix) {
return save(getRegistries(), prefix);
}
public Tag save() {
return save(getRegistries());
}
private static RegistryAccess getRegistries() {
return wrap(Bukkit.getServer(), CraftServer.class).getServer().registryAccess();
}
}

View File

@ -6,18 +6,23 @@ import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import java.util.Optional;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class PlayerDataStorage extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.PlayerDataStorage"));
public static final ReflectMethod<?> getPlayerData = wrapEx(() -> REFLECT.method("getPlayerData", String.class)); // Craftbukkit method
public static final ReflectMethod<?> load = wrapEx(() -> REFLECT.method("load", String.class, String.class));
/**
* @param playerName the name of the player: used for loading error message and of offline UUID generation.
* @param playerId UUID of a player as it is used to name the player data file (UUID.toString())
*/
public CompoundTag getPlayerData(String playerId) {
return wrap(wrapReflectEx(() -> getPlayerData.invoke(__getRuntimeInstance(), playerId)), CompoundTag.class);
@SuppressWarnings("unchecked")
public Optional<CompoundTag> load(String playerName, String playerId) {
return ((Optional<Object>) wrapReflectEx(() -> load.invoke(__getRuntimeInstance(), playerId)))
.map(o -> wrap(o, CompoundTag.class));
}

View File

@ -116,10 +116,9 @@ public record PlayerDataWrapper(CompoundTag data) {
for (int i = 0; i < list.size(); i++) {
CompoundTag itemTag = list.getCompound(i);
int nbtSlot = itemTag.getByte("Slot") & 255;
ItemStack is = filterStack(CraftItemStack.asCraftMirror(itemTag));
if (is != null) {
stacks.put(nbtSlot, CraftItemStack.asCraftMirror(itemTag));
}
fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.parse(itemTag)
.map(nms -> filterStack(CraftItemStack.asCraftMirror(nms)))
.ifPresent(is -> stacks.put(nbtSlot, is));
}
return stacks;
}
@ -148,9 +147,8 @@ public record PlayerDataWrapper(CompoundTag data) {
if (stack == null)
continue;
CompoundTag itemTag = new CompoundTag();
CraftItemStack.asNMSCopy(is.getValue()).save(itemTag);
itemTag.putByte("Slot", is.getKey().byteValue());
list.add(list.size(), itemTag);
list.add(list.size(), CraftItemStack.asNMSCopy(is.getValue()).save(itemTag));
}
data.put(key, list);
}