Fix two issues Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
616 lines
27 KiB
Diff
616 lines
27 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Sven <skbotnl@disroot.org>
|
|
Date: Tue, 15 Sep 2026 09:30:09 +0200
|
|
Subject: [PATCH] Save player data using a RocksDB database
|
|
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index 9c01005751c0088f560f96401cdfdebbbda4e7ec..4d2e77efc362c1f14209e7e51b5ac1511e2abe65 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -38,6 +38,8 @@ dependencies {
|
|
}
|
|
// Paper end
|
|
|
|
+ implementation("org.rocksdb:rocksdbjni:10.10.1.1") // TrueOG - RocksDB for player data storage
|
|
+
|
|
implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur
|
|
implementation("org.mozilla:rhino-engine:1.7.14") // Purpur
|
|
implementation("dev.omega24:upnp4j:1.0") // Purpur
|
|
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
|
|
index cdbf10339a8e8846a8c364de483a0ccd95cd225a..0d725fb135088578f4218f23c40c35f7e621f9da 100644
|
|
--- a/src/main/java/net/minecraft/server/Main.java
|
|
+++ b/src/main/java/net/minecraft/server/Main.java
|
|
@@ -73,6 +73,8 @@ import net.minecraft.SharedConstants;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
// CraftBukkit end
|
|
|
|
+import org.rocksdb.RocksDB; // TrueOG
|
|
+
|
|
public class Main {
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
@@ -111,6 +113,7 @@ public class Main {
|
|
*/ // CraftBukkit end
|
|
|
|
try {
|
|
+ RocksDB.loadLibrary(); // TrueOG
|
|
|
|
Path path = (Path) optionset.valueOf("pidFile"); // CraftBukkit
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index beb05039926e1fb7a656dfcd0c503f82db67fc46..256c9b8abd9c692b942e3be5d3aab0fe38ce8f09 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1010,6 +1010,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
LOGGER.info("Closing Thread Pool");
|
|
Util.shutdownExecutors(); // Paper
|
|
LOGGER.info("Closing Server");
|
|
+ playerDataStorage.close(); // TrueOG
|
|
try {
|
|
net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
|
|
} catch (Exception e) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
|
|
index 36af81f0957d17e170d229059c66f4eb4539dfeb..1dd0c53d464947b2d321b83917a8634cce37372b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
|
|
@@ -4,48 +4,238 @@ import com.mojang.datafixers.DataFixer;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.io.File;
|
|
import javax.annotation.Nullable;
|
|
-import net.minecraft.Util;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtIo;
|
|
import net.minecraft.nbt.NbtUtils;
|
|
-import net.minecraft.server.level.ServerPlayer;
|
|
-import net.minecraft.util.datafix.DataFixTypes;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import org.slf4j.Logger;
|
|
|
|
-// CraftBukkit start
|
|
-import java.io.FileInputStream;
|
|
-import java.io.InputStream;
|
|
-import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
-// CraftBukkit end
|
|
+// TrueOG start
|
|
+import org.rocksdb.BlockBasedTableConfig;
|
|
+import org.rocksdb.BloomFilter;
|
|
+import org.rocksdb.CompressionType;
|
|
+import org.rocksdb.ColumnFamilyDescriptor;
|
|
+import org.rocksdb.ColumnFamilyHandle;
|
|
+import org.rocksdb.ColumnFamilyOptions;
|
|
+import org.rocksdb.DBOptions;
|
|
+import org.rocksdb.Options;
|
|
+import org.rocksdb.RocksDB;
|
|
+import org.rocksdb.RocksDBException;
|
|
+import org.rocksdb.RocksIterator;
|
|
+import org.rocksdb.WriteBatch;
|
|
+import org.rocksdb.WriteOptions;
|
|
+import java.io.IOException;
|
|
+import java.io.DataInputStream;
|
|
+import java.io.DataOutputStream;
|
|
+import java.io.ByteArrayInputStream;
|
|
+import java.io.ByteArrayOutputStream;
|
|
+import java.nio.ByteBuffer;
|
|
+import java.nio.charset.StandardCharsets;
|
|
+import java.nio.file.Files;
|
|
+import java.nio.file.Path;
|
|
+import java.util.List;
|
|
+import java.util.UUID;
|
|
+import java.util.ArrayList;
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
+import java.util.concurrent.ConcurrentMap;
|
|
+import java.util.stream.Stream;
|
|
+// TrueOG end
|
|
|
|
public class PlayerDataStorage {
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
- private final File playerDir;
|
|
protected final DataFixer fixerUpper;
|
|
+ // TrueOG start
|
|
+ private final DBOptions dbOptions;
|
|
+ private final ColumnFamilyOptions columnFamilyOptions;
|
|
+ private final RocksDB rocksDB;
|
|
+ private ColumnFamilyHandle defaultColumnFamily;
|
|
+ private final ConcurrentMap<String, ColumnFamilyHandle> worldHandles = new ConcurrentHashMap<>();
|
|
+ private volatile boolean closed = false;
|
|
+ // TrueOG end
|
|
|
|
public PlayerDataStorage(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer) {
|
|
this.fixerUpper = dataFixer;
|
|
- this.playerDir = session.getLevelPath(LevelResource.PLAYER_DATA_DIR).toFile();
|
|
- this.playerDir.mkdirs();
|
|
+
|
|
+ // TrueOG start
|
|
+ try {
|
|
+ // For faster lookups
|
|
+ BlockBasedTableConfig tableConfig = new BlockBasedTableConfig().setFilterPolicy(new BloomFilter(10));
|
|
+
|
|
+ dbOptions = new DBOptions()
|
|
+ .setCreateIfMissing(true)
|
|
+ .setMaxLogFileSize(10_485_760)
|
|
+ .setKeepLogFileNum(5);
|
|
+
|
|
+ columnFamilyOptions = new ColumnFamilyOptions()
|
|
+ .setCompressionType(CompressionType.LZ4_COMPRESSION)
|
|
+ .setTableFormatConfig(tableConfig);
|
|
+
|
|
+ String path = session.getLevelPath(LevelResource.ROOT).resolve("playerdatadb").toAbsolutePath().toString();
|
|
+
|
|
+ List<ColumnFamilyDescriptor> descriptors = new ArrayList<>();
|
|
+ try (Options options = new Options().setCreateIfMissing(true)) {
|
|
+ for (byte[] name : RocksDB.listColumnFamilies(options, path)) {
|
|
+ descriptors.add(new ColumnFamilyDescriptor(name, columnFamilyOptions));
|
|
+ }
|
|
+ }
|
|
+ if (descriptors.isEmpty()) {
|
|
+ descriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, columnFamilyOptions));
|
|
+ }
|
|
+
|
|
+ List<ColumnFamilyHandle> handles = new ArrayList<>();
|
|
+ rocksDB = RocksDB.open(dbOptions, path, descriptors, handles);
|
|
+ for (int i = 0; i < descriptors.size(); i++) {
|
|
+ String name = new String(descriptors.get(i).getName(), StandardCharsets.UTF_8);
|
|
+ if (name.equals("default")) {
|
|
+ defaultColumnFamily = handles.get(i);
|
|
+ continue;
|
|
+ }
|
|
+ worldHandles.put(name, handles.get(i));
|
|
+ }
|
|
+ } catch (RocksDBException e) {
|
|
+ throw new RuntimeException(e);
|
|
+ }
|
|
+
|
|
+ migrate(session.getLevelPath(LevelResource.PLAYER_DATA_DIR));
|
|
+ // TrueOG end
|
|
}
|
|
|
|
- public void save(Player player) {
|
|
+ // TrueOG start
|
|
+ public void close() {
|
|
+ if (closed) return;
|
|
+ closed = true;
|
|
+ try {
|
|
+ for (ColumnFamilyHandle handle : worldHandles.values()) {
|
|
+ handle.close();
|
|
+ }
|
|
+ worldHandles.clear();
|
|
+
|
|
+ rocksDB.syncWal();
|
|
+ rocksDB.closeE();
|
|
+
|
|
+ columnFamilyOptions.close();
|
|
+ dbOptions.close();
|
|
+ } catch (RocksDBException e) {
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to properly close player data", e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private ColumnFamilyHandle getOrCreateWorldColumnFamily(String world) {
|
|
+ if (closed) return null;
|
|
+ String worldName = "world:" + world;
|
|
+ return worldHandles.computeIfAbsent(worldName, key -> {
|
|
+ ColumnFamilyHandle handle = null;
|
|
+ try {
|
|
+ handle = rocksDB.createColumnFamily(
|
|
+ new ColumnFamilyDescriptor(
|
|
+ key.getBytes(StandardCharsets.UTF_8),
|
|
+ columnFamilyOptions
|
|
+ )
|
|
+ );
|
|
+ } catch (RocksDBException e) {
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to create a new column for world " + world);
|
|
+ }
|
|
+ return handle;
|
|
+ });
|
|
+ }
|
|
+
|
|
+ private static byte[] getBytesFromUuid(UUID uuid) {
|
|
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
|
|
+ byteBuffer.putLong(uuid.getMostSignificantBits());
|
|
+ byteBuffer.putLong(uuid.getLeastSignificantBits());
|
|
+ return byteBuffer.array();
|
|
+ }
|
|
+
|
|
+ private static UUID getUUIDFromBytes(byte[] bytes) {
|
|
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
|
+ final long mostSigBits = byteBuffer.getLong();
|
|
+ final long leastSigBits = byteBuffer.getLong();
|
|
+ return new UUID(mostSigBits, leastSigBits);
|
|
+ }
|
|
+
|
|
+ public void migrate(Path playerDir) {
|
|
+ if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot
|
|
+ final byte[] migratedKey = "migrated".getBytes(StandardCharsets.US_ASCII);
|
|
+ if (!Files.isDirectory(playerDir)) {
|
|
+ try {
|
|
+ rocksDB.put(migratedKey, new byte[0]);
|
|
+ } catch (RocksDBException ignored) {}
|
|
+ return;
|
|
+ };
|
|
+ try {
|
|
+ if (rocksDB.get(migratedKey) != null) return;
|
|
+ } catch (RocksDBException e) {
|
|
+ throw new RuntimeException("Could not determine whether player data was migrated", e);
|
|
+ }
|
|
+ PlayerDataStorage.LOGGER.info("Migrating player data...");
|
|
+ try (Stream<Path> files = Files.list(playerDir)) {
|
|
+ boolean success = true;
|
|
+ for (Path path : files.filter(p -> p.getFileName().toString().endsWith(".dat")).toList()) {
|
|
+ File file = path.toFile();
|
|
+ if (!Files.isRegularFile(path)) continue;
|
|
+ String fileName = path.getFileName().toString();
|
|
+ String uuid = fileName.substring(0, fileName.length() - 4);
|
|
+ try {
|
|
+ final CompoundTag nbttagcompound = NbtIo.readCompressed(file);
|
|
+ final ByteArrayOutputStream playerByteArrayOutputStream = new ByteArrayOutputStream();
|
|
+ NbtIo.write(nbttagcompound, new DataOutputStream(playerByteArrayOutputStream));
|
|
+ rocksDB.put(getBytesFromUuid(UUID.fromString(uuid)), playerByteArrayOutputStream.toByteArray());
|
|
+ } catch (IOException | RocksDBException | IllegalArgumentException e) {
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to migrate player data for {}", uuid, e);
|
|
+ success = false;
|
|
+ }
|
|
+ }
|
|
+ if (!success) {
|
|
+ throw new RuntimeException("Could not migrate player data of one or more players");
|
|
+ }
|
|
+ rocksDB.put(migratedKey, new byte[0]);
|
|
+ Files.move(playerDir, playerDir.resolveSibling(playerDir.getFileName() + "migrated"));
|
|
+ PlayerDataStorage.LOGGER.info("Migrated player data");
|
|
+ } catch (IOException | RocksDBException e) {
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to migrate player data", e);
|
|
+ throw new RuntimeException("Could not successfully migrate player data", e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void saveCompoundTag(UUID uuid, CompoundTag compoundTag) {
|
|
+ if (closed) return;
|
|
if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot
|
|
try {
|
|
- CompoundTag nbttagcompound = player.saveWithoutId(new CompoundTag());
|
|
- File file = File.createTempFile(player.getStringUUID() + "-", ".dat", this.playerDir);
|
|
+ final ByteArrayOutputStream playerByteArrayOutputStream = new ByteArrayOutputStream();
|
|
+ NbtIo.write(compoundTag, new DataOutputStream(playerByteArrayOutputStream));
|
|
+ rocksDB.put(getBytesFromUuid(uuid), playerByteArrayOutputStream.toByteArray());
|
|
+ } catch (Exception exception) {
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to save player data for {}", uuid, exception);
|
|
+ }
|
|
+ }
|
|
|
|
- NbtIo.writeCompressed(nbttagcompound, file);
|
|
- File file1 = new File(this.playerDir, player.getStringUUID() + ".dat");
|
|
- File file2 = new File(this.playerDir, player.getStringUUID() + ".dat_old");
|
|
+ public void saveCompoundTagCustom(String world, UUID uuid, CompoundTag compoundTag) {
|
|
+ if (closed) return;
|
|
+ if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return;
|
|
+ try {
|
|
+ final ByteArrayOutputStream playerByteArrayOutputStream = new ByteArrayOutputStream();
|
|
+ NbtIo.write(compoundTag, new DataOutputStream(playerByteArrayOutputStream));
|
|
+ rocksDB.put(getOrCreateWorldColumnFamily(world), getBytesFromUuid(uuid), playerByteArrayOutputStream.toByteArray());
|
|
+ } catch (Exception exception) {
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to save player data for {}", uuid, exception);
|
|
+ }
|
|
+ }
|
|
|
|
- Util.safeReplaceFile(file1, file, file2);
|
|
+ public void save(Player player) {
|
|
+ CompoundTag playerData;
|
|
+ try {
|
|
+ playerData = player.saveWithoutId(new CompoundTag());
|
|
} catch (Exception exception) {
|
|
- PlayerDataStorage.LOGGER.warn("Failed to save player data for {}", player.getScoreboardName(), exception); // Paper
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to save player data for {}", player.getScoreboardName(), exception);
|
|
+ return;
|
|
}
|
|
+ saveCompoundTag(player.getUUID(), playerData);
|
|
+ }
|
|
|
|
+ private byte[] get(ColumnFamilyHandle columnFamilyHandle, byte[] uuidBytes) throws RocksDBException {
|
|
+ if (closed) return null;
|
|
+ return rocksDB.get(columnFamilyHandle, uuidBytes);
|
|
}
|
|
|
|
@Nullable
|
|
@@ -53,44 +243,34 @@ public class PlayerDataStorage {
|
|
CompoundTag nbttagcompound = null;
|
|
|
|
try {
|
|
- File file = new File(this.playerDir, player.getStringUUID() + ".dat");
|
|
- // Spigot Start
|
|
- boolean usingWrongFile = false;
|
|
- if ( org.bukkit.Bukkit.getOnlineMode() && !file.exists() ) // Paper - Check online mode first
|
|
- {
|
|
- file = new File( this.playerDir, java.util.UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + player.getScoreboardName() ).getBytes( "UTF-8" ) ).toString() + ".dat");
|
|
- if ( file.exists() )
|
|
+ byte[] playerUuidBytes = getBytesFromUuid(player.getUUID());
|
|
+ byte[] nbttagcompoundbytes = get(defaultColumnFamily, playerUuidBytes);
|
|
+
|
|
+ if (nbttagcompoundbytes == null) {
|
|
+ if (org.bukkit.Bukkit.getOnlineMode())
|
|
{
|
|
- usingWrongFile = true;
|
|
- org.bukkit.Bukkit.getServer().getLogger().warning( "Using offline mode UUID file for player " + player.getScoreboardName() + " as it is the only copy we can find." );
|
|
+ byte[] offlineUuidBytes = getBytesFromUuid(java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + player.getScoreboardName()).getBytes("UTF-8")));
|
|
+ nbttagcompoundbytes = get(defaultColumnFamily, offlineUuidBytes);
|
|
+ if (nbttagcompoundbytes == null) return null;
|
|
+
|
|
+ try (WriteBatch batch = new WriteBatch(); WriteOptions options = new WriteOptions()) {
|
|
+ batch.put(playerUuidBytes, nbttagcompoundbytes);
|
|
+ batch.delete(offlineUuidBytes);
|
|
+
|
|
+ rocksDB.write(options, batch);
|
|
+ }
|
|
+ org.bukkit.Bukkit.getServer().getLogger().warning("Using offline mode UUID file for player " + player.getScoreboardName() + " as it is the only copy we can find.");
|
|
+ } else {
|
|
+ return null;
|
|
}
|
|
}
|
|
- // Spigot End
|
|
|
|
- if (file.exists() && file.isFile()) {
|
|
- nbttagcompound = NbtIo.readCompressed(file);
|
|
- }
|
|
- // Spigot Start
|
|
- if ( usingWrongFile )
|
|
- {
|
|
- file.renameTo( new File( file.getPath() + ".offline-read" ) );
|
|
- }
|
|
- // Spigot End
|
|
+ nbttagcompound = NbtIo.read(new DataInputStream(new ByteArrayInputStream(nbttagcompoundbytes)));
|
|
} catch (Exception exception) {
|
|
PlayerDataStorage.LOGGER.warn("Failed to load player data for {}", player.getName().getString());
|
|
}
|
|
|
|
if (nbttagcompound != null) {
|
|
- // CraftBukkit start
|
|
- if (player instanceof ServerPlayer) {
|
|
- CraftPlayer player1 = (CraftPlayer) player.getBukkitEntity();
|
|
- // Only update first played if it is older than the one we have
|
|
- long modified = new File(this.playerDir, player.getUUID().toString() + ".dat").lastModified();
|
|
- if (modified < player1.getFirstPlayed()) {
|
|
- player1.setFirstPlayed(modified);
|
|
- }
|
|
- }
|
|
- // CraftBukkit end
|
|
int i = NbtUtils.getDataVersion(nbttagcompound, -1);
|
|
|
|
player.load(ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, nbttagcompound, i, net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion())); // Paper - replace player converter
|
|
@@ -99,41 +279,45 @@ public class PlayerDataStorage {
|
|
return nbttagcompound;
|
|
}
|
|
|
|
- // CraftBukkit start
|
|
- public CompoundTag getPlayerData(String s) {
|
|
- try {
|
|
- File file1 = new File(this.playerDir, s + ".dat");
|
|
|
|
- if (file1.exists()) {
|
|
- return NbtIo.readCompressed((InputStream) (new FileInputStream(file1)));
|
|
- }
|
|
+ public CompoundTag getPlayerData(UUID uuid) {
|
|
+ try {
|
|
+ byte[] data = get(defaultColumnFamily, getBytesFromUuid(uuid));
|
|
+ if (data == null) return null;
|
|
+ return NbtIo.read(new DataInputStream(new ByteArrayInputStream(data)));
|
|
} catch (Exception exception) {
|
|
- PlayerDataStorage.LOGGER.warn("Failed to load player data for " + s);
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to load player data for " + uuid);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
- // CraftBukkit end
|
|
-
|
|
- public String[] getSeenPlayers() {
|
|
- String[] astring = this.playerDir.list();
|
|
|
|
- if (astring == null) {
|
|
- astring = new String[0];
|
|
+ public CompoundTag getPlayerDataCustom(String world, UUID uuid) {
|
|
+ try {
|
|
+ ColumnFamilyHandle handle = getOrCreateWorldColumnFamily(world);
|
|
+ if (handle == null) return null;
|
|
+ byte[] data = get(handle, getBytesFromUuid(uuid));
|
|
+ if (data == null) return null;
|
|
+ return NbtIo.read(new DataInputStream(new ByteArrayInputStream(data)));
|
|
+ } catch (Exception exception) {
|
|
+ PlayerDataStorage.LOGGER.warn("Failed to load player data for " + uuid);
|
|
}
|
|
|
|
- for (int i = 0; i < astring.length; ++i) {
|
|
- if (astring[i].endsWith(".dat")) {
|
|
- astring[i] = astring[i].substring(0, astring[i].length() - 4);
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public String[] getSeenPlayers() {
|
|
+ if (closed) return new String[0];
|
|
+ final ArrayList<String> seenPlayers = new ArrayList<>();
|
|
+ try (RocksIterator it = rocksDB.newIterator()) {
|
|
+ for (it.seekToFirst(); it.isValid(); it.next()) {
|
|
+ byte[] key = it.key();
|
|
+ if (key.length != 16) continue;
|
|
+ seenPlayers.add(getUUIDFromBytes(key).toString());
|
|
}
|
|
}
|
|
|
|
- return astring;
|
|
- }
|
|
-
|
|
- // CraftBukkit start
|
|
- public File getPlayerDir() {
|
|
- return this.playerDir;
|
|
+ return seenPlayers.toArray(new String[0]);
|
|
}
|
|
- // CraftBukkit end
|
|
+ // TrueOG end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
index 312a6d90c0a09570aef24c205dc2ff277dcd4279..d0e5c8163530f2c530f492a23170b286049cfb98 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
|
@@ -1,7 +1,6 @@
|
|
package org.bukkit.craftbukkit;
|
|
|
|
import com.mojang.authlib.GameProfile;
|
|
-import java.io.File;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
@@ -188,7 +187,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
}
|
|
|
|
private CompoundTag getData() {
|
|
- return this.storage.getPlayerData(this.getUniqueId().toString());
|
|
+ return this.storage.getPlayerData(this.getUniqueId());
|
|
}
|
|
|
|
private CompoundTag getBukkitData() {
|
|
@@ -204,10 +203,6 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
return result;
|
|
}
|
|
|
|
- private File getDataFile() {
|
|
- return new File(this.storage.getPlayerDir(), this.getUniqueId() + ".dat");
|
|
- }
|
|
-
|
|
@Override
|
|
public long getFirstPlayed() {
|
|
Player player = this.getPlayer();
|
|
@@ -215,13 +210,8 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
|
|
CompoundTag data = this.getBukkitData();
|
|
|
|
- if (data != null) {
|
|
- if (data.contains("firstPlayed")) {
|
|
- return data.getLong("firstPlayed");
|
|
- } else {
|
|
- File file = this.getDataFile();
|
|
- return file.lastModified();
|
|
- }
|
|
+ if (data != null && data.contains("firstPlayed")) { // TrueOG
|
|
+ return data.getLong("firstPlayed");
|
|
} else {
|
|
return 0;
|
|
}
|
|
@@ -234,13 +224,8 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
|
|
CompoundTag data = this.getBukkitData();
|
|
|
|
- if (data != null) {
|
|
- if (data.contains("lastPlayed")) {
|
|
- return data.getLong("lastPlayed");
|
|
- } else {
|
|
- File file = this.getDataFile();
|
|
- return file.lastModified();
|
|
- }
|
|
+ if (data != null && data.contains("lastPlayed")) { // TrueOG
|
|
+ return data.getLong("lastPlayed");
|
|
} else {
|
|
return 0;
|
|
}
|
|
@@ -259,14 +244,8 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
|
|
CompoundTag data = getPaperData();
|
|
|
|
- if (data != null) {
|
|
- if (data.contains("LastLogin")) {
|
|
- return data.getLong("LastLogin");
|
|
- } else {
|
|
- // if the player file cannot provide accurate data, this is probably the closest we can approximate
|
|
- File file = getDataFile();
|
|
- return file.lastModified();
|
|
- }
|
|
+ if (data != null && data.contains("LastLogin")) { // TrueOG
|
|
+ return data.getLong("LastLogin");
|
|
} else {
|
|
return 0;
|
|
}
|
|
@@ -279,14 +258,8 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
|
|
CompoundTag data = getPaperData();
|
|
|
|
- if (data != null) {
|
|
- if (data.contains("LastSeen")) {
|
|
- return data.getLong("LastSeen");
|
|
- } else {
|
|
- // if the player file cannot provide accurate data, this is probably the closest we can approximate
|
|
- File file = getDataFile();
|
|
- return file.lastModified();
|
|
- }
|
|
+ if (data != null && data.contains("LastSeen")) { // TrueOG
|
|
+ return data.getLong("LastSeen");
|
|
} else {
|
|
return 0;
|
|
}
|
|
@@ -746,16 +719,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
|
* @param compoundTag
|
|
*/
|
|
private void save(CompoundTag compoundTag) {
|
|
- File playerDir = server.console.playerDataStorage.getPlayerDir();
|
|
- try {
|
|
- File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir);
|
|
- net.minecraft.nbt.NbtIo.writeCompressed(compoundTag, tempFile);
|
|
- File playerDataFile = new File(playerDir, this.getUniqueId()+".dat");
|
|
- File playerDataFileOld = new File(playerDir, this.getUniqueId()+".dat_old");
|
|
- net.minecraft.Util.safeReplaceFile(playerDataFile, tempFile, playerDataFileOld);
|
|
- } catch (java.io.IOException e) {
|
|
- e.printStackTrace();
|
|
- }
|
|
+ storage.saveCompoundTag(this.getUniqueId(), compoundTag); // TrueOG
|
|
}
|
|
// Purpur end - OfflinePlayer API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index 3310828edcabd2c24e3200dcb89d4e8ebd82cf16..cce3eb21545a0f6e3d5bed9308b9768185984e72 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -194,7 +194,6 @@ import org.bukkit.craftbukkit.util.CraftLocation;
|
|
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
|
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
|
import org.bukkit.craftbukkit.util.CraftSpawnCategory;
|
|
-import org.bukkit.craftbukkit.util.DatFileFilter;
|
|
import org.bukkit.craftbukkit.util.Versioning;
|
|
import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions;
|
|
import org.bukkit.entity.Entity;
|
|
@@ -2126,16 +2125,17 @@ public final class CraftServer implements Server {
|
|
@Override
|
|
public OfflinePlayer[] getOfflinePlayers() {
|
|
PlayerDataStorage storage = console.playerDataStorage;
|
|
- String[] files = storage.getPlayerDir().list(new DatFileFilter());
|
|
Set<OfflinePlayer> players = new HashSet<OfflinePlayer>();
|
|
|
|
- for (String file : files) {
|
|
+ // TrueOG start
|
|
+ for (String uuid : storage.getSeenPlayers()) {
|
|
try {
|
|
- players.add(this.getOfflinePlayer(UUID.fromString(file.substring(0, file.length() - 4))));
|
|
+ players.add(this.getOfflinePlayer(UUID.fromString(uuid)));
|
|
} catch (IllegalArgumentException ex) {
|
|
- // Who knows what is in this directory, just ignore invalid files
|
|
+ // Ignore invalid UUIDs
|
|
}
|
|
}
|
|
+ // TrueOG end
|
|
|
|
players.addAll(this.getOnlinePlayers());
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java b/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java
|
|
deleted file mode 100644
|
|
index 66ff685d707961757de553515f634af21ec21f82..0000000000000000000000000000000000000000
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java
|
|
+++ /dev/null
|
|
@@ -1,11 +0,0 @@
|
|
-package org.bukkit.craftbukkit.util;
|
|
-
|
|
-import java.io.File;
|
|
-import java.io.FilenameFilter;
|
|
-
|
|
-public class DatFileFilter implements FilenameFilter {
|
|
- @Override
|
|
- public boolean accept(File dir, String name) {
|
|
- return name.endsWith(".dat");
|
|
- }
|
|
-}
|