diff --git a/internal/v1_20_R1/pom.xml b/internal/v1_20_R3/pom.xml
similarity index 94%
rename from internal/v1_20_R1/pom.xml
rename to internal/v1_20_R3/pom.xml
index 0ac9f36b..45af58dc 100644
--- a/internal/v1_20_R1/pom.xml
+++ b/internal/v1_20_R3/pom.xml
@@ -26,13 +26,13 @@
4.4.2-SNAPSHOT
- openinvadapter1_20_R1
- OpenInvAdapter1_20_R1
+ openinvadapter1_20_R3
+ OpenInvAdapter1_20_R3
17
17
- 1.20.1-R0.1-SNAPSHOT
+ 1.20.4-R0.1-SNAPSHOT
diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java
similarity index 99%
rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/AnySilentContainer.java
rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java
index 459f9693..ea492c70 100644
--- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/AnySilentContainer.java
+++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java
@@ -14,13 +14,11 @@
* along with this program. If not, see .
*/
-package com.lishid.openinv.internal.v1_20_R1;
+package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
-import java.lang.reflect.Field;
-import java.util.logging.Logger;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
@@ -49,11 +47,14 @@
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.ShulkerBox;
-import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Field;
+import java.util.logging.Logger;
+
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java
similarity index 85%
rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java
rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java
index 6d70e3db..23ed6bb9 100644
--- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java
+++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java
@@ -14,7 +14,7 @@
* along with this program. If not, see .
*/
-package com.lishid.openinv.internal.v1_20_R1;
+package com.lishid.openinv.internal.v1_20_R3;
import com.mojang.logging.LogUtils;
import net.minecraft.Util;
@@ -25,12 +25,13 @@
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.PlayerDataStorage;
-import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
-import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
+import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
public class OpenPlayer extends CraftPlayer {
@@ -40,11 +41,7 @@ public OpenPlayer(CraftServer server, ServerPlayer entity) {
@Override
public void loadData() {
- // See CraftPlayer#loadData
- CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
- if (loaded != null) {
- getHandle().readAdditionalSaveData(loaded);
- }
+ PlayerDataManager.loadData(getHandle());
}
@Override
@@ -63,11 +60,12 @@ public void saveData() {
revertSpecialValues(playerData, oldData);
}
- File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
+ Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
+ Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
NbtIo.writeCompressed(playerData, file);
- File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
- File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
- Util.safeReplaceFile(file1, file, file2);
+ Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
+ Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
+ Util.safeReplaceFile(dataFile, file, backupFile);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/PlayerDataManager.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java
similarity index 59%
rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/PlayerDataManager.java
rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java
index 710e1987..240fafa5 100644
--- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/PlayerDataManager.java
+++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/PlayerDataManager.java
@@ -14,47 +14,68 @@
* along with this program. If not, see .
*/
-package com.lishid.openinv.internal.v1_20_R1;
+package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
-import java.lang.reflect.Field;
-import java.util.logging.Logger;
+import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
+import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
-import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
-import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory;
-import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftContainer;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
+import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
+import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
+import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Field;
+import java.util.UUID;
+import java.util.logging.Logger;
+
public class PlayerDataManager implements IPlayerDataManager {
+ private static boolean paper;
+
+ static {
+ try {
+ Class.forName("io.papermc.paper.configuration.Configuration");
+ paper = true;
+ } catch (ClassNotFoundException ignored) {
+ paper = false;
+ }
+ }
+
private @Nullable Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
- Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
- logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
+ Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger();
+ logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!");
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
bukkitEntity = null;
}
@@ -74,7 +95,7 @@ public PlayerDataManager() {
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
- throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
+ throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
}
return nmsPlayer;
@@ -87,11 +108,6 @@ public PlayerDataManager() {
return null;
}
- // Create a profile and entity to load the player data
- // See net.minecraft.server.players.PlayerList#canPlayerLogin
- // and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello
- GameProfile profile = new GameProfile(offline.getUniqueId(),
- offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
@@ -99,39 +115,103 @@ public PlayerDataManager() {
return null;
}
- ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
+ // Create a new ServerPlayer.
+ ServerPlayer entity = createNewPlayer(server, worldServer, offline);
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
entity.getAdvancements().stopListening();
+ // Try to load the player's data.
+ if (loadData(entity)) {
+ // If data is loaded successfully, return the Bukkit entity.
+ return entity.getBukkitEntity();
+ }
+
+ return null;
+ }
+
+ private @NotNull ServerPlayer createNewPlayer(
+ @NotNull MinecraftServer server,
+ @NotNull ServerLevel worldServer,
+ @NotNull final OfflinePlayer offline) {
+ // See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile)
+ // See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket)
+ GameProfile profile = new GameProfile(offline.getUniqueId(),
+ offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
+
+ ClientInformation dummyInfo = new ClientInformation(
+ "en_us",
+ 1, // Reduce distance just in case.
+ ChatVisiblity.HIDDEN, // Don't accept chat.
+ false,
+ ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION,
+ ServerPlayer.DEFAULT_MAIN_HAND,
+ true,
+ false // Don't list in player list (not that this player is in the list anyway).
+ );
+
+ ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
+
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
+ java.util.logging.Level.WARNING,
+ e,
+ () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
}
- // Load data. This also reads basic data into the player.
+ return entity;
+ }
+
+ static boolean loadData(@NotNull ServerPlayer player) {
// See CraftPlayer#loadData
- CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
+ CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player);
if (loadedData == null) {
// Exceptions with loading are logged by Mojang.
- return null;
+ return false;
}
+ // Read basic data into the player.
+ player.load(loadedData);
// Also read "extra" data.
- entity.readAdditionalSaveData(loadedData);
+ player.readAdditionalSaveData(loadedData);
- if (entity.level() == null) {
- // Paper: Move player to spawn
- entity.spawnIn(null);
+ if (paper) {
+ // Paper: world is not loaded by ServerPlayer#load(CompoundTag).
+ parseWorld(player, loadedData);
}
- // Return the Bukkit entity.
- return entity.getBukkitEntity();
+ return true;
+ }
+
+ private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) {
+ // See PlayerList#placeNewPlayer
+ World bukkitWorld;
+ if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) {
+ // Modern Bukkit world.
+ bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast")));
+ } else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) {
+ // Legacy Bukkit world.
+ bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world"));
+ } else {
+ // Vanilla player data.
+ DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension")))
+ .resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning)
+ .map(player.server::getLevel)
+ // If ServerLevel exists, set, otherwise move to spawn.
+ .ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null));
+ return;
+ }
+ if (bukkitWorld == null) {
+ player.spawnIn(null);
+ return;
+ }
+ player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}
- void injectPlayer(ServerPlayer player) throws IllegalAccessException {
+ private void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
@@ -141,9 +221,8 @@ void injectPlayer(ServerPlayer player) throws IllegalAccessException {
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}
- @NotNull
@Override
- public Player inject(@NotNull Player player) {
+ public @NotNull Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
@@ -152,14 +231,16 @@ public Player inject(@NotNull Player player) {
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
+ java.util.logging.Level.WARNING,
+ e,
+ () -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
return player;
}
}
- @Nullable
@Override
- public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
+ public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ServerPlayer nmsPlayer = getHandle(player);
diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialEnderChest.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java
similarity index 98%
rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialEnderChest.java
rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java
index 3c66f9cd..941e7979 100644
--- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialEnderChest.java
+++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialEnderChest.java
@@ -14,12 +14,9 @@
* along with this program. If not, see .
*/
-package com.lishid.openinv.internal.v1_20_R1;
+package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
@@ -33,13 +30,17 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
-import org.bukkit.craftbukkit.v1_20_R1.entity.CraftHumanEntity;
-import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialPlayerInventory.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java
similarity index 99%
rename from internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialPlayerInventory.java
rename to internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java
index aef0914a..2ce13a58 100644
--- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/SpecialPlayerInventory.java
+++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/SpecialPlayerInventory.java
@@ -14,16 +14,10 @@
* along with this program. If not, see .
*/
-package com.lishid.openinv.internal.v1_20_R1;
+package com.lishid.openinv.internal.v1_20_R3;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
@@ -47,13 +41,20 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
-import org.bukkit.craftbukkit.v1_20_R1.entity.CraftHumanEntity;
-import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
diff --git a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java
index 2bf529a3..ff2c716a 100644
--- a/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java
+++ b/plugin/src/main/java/com/lishid/openinv/InternalAccessor.java
@@ -22,11 +22,12 @@
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.util.InventoryAccess;
-import java.lang.reflect.Constructor;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
+import java.lang.reflect.Constructor;
+
class InternalAccessor {
private final @NotNull Plugin plugin;
@@ -66,6 +67,7 @@ public String getReleasesLink() {
case "v1_17_R1", "v1_18_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10";
case "v1_19_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2";
case "v1_18_R2", "v1_19_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0";
+ case "v1_20_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1";
default -> "https://github.com/Jikoo/OpenInv/releases";
};
}
diff --git a/pom.xml b/pom.xml
index d55b7657..6126bc82 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,8 +41,8 @@
api
plugin
internal/v1_19_R3
- internal/v1_20_R1
internal/v1_20_R2
+ internal/v1_20_R3
assembly