diff --git a/Spigot-API-Patches/0170-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch b/Spigot-API-Patches/0170-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch new file mode 100644 index 000000000..7680a5eb9 --- /dev/null +++ b/Spigot-API-Patches/0170-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch @@ -0,0 +1,65 @@ +From aa3efefb9b8b1ffba66ad5ea00c9acd76c6d6a49 Mon Sep 17 00:00:00 2001 +From: Zach Brown +Date: Wed, 2 Jan 2019 00:31:12 -0600 +Subject: [PATCH] Add APIs to replace OfflinePlayer#getLastPlayed + +Currently OfflinePlayer#getLastPlayed could more accurately be described +as "OfflinePlayer#getLastTimeTheirDataWasSaved". + +The API doc says it should return the last time the server "witnessed" +the player, whilst also saying it should return the last time they +logged in. The current implementation does neither. + +Given this interesting contradiction in the API documentation and the +current defacto implementation, I've elected to deprecate (with no +intent to remove) and replace it with two new methods, clearly named and +documented as to their purpose. + +diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java +index 658eac26..335d3803 100644 +--- a/src/main/java/org/bukkit/OfflinePlayer.java ++++ b/src/main/java/org/bukkit/OfflinePlayer.java +@@ -135,7 +135,9 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio + * UTC. + * + * @return Date of last log-in for this player, or 0 ++ * @deprecated The API contract is ambiguous and the implementation may or may not return the correct value given this API ambiguity. It is instead recommended use {@link #getLastLogin()} or {@link #getLastSeen()} depending on your needs. + */ ++ @Deprecated + public long getLastPlayed(); + + /** +@@ -153,4 +155,30 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio + */ + public Location getBedSpawnLocation(); + ++ // Paper start ++ ++ /** ++ * Gets the last date and time that this player logged into the server. ++ *

++ * If the player has never played before, this will return 0. Otherwise, ++ * it will be the amount of milliseconds since midnight, January 1, 1970 ++ * UTC. ++ * ++ * @return last login time ++ */ ++ public long getLastLogin(); ++ ++ /** ++ * Gets the last date and time that this player was seen on the server. ++ *

++ * If the player has never played before, this will return 0. If the ++ * player is currently online, this will return the current time. ++ * Otherwise it will be the amount of milliseconds since midnight, ++ * January 1, 1970 UTC. ++ * ++ * @return last seen time ++ */ ++ public long getLastSeen(); ++ // Paper end ++ + } +-- +2.20.1 + diff --git a/Spigot-Server-Patches/0414-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch b/Spigot-Server-Patches/0414-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch new file mode 100644 index 000000000..d14a5de40 --- /dev/null +++ b/Spigot-Server-Patches/0414-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch @@ -0,0 +1,167 @@ +From 5c60a4f213a6d16a66551b60c147fb46b86d1b95 Mon Sep 17 00:00:00 2001 +From: Zach Brown +Date: Wed, 2 Jan 2019 00:35:43 -0600 +Subject: [PATCH] Add APIs to replace OfflinePlayer#getLastPlayed + +Currently OfflinePlayer#getLastPlayed could more accurately be described +as "OfflinePlayer#getLastTimeTheirDataWasSaved". + +The API doc says it should return the last time the server "witnessed" +the player, whilst also saying it should return the last time they +logged in. The current implementation does neither. + +Given this interesting contradiction in the API documentation and the +current defacto implementation, I've elected to deprecate (with no +intent to remove) and replace it with two new methods, clearly named and +documented as to their purpose. + +diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java +index 1c90f2f8..32557caf 100644 +--- a/src/main/java/net/minecraft/server/EntityPlayer.java ++++ b/src/main/java/net/minecraft/server/EntityPlayer.java +@@ -72,6 +72,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + public int ping; + public boolean viewingCredits; + private int containerUpdateDelay; // Paper ++ public long loginTime; // Paper + // Paper start - cancellable death event + public boolean queueHealthUpdatePacket = false; + public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket; +diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java +index f3234025..61724808 100644 +--- a/src/main/java/net/minecraft/server/PlayerList.java ++++ b/src/main/java/net/minecraft/server/PlayerList.java +@@ -95,6 +95,7 @@ public abstract class PlayerList { + } + + public void a(NetworkManager networkmanager, EntityPlayer entityplayer) { ++ entityplayer.loginTime = System.currentTimeMillis(); // Paper + GameProfile gameprofile = entityplayer.getProfile(); + UserCache usercache = this.server.getUserCache(); + GameProfile gameprofile1 = usercache.a(gameprofile.getId()); +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +index fbdb2df2..e1973c5d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +@@ -245,6 +245,61 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa + return getData() != null; + } + ++ // Paper start ++ @Override ++ public long getLastLogin() { ++ Player player = getPlayer(); ++ if (player != null) return player.getLastLogin(); ++ ++ NBTTagCompound data = getPaperData(); ++ ++ if (data != null) { ++ if (data.hasKey("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(); ++ } ++ } else { ++ return 0; ++ } ++ } ++ ++ @Override ++ public long getLastSeen() { ++ Player player = getPlayer(); ++ if (player != null) return player.getLastSeen(); ++ ++ NBTTagCompound data = getPaperData(); ++ ++ if (data != null) { ++ if (data.hasKey("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(); ++ } ++ } else { ++ return 0; ++ } ++ } ++ ++ private NBTTagCompound getPaperData() { ++ NBTTagCompound result = getData(); ++ ++ if (result != null) { ++ if (!result.hasKey("Paper")) { ++ result.set("Paper", new NBTTagCompound()); ++ } ++ result = result.getCompound("Paper"); ++ } ++ ++ return result; ++ } ++ // Paper end ++ + public Location getBedSpawnLocation() { + NBTTagCompound data = getData(); + if (data == null) return null; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 1c9cc125..e05867ed 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -134,6 +134,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + private org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; + private String resourcePackHash; + private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit ++ private long lastSaveTime; + // Paper end + + public CraftPlayer(CraftServer server, EntityPlayer entity) { +@@ -1363,6 +1364,18 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + this.firstPlayed = firstPlayed; + } + ++ // Paper start ++ @Override ++ public long getLastLogin() { ++ return getHandle().loginTime; ++ } ++ ++ @Override ++ public long getLastSeen() { ++ return isOnline() ? System.currentTimeMillis() : this.lastSaveTime; ++ } ++ // Paper end ++ + public void readExtraData(NBTTagCompound nbttagcompound) { + hasPlayedBefore = true; + if (nbttagcompound.hasKey("bukkit")) { +@@ -1385,6 +1398,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + + public void setExtraData(NBTTagCompound nbttagcompound) { ++ this.lastSaveTime = System.currentTimeMillis(); // Paper ++ + if (!nbttagcompound.hasKey("bukkit")) { + nbttagcompound.set("bukkit", new NBTTagCompound()); + } +@@ -1399,6 +1414,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + data.setLong("firstPlayed", getFirstPlayed()); + data.setLong("lastPlayed", System.currentTimeMillis()); + data.setString("lastKnownName", handle.getName()); ++ ++ // Paper start - persist for use in offline save data ++ if (!nbttagcompound.hasKey("Paper")) { ++ nbttagcompound.set("Paper", new NBTTagCompound()); ++ } ++ ++ NBTTagCompound paper = nbttagcompound.getCompound("Paper"); ++ paper.setLong("LastLogin", handle.loginTime); ++ paper.setLong("LastSeen", System.currentTimeMillis()); ++ // Paper end + } + + @Override +-- +2.20.1 +