diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index 1a6cc3c1d..d046db850 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -7,6 +7,7 @@ import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.GameProfile; +import com.velocitypowered.api.util.Identifiable; import com.velocitypowered.api.util.MessagePosition; import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.api.util.title.Title; @@ -19,7 +20,7 @@ import net.kyori.text.Component; * Represents a player who is connected to the proxy. */ public interface Player extends CommandSource, InboundConnection, ChannelMessageSource, - ChannelMessageSink { + ChannelMessageSink, Identifiable { /** * Returns the player's current username. @@ -28,13 +29,6 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage */ String getUsername(); - /** - * Returns the player's UUID. - * - * @return the UUID - */ - UUID getUniqueId(); - /** * Returns the server that the player is currently connected to. * diff --git a/api/src/main/java/com/velocitypowered/api/util/GameProfile.java b/api/src/main/java/com/velocitypowered/api/util/GameProfile.java index a868a801c..d9147c7a4 100644 --- a/api/src/main/java/com/velocitypowered/api/util/GameProfile.java +++ b/api/src/main/java/com/velocitypowered/api/util/GameProfile.java @@ -8,24 +8,36 @@ import java.util.UUID; /** * Represents a Mojang game profile. This class is immutable. */ -public final class GameProfile { +public final class GameProfile implements Identifiable { - private final String id; - private final String name; + private final UUID id; + private final String undashedId, name; private final List properties; - public GameProfile(String id, String name, List properties) { - this.id = Preconditions.checkNotNull(id, "id"); - this.name = Preconditions.checkNotNull(name, "name"); - this.properties = ImmutableList.copyOf(properties); + public GameProfile(UUID id, String name, List properties) { + this(Preconditions.checkNotNull(id, "id"), UuidUtils.toUndashed(id), + Preconditions.checkNotNull(name, "name"), ImmutableList.copyOf(properties)); } - public String getId() { - return id; + public GameProfile(String undashedId, String name, List properties) { + this(UuidUtils.fromUndashed(Preconditions.checkNotNull(undashedId, "undashedId")), undashedId, + Preconditions.checkNotNull(name, "name"), ImmutableList.copyOf(properties)); } - public UUID idAsUuid() { - return UuidUtils.fromUndashed(id); + private GameProfile(UUID id, String undashedId, String name, List properties) { + this.id = id; + this.undashedId = undashedId; + this.name = name; + this.properties = properties; + } + + @Override + public UUID getUniqueId() { + return this.id; + } + + public String getUndashedId() { + return this.undashedId; } public String getName() { @@ -36,6 +48,36 @@ public final class GameProfile { return properties; } + public GameProfile setUniqueId(UUID id) { + return new GameProfile(Preconditions.checkNotNull(id, "id"), UuidUtils.toUndashed(id), + this.name, this.properties); + } + + public GameProfile setUndashedId(String undashedId) { + return new GameProfile( + UuidUtils.fromUndashed(Preconditions.checkNotNull(undashedId, "undashedId")), undashedId, + this.name, this.properties); + } + + public GameProfile setName(String name) { + return new GameProfile(this.id, this.undashedId, Preconditions.checkNotNull(name, "name"), + this.properties); + } + + public GameProfile setProperties(List properties) { + return new GameProfile(this.id, this.undashedId, this.name, ImmutableList.copyOf(properties)); + } + + public GameProfile addProperties(Iterable properties) { + return new GameProfile(this.id, this.undashedId, this.name, + ImmutableList.builder().addAll(this.properties).addAll(properties).build()); + } + + public GameProfile addProperty(Property property) { + return new GameProfile(this.id, this.undashedId, this.name, + ImmutableList.builder().addAll(this.properties).add(property).build()); + } + /** * Creates a game profile suitable for use in offline-mode. * @@ -44,8 +86,8 @@ public final class GameProfile { */ public static GameProfile forOfflinePlayer(String username) { Preconditions.checkNotNull(username, "username"); - String id = UuidUtils.toUndashed(UuidUtils.generateOfflinePlayerUuid(username)); - return new GameProfile(id, username, ImmutableList.of()); + return new GameProfile(UuidUtils.generateOfflinePlayerUuid(username), username, + ImmutableList.of()); } @Override diff --git a/api/src/main/java/com/velocitypowered/api/util/Identifiable.java b/api/src/main/java/com/velocitypowered/api/util/Identifiable.java new file mode 100644 index 000000000..8235e5267 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/Identifiable.java @@ -0,0 +1,11 @@ +package com.velocitypowered.api.util; + +import java.util.UUID; + +/** + * Represents an object that can be identified by its UUID + */ +public interface Identifiable { + + UUID getUniqueId(); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 0bc9e16ec..9a12a9083 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -15,6 +15,7 @@ import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.util.Favicon; +import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.ProxyVersion; import com.velocitypowered.proxy.command.ServerCommand; import com.velocitypowered.proxy.command.ShutdownCommand; @@ -30,6 +31,7 @@ import com.velocitypowered.proxy.plugin.VelocityEventManager; import com.velocitypowered.proxy.plugin.VelocityPluginManager; import com.velocitypowered.proxy.protocol.packet.Chat; import com.velocitypowered.proxy.protocol.util.FaviconSerializer; +import com.velocitypowered.proxy.protocol.util.GameProfileSerializer; import com.velocitypowered.proxy.scheduler.VelocityScheduler; import com.velocitypowered.proxy.server.ServerMap; import com.velocitypowered.proxy.util.AddressUtil; @@ -65,6 +67,7 @@ public class VelocityServer implements ProxyServer { public static final Gson GSON = new GsonBuilder() .registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer()) .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) + .registerTypeHierarchyAdapter(GameProfile.class, new GameProfileSerializer()) .create(); private @MonotonicNonNull ConnectionManager cm; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index 4ab39ecca..603a2190c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -157,7 +157,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { try { ProtocolUtils.writeVarInt(dataToForward, VelocityConstants.FORWARDING_VERSION); ProtocolUtils.writeString(dataToForward, address); - ProtocolUtils.writeUuid(dataToForward, profile.idAsUuid()); + ProtocolUtils.writeUuid(dataToForward, profile.getUniqueId()); ProtocolUtils.writeString(dataToForward, profile.getName()); ProtocolUtils.writeProperties(dataToForward, profile.getProperties()); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index 7ee3804b6..aff20854c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -109,7 +109,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, .append('\0') .append(proxyPlayer.getRemoteAddress().getHostString()) .append('\0') - .append(proxyPlayer.getProfile().getId()) + .append(proxyPlayer.getProfile().getUndashedId()) .append('\0'); GSON.toJson(proxyPlayer.getProfile().getProperties(), data); return data.toString(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index a2d2e64b5..81f2d1209 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -100,7 +100,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public UUID getUniqueId() { - return profile.idAsUuid(); + return profile.getUniqueId(); } @Override @@ -212,8 +212,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public void setGameProfileProperties(List properties) { - Preconditions.checkNotNull(properties); - this.profile = new GameProfile(profile.getId(), profile.getName(), properties); + this.profile = profile.setProperties(Preconditions.checkNotNull(properties)); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index 9c4961fc0..4223ea812 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -219,9 +219,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { if (inbound.isLegacyForge() && server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.LEGACY) { // We want to add the FML token to the properties - List properties = new ArrayList<>(profile.getProperties()); - properties.add(new GameProfile.Property("forgeClient", "true", "")); - profile = new GameProfile(profile.getId(), profile.getName(), properties); + profile = profile.addProperty(new GameProfile.Property("forgeClient", "true", "")); } GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(apiInbound, profile, onlineMode); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java index a389ed085..4a67bdbdf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java @@ -142,7 +142,7 @@ public class PlayerListItem implements MinecraftPacket { } public static Item from(TabListEntry entry) { - return new Item(entry.getProfile().idAsUuid()) + return new Item(entry.getProfile().getUniqueId()) .setName(entry.getProfile().getName()) .setProperties(entry.getProfile().getProperties()) .setLatency(entry.getLatency()) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java new file mode 100644 index 000000000..600312b90 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java @@ -0,0 +1,37 @@ +package com.velocitypowered.proxy.protocol.util; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.reflect.TypeToken; +import com.velocitypowered.api.util.GameProfile; +import com.velocitypowered.api.util.GameProfile.Property; +import java.lang.reflect.Type; +import java.util.List; + +public class GameProfileSerializer implements JsonSerializer, + JsonDeserializer { + + private static final Type propertyList = new TypeToken>() {}.getType(); + + @Override + public GameProfile deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) { + JsonObject obj = json.getAsJsonObject(); + return new GameProfile(obj.get("id").getAsString(), obj.get("name").getAsString(), + context.deserialize(obj.get("properties"), propertyList)); + } + + @Override + public JsonElement serialize(GameProfile src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + obj.add("id", new JsonPrimitive(src.getUndashedId())); + obj.add("name", new JsonPrimitive(src.getName())); + obj.add("properties", context.serialize(src.getProperties(), propertyList)); + return obj; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index 920518dc9..0b6b90e1a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -45,13 +45,13 @@ public class VelocityTabList implements TabList { Preconditions.checkNotNull(entry, "entry"); Preconditions.checkArgument(entry.getTabList().equals(this), "The provided entry was not created by this tab list"); - Preconditions.checkArgument(!entries.containsKey(entry.getProfile().idAsUuid()), + Preconditions.checkArgument(!entries.containsKey(entry.getProfile().getUniqueId()), "this TabList already contains an entry with the same uuid"); PlayerListItem.Item packetItem = PlayerListItem.Item.from(entry); connection.write( new PlayerListItem(PlayerListItem.ADD_PLAYER, Collections.singletonList(packetItem))); - entries.put(entry.getProfile().idAsUuid(), entry); + entries.put(entry.getProfile().getUniqueId(), entry); } @Override @@ -105,7 +105,7 @@ public class VelocityTabList implements TabList { } entries.put(item.getUuid(), TabListEntry.builder() .tabList(this) - .profile(new GameProfile(UuidUtils.toUndashed(uuid), name, properties)) + .profile(new GameProfile(uuid, name, properties)) .displayName(item.getDisplayName()) .latency(item.getLatency()) .gameMode(item.getGameMode()) @@ -141,7 +141,7 @@ public class VelocityTabList implements TabList { } void updateEntry(int action, TabListEntry entry) { - if (entries.containsKey(entry.getProfile().idAsUuid())) { + if (entries.containsKey(entry.getProfile().getUniqueId())) { PlayerListItem.Item packetItem = PlayerListItem.Item.from(entry); connection.write(new PlayerListItem(action, Collections.singletonList(packetItem))); }