Save existing vehicles if we had to load player (#22)

This commit is contained in:
Jikoo 2020-04-19 17:52:44 -04:00
parent 7210f9153e
commit a1b2df74cc
6 changed files with 269 additions and 6 deletions

View file

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>com.lishid</groupId> <groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId> <artifactId>openinvparent</artifactId>
<version>4.1.0</version> <version>4.1.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>openinvapi</artifactId> <artifactId>openinvapi</artifactId>

View file

@ -20,18 +20,26 @@ import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_14_R1.ChatComponentText; import net.minecraft.server.v1_14_R1.ChatComponentText;
import net.minecraft.server.v1_14_R1.ChatMessageType; import net.minecraft.server.v1_14_R1.ChatMessageType;
import net.minecraft.server.v1_14_R1.Container; import net.minecraft.server.v1_14_R1.Container;
import net.minecraft.server.v1_14_R1.Containers; import net.minecraft.server.v1_14_R1.Containers;
import net.minecraft.server.v1_14_R1.DimensionManager; import net.minecraft.server.v1_14_R1.DimensionManager;
import net.minecraft.server.v1_14_R1.Entity;
import net.minecraft.server.v1_14_R1.EntityHuman; import net.minecraft.server.v1_14_R1.EntityHuman;
import net.minecraft.server.v1_14_R1.EntityPlayer; import net.minecraft.server.v1_14_R1.EntityPlayer;
import net.minecraft.server.v1_14_R1.MinecraftServer; import net.minecraft.server.v1_14_R1.MinecraftServer;
import net.minecraft.server.v1_14_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_14_R1.NBTTagCompound;
import net.minecraft.server.v1_14_R1.PacketPlayOutChat; import net.minecraft.server.v1_14_R1.PacketPlayOutChat;
import net.minecraft.server.v1_14_R1.PacketPlayOutOpenWindow; import net.minecraft.server.v1_14_R1.PacketPlayOutOpenWindow;
import net.minecraft.server.v1_14_R1.PlayerInteractManager; import net.minecraft.server.v1_14_R1.PlayerInteractManager;
import net.minecraft.server.v1_14_R1.PlayerInventory; import net.minecraft.server.v1_14_R1.PlayerInventory;
import net.minecraft.server.v1_14_R1.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.Server; import org.bukkit.Server;
@ -49,6 +57,18 @@ import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager { public class PlayerDataManager implements IPlayerDataManager {
private Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
bukkitEntity = null;
}
}
@NotNull @NotNull
public static EntityPlayer getHandle(final Player player) { public static EntityPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) { if (player instanceof CraftPlayer) {
@ -78,11 +98,18 @@ public class PlayerDataManager implements IPlayerDataManager {
} }
// Create a profile and entity to load the player data // Create a profile and entity to load the player data
GameProfile profile = new GameProfile(offline.getUniqueId(), offline.getName()); GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(DimensionManager.OVERWORLD), profile, EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(DimensionManager.OVERWORLD), profile,
new PlayerInteractManager(server.getWorldServer(DimensionManager.OVERWORLD))); new PlayerInteractManager(server.getWorldServer(DimensionManager.OVERWORLD)));
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Get the bukkit entity // Get the bukkit entity
Player target = entity.getBukkitEntity(); Player target = entity.getBukkitEntity();
if (target != null) { if (target != null) {
@ -93,6 +120,63 @@ public class PlayerDataManager implements IPlayerDataManager {
return target; return target;
} }
void injectPlayer(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityPlayer)
try {
WorldNBTStorage worldNBTStorage = (WorldNBTStorage) player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = player.save(new NBTTagCompound());
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("RootVehicle", oldData.getCompound("RootVehicle"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists()) {
file1.delete();
}
file.renameTo(file1);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable @Nullable
@Override @Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {

View file

@ -20,18 +20,26 @@ import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_15_R1.ChatComponentText; import net.minecraft.server.v1_15_R1.ChatComponentText;
import net.minecraft.server.v1_15_R1.ChatMessageType; import net.minecraft.server.v1_15_R1.ChatMessageType;
import net.minecraft.server.v1_15_R1.Container; import net.minecraft.server.v1_15_R1.Container;
import net.minecraft.server.v1_15_R1.Containers; import net.minecraft.server.v1_15_R1.Containers;
import net.minecraft.server.v1_15_R1.DimensionManager; import net.minecraft.server.v1_15_R1.DimensionManager;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityHuman; import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer; import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.MinecraftServer; import net.minecraft.server.v1_15_R1.MinecraftServer;
import net.minecraft.server.v1_15_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.PacketPlayOutChat; import net.minecraft.server.v1_15_R1.PacketPlayOutChat;
import net.minecraft.server.v1_15_R1.PacketPlayOutOpenWindow; import net.minecraft.server.v1_15_R1.PacketPlayOutOpenWindow;
import net.minecraft.server.v1_15_R1.PlayerInteractManager; import net.minecraft.server.v1_15_R1.PlayerInteractManager;
import net.minecraft.server.v1_15_R1.PlayerInventory; import net.minecraft.server.v1_15_R1.PlayerInventory;
import net.minecraft.server.v1_15_R1.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.Server; import org.bukkit.Server;
@ -49,6 +57,18 @@ import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager { public class PlayerDataManager implements IPlayerDataManager {
private Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
bukkitEntity = null;
}
}
@NotNull @NotNull
public static EntityPlayer getHandle(final Player player) { public static EntityPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) { if (player instanceof CraftPlayer) {
@ -79,11 +99,19 @@ public class PlayerDataManager implements IPlayerDataManager {
} }
// Create a profile and entity to load the player data // Create a profile and entity to load the player data
GameProfile profile = new GameProfile(offline.getUniqueId(), offline.getName()); // See net.minecraft.server.PlayerList#attemptLogin
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(DimensionManager.OVERWORLD), profile, EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(DimensionManager.OVERWORLD), profile,
new PlayerInteractManager(server.getWorldServer(DimensionManager.OVERWORLD))); new PlayerInteractManager(server.getWorldServer(DimensionManager.OVERWORLD)));
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Get the bukkit entity // Get the bukkit entity
Player target = entity.getBukkitEntity(); Player target = entity.getBukkitEntity();
if (target != null) { if (target != null) {
@ -94,6 +122,63 @@ public class PlayerDataManager implements IPlayerDataManager {
return target; return target;
} }
void injectPlayer(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityPlayer)
try {
WorldNBTStorage worldNBTStorage = (WorldNBTStorage) player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = player.save(new NBTTagCompound());
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("RootVehicle", oldData.getCompound("RootVehicle"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists()) {
file1.delete();
}
file.renameTo(file1);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable @Nullable
@Override @Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
@ -111,8 +196,6 @@ public class PlayerDataManager implements IPlayerDataManager {
if (title == null) { if (title == null) {
title = "%player%'s Ender Chest"; title = "%player%'s Ender Chest";
} }
//noinspection ConstantConditions - owner name can be null if loaded by UUID
title = title.replace("%player%", owner.getName() != null ? owner.getName() : owner.getUniqueId().toString());
} else if (inventory instanceof SpecialPlayerInventory) { } else if (inventory instanceof SpecialPlayerInventory) {
EntityHuman owner = ((PlayerInventory) inventory).player; EntityHuman owner = ((PlayerInventory) inventory).player;
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player"); title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player");

View file

@ -19,11 +19,19 @@ package com.lishid.openinv.internal.v1_8_R3;
import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_8_R3.ChatComponentText; import net.minecraft.server.v1_8_R3.ChatComponentText;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer; import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.MinecraftServer; import net.minecraft.server.v1_8_R3.MinecraftServer;
import net.minecraft.server.v1_8_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.PacketPlayOutChat; import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
import net.minecraft.server.v1_8_R3.PlayerInteractManager; import net.minecraft.server.v1_8_R3.PlayerInteractManager;
import net.minecraft.server.v1_8_R3.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.Server; import org.bukkit.Server;
@ -36,6 +44,18 @@ import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager { public class PlayerDataManager implements IPlayerDataManager {
private Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
bukkitEntity = null;
}
}
@NotNull @NotNull
public static EntityPlayer getHandle(Player player) { public static EntityPlayer getHandle(Player player) {
if (player instanceof CraftPlayer) { if (player instanceof CraftPlayer) {
@ -66,11 +86,18 @@ public class PlayerDataManager implements IPlayerDataManager {
} }
// Create a profile and entity to load the player data // Create a profile and entity to load the player data
GameProfile profile = new GameProfile(offline.getUniqueId(), offline.getName()); GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), profile, EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), profile,
new PlayerInteractManager(server.getWorldServer(0))); new PlayerInteractManager(server.getWorldServer(0)));
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Get the bukkit entity // Get the bukkit entity
Player target = entity.getBukkitEntity(); Player target = entity.getBukkitEntity();
if (target != null) { if (target != null) {
@ -81,6 +108,64 @@ public class PlayerDataManager implements IPlayerDataManager {
return target; return target;
} }
void injectPlayer(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityHuman)
try {
WorldNBTStorage worldNBTStorage = (WorldNBTStorage) player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = new NBTTagCompound();
player.e(playerData);
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("Riding", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("Riding", oldData.getCompound("Riding"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueID().toString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueID().toString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists()) {
file1.delete();
}
file.renameTo(file1);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getName());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable @Nullable
@Override @Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {

View file

@ -465,6 +465,9 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
return; return;
} }
// Replace stored player with our own version
this.playerCache.put(key, this.accessor.getPlayerDataManager().inject(player));
if (this.inventories.containsKey(key)) { if (this.inventories.containsKey(key)) {
this.inventories.get(key).setPlayerOffline(); this.inventories.get(key).setPlayerOffline();
} }

View file

@ -35,6 +35,14 @@ public interface IPlayerDataManager {
@Nullable @Nullable
Player loadPlayer(@NotNull OfflinePlayer offline); Player loadPlayer(@NotNull OfflinePlayer offline);
/**
* Creates a new Player from an existing one that will function slightly better offline.
*
* @return the Player
*/
@NotNull
Player inject(@NotNull Player player);
/** /**
* Opens an ISpecialInventory for a Player. * Opens an ISpecialInventory for a Player.
* *