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..ce8a8c4b1 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -28,6 +28,7 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage */ String getUsername(); + /** * Returns the player's UUID. * 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..015cdae97 100644 --- a/api/src/main/java/com/velocitypowered/api/util/GameProfile.java +++ b/api/src/main/java/com/velocitypowered/api/util/GameProfile.java @@ -10,22 +10,33 @@ import java.util.UUID; */ public final class GameProfile { - 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 GameProfile(String undashedId, String name, List properties) { + this(UuidUtils.fromUndashed(Preconditions.checkNotNull(undashedId, "undashedId")), undashedId, + Preconditions.checkNotNull(name, "name"), ImmutableList.copyOf(properties)); + } + + private GameProfile(UUID id, String undashedId, String name, List properties) { + this.id = id; + this.undashedId = undashedId; + this.name = name; + this.properties = properties; } public String getId() { - return id; + return undashedId; } public UUID idAsUuid() { - return UuidUtils.fromUndashed(id); + return id; } public String getName() { @@ -36,6 +47,74 @@ public final class GameProfile { return properties; } + /** + * Creates a new {@code GameProfile} with the specified unique id. + * + * @param id the new unique id + * @return the new {@code GameProfile} + */ + public GameProfile withUuid(UUID id) { + return new GameProfile(Preconditions.checkNotNull(id, "id"), UuidUtils.toUndashed(id), + this.name, this.properties); + } + + /** + * Creates a new {@code GameProfile} with the specified undashed id. + * + * @param undashedId the new undashed id + * @return the new {@code GameProfile} + */ + public GameProfile withId(String undashedId) { + return new GameProfile( + UuidUtils.fromUndashed(Preconditions.checkNotNull(undashedId, "undashedId")), undashedId, + this.name, this.properties); + } + + /** + * Creates a new {@code GameProfile} with the specified name. + * + * @param name the new name + * @return the new {@code GameProfile} + */ + public GameProfile withName(String name) { + return new GameProfile(this.id, this.undashedId, Preconditions.checkNotNull(name, "name"), + this.properties); + } + + /** + * Creates a new {@code GameProfile} with the specified properties. + * + * @param properties the new properties + * @return the new {@code GameProfile} + */ + public GameProfile withProperties(List properties) { + return new GameProfile(this.id, this.undashedId, this.name, ImmutableList.copyOf(properties)); + } + + /** + * Creates a new {@code GameProfile} with the properties of this object plus the specified + * properties. + * + * @param properties the properties to add + * @return the new {@code GameProfile} + */ + public GameProfile addProperties(Iterable properties) { + return new GameProfile(this.id, this.undashedId, this.name, + ImmutableList.builder().addAll(this.properties).addAll(properties).build()); + } + + /** + * Creates a new {@code GameProfile} with the properties of this object plus the specified + * property. + * + * @param property the property to add + * @return the new {@code GameProfile} + */ + 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 +123,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/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 8bc8c0666..4f0e3616a 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/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 1bbd21404..f9274e372 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 @@ -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.withProperties(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/util/GameProfileSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/GameProfileSerializer.java new file mode 100644 index 000000000..da4de84ba --- /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.getId())); + 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..4e084d61e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -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())