Files
Paper/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
Zach Brown f1f016dd5e 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.
2019-01-02 00:35:43 -06:00

585 lines
19 KiB
Java

package org.bukkit.craftbukkit;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.GlobalPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.server.players.UserWhiteListEntry;
import net.minecraft.stats.ServerStatsCounter;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.Statistic;
import org.bukkit.ban.ProfileBanList;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.SerializableAs;
import org.bukkit.craftbukkit.entity.memory.CraftMemoryMapper;
import org.bukkit.craftbukkit.profile.CraftPlayerProfile;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.profile.PlayerProfile;
@SerializableAs("Player")
public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable {
private final GameProfile profile;
private final CraftServer server;
private final PlayerDataStorage storage;
protected CraftOfflinePlayer(CraftServer server, GameProfile profile) {
this.server = server;
this.profile = profile;
this.storage = server.console.playerDataStorage;
}
@Override
public boolean isOnline() {
return this.getPlayer() != null;
}
@Override
public String getName() {
Player player = this.getPlayer();
if (player != null) {
return player.getName();
}
// This might not match lastKnownName but if not it should be more correct
if (!this.profile.getName().isEmpty()) {
return this.profile.getName();
}
CompoundTag data = this.getBukkitData();
if (data != null) {
if (data.contains("lastKnownName")) {
return data.getString("lastKnownName");
}
}
return null;
}
@Override
public UUID getUniqueId() {
return this.profile.getId();
}
@Override
public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() { // Paper
return com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitCopy(this.profile); // Paper
}
public Server getServer() {
return this.server;
}
@Override
public boolean isOp() {
return this.server.getHandle().isOp(this.profile);
}
@Override
public void setOp(boolean value) {
if (value == this.isOp()) {
return;
}
if (value) {
this.server.getHandle().op(this.profile);
} else {
this.server.getHandle().deop(this.profile);
}
}
@Override
public boolean isBanned() {
return ((ProfileBanList) this.server.getBanList(BanList.Type.PROFILE)).isBanned(this.getPlayerProfile());
}
@Override
public BanEntry<PlayerProfile> ban(String reason, Date expires, String source) {
return ((ProfileBanList) this.server.getBanList(BanList.Type.PROFILE)).addBan(this.getPlayerProfile(), reason, expires, source);
}
@Override
public BanEntry<PlayerProfile> ban(String reason, Instant expires, String source) {
return ((ProfileBanList) this.server.getBanList(BanList.Type.PROFILE)).addBan(this.getPlayerProfile(), reason, expires, source);
}
@Override
public BanEntry<PlayerProfile> ban(String reason, Duration duration, String source) {
return ((ProfileBanList) this.server.getBanList(BanList.Type.PROFILE)).addBan(this.getPlayerProfile(), reason, duration, source);
}
public void setBanned(boolean value) {
if (value) {
((ProfileBanList) this.server.getBanList(BanList.Type.PROFILE)).addBan(this.getPlayerProfile(), null, (Date) null, null);
} else {
((ProfileBanList) this.server.getBanList(BanList.Type.PROFILE)).pardon(this.getPlayerProfile());
}
}
@Override
public boolean isWhitelisted() {
return this.server.getHandle().getWhiteList().isWhiteListed(this.profile);
}
@Override
public void setWhitelisted(boolean value) {
if (value) {
this.server.getHandle().getWhiteList().add(new UserWhiteListEntry(this.profile));
} else {
this.server.getHandle().getWhiteList().remove(this.profile);
}
}
@Override
public Map<String, Object> serialize() {
Map<String, Object> result = new LinkedHashMap<>();
result.put("UUID", this.profile.getId().toString());
return result;
}
public static OfflinePlayer deserialize(Map<String, Object> args) {
// Backwards comparability
if (args.get("name") != null) {
return Bukkit.getServer().getOfflinePlayer((String) args.get("name"));
}
return Bukkit.getServer().getOfflinePlayer(UUID.fromString((String) args.get("UUID")));
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "[UUID=" + this.profile.getId() + "]";
}
@Override
public Player getPlayer() {
return this.server.getPlayer(this.getUniqueId());
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof OfflinePlayer other)) {
return false;
}
if ((this.getUniqueId() == null) || (other.getUniqueId() == null)) {
return false;
}
return this.getUniqueId().equals(other.getUniqueId());
}
@Override
public int hashCode() {
int hash = 5;
hash = 97 * hash + (this.getUniqueId() != null ? this.getUniqueId().hashCode() : 0);
return hash;
}
private CompoundTag getData() {
return this.storage.load(this.profile.getName(), this.profile.getId().toString()).orElse(null);
}
private CompoundTag getBukkitData() {
CompoundTag result = this.getData();
if (result != null) {
if (!result.contains("bukkit")) {
result.put("bukkit", new CompoundTag());
}
result = result.getCompound("bukkit");
}
return result;
}
private File getDataFile() {
return new File(this.storage.getPlayerDir(), this.getUniqueId() + ".dat");
}
@Override
public long getFirstPlayed() {
Player player = this.getPlayer();
if (player != null) return player.getFirstPlayed();
CompoundTag data = this.getBukkitData();
if (data != null) {
if (data.contains("firstPlayed")) {
return data.getLong("firstPlayed");
} else {
File file = this.getDataFile();
return file.lastModified();
}
} else {
return 0;
}
}
@Override
public long getLastPlayed() {
Player player = this.getPlayer();
if (player != null) return player.getLastPlayed();
CompoundTag data = this.getBukkitData();
if (data != null) {
if (data.contains("lastPlayed")) {
return data.getLong("lastPlayed");
} else {
File file = this.getDataFile();
return file.lastModified();
}
} else {
return 0;
}
}
@Override
public boolean hasPlayedBefore() {
return this.getData() != null;
}
// Paper start
@Override
public long getLastLogin() {
Player player = getPlayer();
if (player != null) return player.getLastLogin();
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();
}
} else {
return 0;
}
}
@Override
public long getLastSeen() {
Player player = getPlayer();
if (player != null) return player.getLastSeen();
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();
}
} else {
return 0;
}
}
private CompoundTag getPaperData() {
CompoundTag result = getData();
if (result != null) {
if (!result.contains("Paper")) {
result.put("Paper", new CompoundTag());
}
result = result.getCompound("Paper");
}
return result;
}
// Paper end
@Override
public Location getLastDeathLocation() {
if (this.getData().contains("LastDeathLocation", 10)) {
return GlobalPos.CODEC.parse(NbtOps.INSTANCE, this.getData().get("LastDeathLocation")).result().map(CraftMemoryMapper::fromNms).orElse(null);
}
return null;
}
@Override
public Location getLocation() {
CompoundTag data = this.getData();
if (data == null) {
return null;
}
if (data.contains("Pos") && data.contains("Rotation")) {
ListTag position = (ListTag) data.get("Pos");
ListTag rotation = (ListTag) data.get("Rotation");
UUID uuid = new UUID(data.getLong("WorldUUIDMost"), data.getLong("WorldUUIDLeast"));
return new Location(this.server.getWorld(uuid),
position.getDouble(0),
position.getDouble(1),
position.getDouble(2),
rotation.getFloat(0),
rotation.getFloat(1)
);
}
return null;
}
@Override
public Location getBedSpawnLocation() {
return this.getRespawnLocation();
}
@Override
public Location getRespawnLocation() {
CompoundTag data = this.getData();
if (data == null) return null;
if (data.contains("SpawnX") && data.contains("SpawnY") && data.contains("SpawnZ")) {
String spawnWorld = data.getString("SpawnWorld");
if (spawnWorld.equals("")) {
spawnWorld = this.server.getWorlds().get(0).getName();
}
return new Location(this.server.getWorld(spawnWorld), data.getInt("SpawnX"), data.getInt("SpawnY"), data.getInt("SpawnZ"));
}
return null;
}
public void setMetadata(String metadataKey, MetadataValue metadataValue) {
this.server.getPlayerMetadata().setMetadata(this, metadataKey, metadataValue);
}
public List<MetadataValue> getMetadata(String metadataKey) {
return this.server.getPlayerMetadata().getMetadata(this, metadataKey);
}
public boolean hasMetadata(String metadataKey) {
return this.server.getPlayerMetadata().hasMetadata(this, metadataKey);
}
public void removeMetadata(String metadataKey, Plugin plugin) {
this.server.getPlayerMetadata().removeMetadata(this, metadataKey, plugin);
}
private ServerStatsCounter getStatisticManager() {
return this.server.getHandle().getPlayerStats(this.getUniqueId(), this.getName());
}
@Override
public void incrementStatistic(Statistic statistic) {
if (this.isOnline()) {
this.getPlayer().incrementStatistic(statistic);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.incrementStatistic(manager, statistic, null);
manager.save();
}
}
@Override
public void decrementStatistic(Statistic statistic) {
if (this.isOnline()) {
this.getPlayer().decrementStatistic(statistic);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.decrementStatistic(manager, statistic, null);
manager.save();
}
}
@Override
public int getStatistic(Statistic statistic) {
if (this.isOnline()) {
return this.getPlayer().getStatistic(statistic);
} else {
return CraftStatistic.getStatistic(this.getStatisticManager(), statistic);
}
}
@Override
public void incrementStatistic(Statistic statistic, int amount) {
if (this.isOnline()) {
this.getPlayer().incrementStatistic(statistic, amount);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.incrementStatistic(manager, statistic, amount, null);
manager.save();
}
}
@Override
public void decrementStatistic(Statistic statistic, int amount) {
if (this.isOnline()) {
this.getPlayer().decrementStatistic(statistic, amount);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.decrementStatistic(manager, statistic, amount, null);
manager.save();
}
}
@Override
public void setStatistic(Statistic statistic, int newValue) {
if (this.isOnline()) {
this.getPlayer().setStatistic(statistic, newValue);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.setStatistic(manager, statistic, newValue, null);
manager.save();
}
}
@Override
public void incrementStatistic(Statistic statistic, Material material) {
if (this.isOnline()) {
this.getPlayer().incrementStatistic(statistic, material);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.incrementStatistic(manager, statistic, material, null);
manager.save();
}
}
@Override
public void decrementStatistic(Statistic statistic, Material material) {
if (this.isOnline()) {
this.getPlayer().decrementStatistic(statistic, material);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.decrementStatistic(manager, statistic, material, null);
manager.save();
}
}
@Override
public int getStatistic(Statistic statistic, Material material) {
if (this.isOnline()) {
return this.getPlayer().getStatistic(statistic, material);
} else {
return CraftStatistic.getStatistic(this.getStatisticManager(), statistic, material);
}
}
@Override
public void incrementStatistic(Statistic statistic, Material material, int amount) {
if (this.isOnline()) {
this.getPlayer().incrementStatistic(statistic, material, amount);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.incrementStatistic(manager, statistic, material, amount, null);
manager.save();
}
}
@Override
public void decrementStatistic(Statistic statistic, Material material, int amount) {
if (this.isOnline()) {
this.getPlayer().decrementStatistic(statistic, material, amount);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.decrementStatistic(manager, statistic, material, amount, null);
manager.save();
}
}
@Override
public void setStatistic(Statistic statistic, Material material, int newValue) {
if (this.isOnline()) {
this.getPlayer().setStatistic(statistic, material, newValue);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.setStatistic(manager, statistic, material, newValue, null);
manager.save();
}
}
@Override
public void incrementStatistic(Statistic statistic, EntityType entityType) {
if (this.isOnline()) {
this.getPlayer().incrementStatistic(statistic, entityType);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.incrementStatistic(manager, statistic, entityType, null);
manager.save();
}
}
@Override
public void decrementStatistic(Statistic statistic, EntityType entityType) {
if (this.isOnline()) {
this.getPlayer().decrementStatistic(statistic, entityType);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.decrementStatistic(manager, statistic, entityType, null);
manager.save();
}
}
@Override
public int getStatistic(Statistic statistic, EntityType entityType) {
if (this.isOnline()) {
return this.getPlayer().getStatistic(statistic, entityType);
} else {
return CraftStatistic.getStatistic(this.getStatisticManager(), statistic, entityType);
}
}
@Override
public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) {
if (this.isOnline()) {
this.getPlayer().incrementStatistic(statistic, entityType, amount);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.incrementStatistic(manager, statistic, entityType, amount, null);
manager.save();
}
}
@Override
public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) {
if (this.isOnline()) {
this.getPlayer().decrementStatistic(statistic, entityType, amount);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.decrementStatistic(manager, statistic, entityType, amount, null);
manager.save();
}
}
@Override
public void setStatistic(Statistic statistic, EntityType entityType, int newValue) {
if (this.isOnline()) {
this.getPlayer().setStatistic(statistic, entityType, newValue);
} else {
ServerStatsCounter manager = this.getStatisticManager();
CraftStatistic.setStatistic(manager, statistic, entityType, newValue, null);
manager.save();
}
}
}