Skip to content

Commit

Permalink
fix: undefined behaviour when uuid on proxy and server don't match (#388
Browse files Browse the repository at this point in the history
)

Fixes #320

Port of 888424f to v4
  • Loading branch information
diogotcorreia committed Mar 22, 2024
1 parent f1c206a commit 26b847d
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,69 @@
import com.rexcantor64.triton.language.item.LanguageText;
import com.rexcantor64.triton.player.LanguagePlayer;
import com.rexcantor64.triton.storage.LocalStorage;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;

import java.util.ArrayList;
import java.util.List;

/**
* Actions:
* 0 -> send storage and config
* 1 -> send player language
* 2 -> send command to be ran as console
* 3 -> tell server to re-fetch translations from database
* 4 -> forward Triton command as player
*/
@SuppressWarnings("UnstableApiUsage") // Guava's ByteStreams is marked with @Beta
public class BridgeSerializer {

/**
* Actions from proxy to server
*/
@RequiredArgsConstructor
@Getter
public enum ActionP2S {
/**
* Send storage and config
**/
SEND_STORAGE_AND_CONFIG(0),
/**
* Send a player's language
**/
SEND_PLAYER_LANGUAGE(1),
/**
* Send command to be run as console
**/
SEND_COMMAND_AS_CONSOLE(2),
/**
* Signal server to re-fetch translations from database
**/
SIGNAL_REFRESH_FROM_DB(3),
/**
* Forward a Triton command to be run as the player themselves
**/
FORWARD_TRITON_COMMAND(4),
;

private final int key;
}

/**
* Actions from server to proxy
*/
@RequiredArgsConstructor
@Getter
public enum ActionS2P {
/**
* Update player's language
**/
UPDATE_PLAYER_LANGUAGE(0),
/**
* Add or remove a sign (location) from a sign group
**/
UPDATE_SIGN_GROUP_MEMBERSHIP(1),
;

private final int key;
}

public static byte[] getLanguageDataOutput() {
val languageOut = ByteStreams.newDataOutput();
// Action 0 (send config)
languageOut.writeUTF(Triton.get().getLanguageManager().getMainLanguage().getName());
val languageList = Triton.get().getLanguageManager().getAllLanguages();
languageOut.writeShort(languageList.size());
Expand All @@ -53,8 +96,7 @@ public static List<byte[]> buildTranslationData(String serverName, byte @NonNull
// If not using local storage, each server should fetch the translations for themselves
if (!(Triton.get().getStorage() instanceof LocalStorage)) {
val refreshSignalOut = ByteStreams.newDataOutput();
// Action 3 (tell server to re-fetch from database storage)
refreshSignalOut.writeByte(3);
refreshSignalOut.writeByte(ActionP2S.SIGNAL_REFRESH_FROM_DB.getKey());
outList.add(refreshSignalOut.toByteArray());
return outList;
}
Expand All @@ -67,7 +109,7 @@ public static List<byte[]> buildTranslationData(String serverName, byte @NonNull
for (val item : collection.getItems()) {
if (languageItemsOut.toByteArray().length > 29000) {
val out = ByteStreams.newDataOutput();
out.writeByte(0);
out.writeByte(ActionP2S.SEND_STORAGE_AND_CONFIG.getKey());
if (outList.size() == 0) {
out.writeBoolean(true);
out.write(languageOut);
Expand Down Expand Up @@ -144,7 +186,7 @@ public static List<byte[]> buildTranslationData(String serverName, byte @NonNull
size++;
}
val out = ByteStreams.newDataOutput();
out.writeByte(0);
out.writeByte(ActionP2S.SEND_STORAGE_AND_CONFIG.getKey());
val firstSend = outList.size() == 0;
out.writeBoolean(firstSend);
if (firstSend)
Expand All @@ -162,8 +204,7 @@ public static List<byte[]> buildTranslationData(String serverName, byte @NonNull

public static byte[] buildPlayerLanguageData(LanguagePlayer lp) {
val out = ByteStreams.newDataOutput();
// Action 1
out.writeByte(1);
out.writeByte(ActionP2S.SEND_PLAYER_LANGUAGE.getKey());
val uuid = lp.getUUID();
out.writeLong(uuid.getMostSignificantBits());
out.writeLong(uuid.getLeastSignificantBits());
Expand All @@ -173,16 +214,14 @@ public static byte[] buildPlayerLanguageData(LanguagePlayer lp) {

public static byte[] buildExecutableCommandData(String command) {
val out = ByteStreams.newDataOutput();
// Action 2
out.writeByte(2);
out.writeByte(ActionP2S.SEND_COMMAND_AS_CONSOLE.getKey());
out.writeUTF(command);
return out.toByteArray();
}

public static byte[] buildForwardCommandData(CommandEvent commandEvent) {
val out = ByteStreams.newDataOutput();
// Action 4
out.writeByte(4);
out.writeByte(ActionP2S.FORWARD_TRITON_COMMAND.getKey());

val uuid = commandEvent.getSender().getUUID();
out.writeLong(uuid.getMostSignificantBits());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.rexcantor64.triton.api.language.Language;

import java.util.UUID;

public interface LanguagePlayer extends com.rexcantor64.triton.api.players.LanguagePlayer {

boolean isWaitingForClientLocale();
Expand All @@ -12,4 +14,11 @@ public interface LanguagePlayer extends com.rexcantor64.triton.api.players.Langu
default Language getLanguage() {
return this.getLang();
}

/**
* @return the UUID that is to be used for storage-related operations
*/
default UUID getStorageUniqueId() {
return this.getUUID();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public Language getLanguageFromIp(String ip) {
@Override
public Language getLanguage(LanguagePlayer lp) {
Triton.get().getLogger().logTrace("[Local Storage] Getting language for player %1", lp);
String lang = languageMap.get(lp.getUUID().toString());
String lang = languageMap.get(lp.getStorageUniqueId().toString());
if ((Triton.isProxy() || !Triton.get().getConfig().isBungeecord()) &&
(lang == null
|| (Triton.get().getConfig().isAlwaysCheckClientLocale()))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public Language getLanguageFromIp(String ip) {
@Override
public Language getLanguage(LanguagePlayer lp) {
Triton.get().getLogger().logTrace("[MySQL Storage] Getting language for player %1", lp);
String lang = getValueFromStorage(lp.getUUID().toString());
String lang = getValueFromStorage(lp.getStorageUniqueId().toString());
if ((Triton.isProxy() || !Triton.get().getConfig().isBungeecord()) &&
(lang == null
|| (Triton.get().getConfig().isAlwaysCheckClientLocale())))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void onPluginMessage(PluginMessageEvent e) {
val action = in.readByte();

// Player changes language
if (action == 0) {
if (action == BridgeSerializer.ActionS2P.UPDATE_PLAYER_LANGUAGE.getKey()) {
val uuid = UUID.fromString(in.readUTF());
val language = in.readUTF();

Expand All @@ -53,7 +53,7 @@ public void onPluginMessage(PluginMessageEvent e) {
}

// Add or remove a location from a sign group using /triton sign
if (action == 1) {
if (action == BridgeSerializer.ActionS2P.UPDATE_SIGN_GROUP_MEMBERSHIP.getKey()) {
val server = ((Server) e.getSender()).getInfo();
SignLocation location = new SignLocation(server.getName(), in.readUTF(), in.readInt(), in.readInt(), in
.readInt());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
import com.google.gson.JsonObject;
import com.rexcantor64.triton.Triton;
import com.rexcantor64.triton.bridge.BridgeManager;
import com.rexcantor64.triton.bridge.BridgeSerializer;
import com.rexcantor64.triton.commands.handler.CommandEvent;
import com.rexcantor64.triton.language.Language;
import com.rexcantor64.triton.language.item.Collection;
import com.rexcantor64.triton.language.item.LanguageItem;
import com.rexcantor64.triton.language.item.LanguageSign;
import com.rexcantor64.triton.language.item.LanguageText;
import com.rexcantor64.triton.language.item.SignLocation;
import com.rexcantor64.triton.spigot.SpigotTriton;
import com.rexcantor64.triton.spigot.commands.handler.SpigotSender;
import com.rexcantor64.triton.language.Language;
import com.rexcantor64.triton.language.item.*;
import com.rexcantor64.triton.spigot.player.SpigotLanguagePlayer;
import com.rexcantor64.triton.storage.LocalStorage;
import lombok.val;
Expand Down Expand Up @@ -48,7 +53,7 @@ public void onPluginMessageReceived(@NotNull String channel, @NotNull Player pla
val in = new DataInputStream(new ByteArrayInputStream(bytes));
try {
val action = in.readByte();
if (action == 0) {
if (action == BridgeSerializer.ActionP2S.SEND_STORAGE_AND_CONFIG.getKey()) {
if (!(Triton.get().getStorage() instanceof LocalStorage)) {
Triton.get().getLogger()
.logWarning("You're using BungeeCord with a local storage option, but this server is " +
Expand Down Expand Up @@ -165,15 +170,18 @@ public void onPluginMessageReceived(@NotNull String channel, @NotNull Player pla
Bukkit.getScheduler().runTaskLater(SpigotTriton.asSpigot().getLoader(), () -> Triton.get()
.refreshPlayers(), 10L);
}
} else if (action == 1) {
} else if (action == BridgeSerializer.ActionP2S.SEND_PLAYER_LANGUAGE.getKey()) {
val uuid = new UUID(in.readLong(), in.readLong());

val lang = Triton.get().getLanguageManager().getLanguageByNameOrDefault(in.readUTF());
val languagePlayer = (SpigotLanguagePlayer) SpigotTriton.asSpigot().getPlayerManager().get(player.getUniqueId());
languagePlayer.setProxyUniqueId(uuid);
Bukkit.getScheduler().runTaskLater(SpigotTriton.asSpigot().getLoader(),
() -> ((SpigotLanguagePlayer) Triton.get().getPlayerManager().get(uuid)).setLang(lang, false)
, 10L);
} else if (action == 2) {
() -> languagePlayer.setLang(lang, false),
10L);
} else if (action == BridgeSerializer.ActionP2S.SEND_COMMAND_AS_CONSOLE.getKey()) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), in.readUTF());
} else if (action == 3) {
} else if (action == BridgeSerializer.ActionP2S.SIGNAL_REFRESH_FROM_DB.getKey()) {
val storage = Triton.get().getStorage();
if (storage instanceof LocalStorage) {
Triton.get().getLogger()
Expand All @@ -193,20 +201,20 @@ public void onPluginMessageReceived(@NotNull String channel, @NotNull Player pla
Bukkit.getScheduler().runTaskLater(SpigotTriton.asSpigot().getLoader(), () -> Triton.get()
.refreshPlayers(), 10L);
});
} else if (action == 4) {
val uuid = new UUID(in.readLong(), in.readLong());
val p = Bukkit.getPlayer(uuid);
} else if (action == BridgeSerializer.ActionP2S.FORWARD_TRITON_COMMAND.getKey()) {
@Deprecated
val uuid = new UUID(in.readLong(), in.readLong()); // TODO remove in v4

val subCommand = in.readBoolean() ? in.readUTF() : null;
val args = new String[in.readShort()];
for (int i = 0; i < args.length; ++i)
args[i] = in.readUTF();

Triton.get().getLogger().logTrace("Received forwarded command '%1' with args %2 for player %3",
subCommand, Arrays.toString(args), uuid);
subCommand, Arrays.toString(args), player.getUniqueId());

val commandEvent = new CommandEvent(
new SpigotSender(p),
new SpigotSender(player),
subCommand,
args,
"triton"
Expand All @@ -220,9 +228,8 @@ public void onPluginMessageReceived(@NotNull String channel, @NotNull Player pla

public void updatePlayerLanguage(SpigotLanguagePlayer lp) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
// Action (0): updatePlayerLanguage
out.writeByte(0);
out.writeUTF(lp.getUUID().toString());
out.writeByte(BridgeSerializer.ActionS2P.UPDATE_PLAYER_LANGUAGE.getKey());
out.writeUTF(lp.getProxyUniqueId().toString());
out.writeUTF(lp.getLang().getName());
lp.toBukkit().ifPresent(player ->
player.sendPluginMessage(SpigotTriton.asSpigot().getLoader(), "triton:main", out.toByteArray())
Expand All @@ -231,8 +238,7 @@ public void updatePlayerLanguage(SpigotLanguagePlayer lp) {

public void updateSign(String world, int x, int y, int z, String key, Player p) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
// Action (1): sign management
out.writeByte(1);
out.writeByte(BridgeSerializer.ActionS2P.UPDATE_SIGN_GROUP_MEMBERSHIP.getKey());
out.writeUTF(world);
out.writeInt(x);
out.writeInt(y);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
Expand All @@ -33,6 +34,11 @@
public class SpigotLanguagePlayer implements LanguagePlayer {

private final UUID uuid;
/**
* UUID of this player as seen by the proxy; some servers might have different player UUIDs on proxy and servers
**/
@Getter
private UUID proxyUniqueId;
private Player bukkit;

private Language lang;
Expand Down Expand Up @@ -67,6 +73,7 @@ public class SpigotLanguagePlayer implements LanguagePlayer {

public SpigotLanguagePlayer(UUID p) {
uuid = p;
proxyUniqueId = this.uuid;
load();
}

Expand Down Expand Up @@ -98,6 +105,15 @@ public void saveSign(SignLocation location, Object tileEntityType, NbtCompound n
this.signs.put(location, new Sign(tileEntityType, nbtCompound));
}

public void setProxyUniqueId(UUID proxyUniqueId) {
if (Objects.equals(proxyUniqueId, this.proxyUniqueId)) {
return;
}
this.proxyUniqueId = proxyUniqueId;
val language = Triton.get().getStorage().getLanguage(this);
setLang(language, false);
}

public Language getLang() {
if (lang == null)
lang = Triton.get().getLanguageManager().getMainLanguage();
Expand Down Expand Up @@ -126,14 +142,17 @@ public void setLang(Language lang, boolean sendToBungee) {
Triton.get().getLogger().logError(e, "Failed to send \"language changed\" message.");
}
}
boolean hasChanged = !Objects.equals(event.getNewLanguage(), this.lang);
this.lang = event.getNewLanguage();
this.waitingForClientLocale = false;
refreshAll();
if (SpigotTriton.asSpigot().getBridgeManager() == null || Triton.get().getStorage() instanceof LocalStorage)
save();
if (sendToBungee && SpigotTriton.asSpigot().getBridgeManager() != null && Triton.get().getConfig().isBungeecord())
SpigotTriton.asSpigot().getBridgeManager().updatePlayerLanguage(this);
executeCommands();
if (hasChanged) {
refreshAll();
if (SpigotTriton.asSpigot().getBridgeManager() == null || Triton.get().getStorage() instanceof LocalStorage)
save();
if (sendToBungee && SpigotTriton.asSpigot().getBridgeManager() != null && Triton.get().getConfig().isBungeecord())
SpigotTriton.asSpigot().getBridgeManager().updatePlayerLanguage(this);
executeCommands();
}
}

@Override
Expand Down Expand Up @@ -221,7 +240,7 @@ private void save() {
ip = player.getAddress().getAddress().getHostAddress();
}
}
Triton.get().getStorage().setLanguage(uuid, ip, lang);
Triton.get().getStorage().setLanguage(this.getStorageUniqueId(), ip, lang);
});
}

Expand Down
Loading

0 comments on commit 26b847d

Please sign in to comment.