Skip to content

Commit

Permalink
Rewrite uuids in Velocity Tablist API
Browse files Browse the repository at this point in the history
  • Loading branch information
UserNugget committed Jun 21, 2024
1 parent 9ecc334 commit ca691d8
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 5 deletions.
11 changes: 6 additions & 5 deletions plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import net.elytrium.commons.config.YamlConfig;
import net.elytrium.commons.kyori.serialization.Serializer;
Expand Down Expand Up @@ -133,6 +134,8 @@ public class LimboAPI implements LimboFactory {
@MonotonicNonNull
private static Serializer SERIALIZER;

public static final ConcurrentHashMap<Player, UUID> INITIAL_ID = new ConcurrentHashMap<>();

private final VelocityServer server;
private final Metrics.Factory metricsFactory;
private final File configFile;
Expand All @@ -143,7 +146,6 @@ public class LimboAPI implements LimboFactory {
private final HashMap<Player, LoginTasksQueue> loginQueue;
private final HashMap<Player, Function<KickedFromServerEvent, Boolean>> kickCallback;
private final HashMap<Player, RegisteredServer> nextServer;
private final HashMap<Player, UUID> initialID;

private PreparedPacketFactory preparedPacketFactory;
private PreparedPacketFactory configPreparedPacketFactory;
Expand All @@ -167,7 +169,6 @@ public LimboAPI(Logger logger, ProxyServer server, Metrics.Factory metricsFactor
this.loginQueue = new HashMap<>();
this.kickCallback = new HashMap<>();
this.nextServer = new HashMap<>();
this.initialID = new HashMap<>();

int maximumProtocolVersionNumber = ProtocolVersion.MAXIMUM_VERSION.getProtocol();
if (maximumProtocolVersionNumber < SUPPORTED_MAXIMUM_PROTOCOL_VERSION_NUMBER) {
Expand Down Expand Up @@ -600,15 +601,15 @@ public RegisteredServer getNextServer(Player player) {
}

public void setInitialID(Player player, UUID nextServer) {
this.initialID.put(player, nextServer);
INITIAL_ID.put(player, nextServer);
}

public void removeInitialID(Player player) {
this.initialID.remove(player);
INITIAL_ID.remove(player);
}

public UUID getInitialID(Player player) {
return this.initialID.get(player);
return INITIAL_ID.get(player);
}

public LoginListener getLoginListener() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,23 @@
import net.elytrium.limboapi.injection.dummy.DummyEventPool;
import net.elytrium.limboapi.injection.login.confirmation.LoginConfirmHandler;
import net.elytrium.limboapi.injection.packet.ServerLoginSuccessHook;
import net.elytrium.limboapi.injection.tablist.RewritingKeyedVelocityTabList;
import net.elytrium.limboapi.injection.tablist.RewritingVelocityTabList;
import net.elytrium.limboapi.injection.tablist.RewritingVelocityTabListLegacy;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import sun.misc.Unsafe;

public class LoginListener {

private static final ClosedMinecraftConnection CLOSED_MINECRAFT_CONNECTION;

private static final Unsafe UNSAFE;
private static final MethodHandle DELEGATE_FIELD;
private static final Field MC_CONNECTION_FIELD;
private static final MethodHandle CONNECTED_PLAYER_CONSTRUCTOR;
private static final MethodHandle SPAWNED_FIELD;
private static final Field TABLIST_FIELD;

private final LimboAPI plugin;
private final VelocityServer server;
Expand Down Expand Up @@ -162,6 +168,16 @@ public void hookLoginSession(GameProfileRequestEvent event) throws Throwable {
event.isOnlineMode(),
playerKey
);

long fieldOffset = UNSAFE.objectFieldOffset(TABLIST_FIELD);
if (connection.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
UNSAFE.putObject(player, fieldOffset, new RewritingVelocityTabList(player));
} else if (connection.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
UNSAFE.putObject(player, fieldOffset, new RewritingKeyedVelocityTabList(player, this.server));
} else {
UNSAFE.putObject(player, fieldOffset, new RewritingVelocityTabListLegacy(player, this.server));
}

if (connection.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_20_2) >= 0) {
loginHandler.setPlayer(player);
}
Expand Down Expand Up @@ -292,6 +308,13 @@ public void hookPlaySession(ServerConnectedEvent event) {

SPAWNED_FIELD = MethodHandles.privateLookupIn(ClientPlaySessionHandler.class, MethodHandles.lookup())
.findSetter(ClientPlaySessionHandler.class, "spawned", boolean.class);

Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
UNSAFE = (Unsafe) unsafeField.get(null);

TABLIST_FIELD = ConnectedPlayer.class.getDeclaredField("tabList");
TABLIST_FIELD.setAccessible(true);
} catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException e) {
throw new ReflectionException(e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2021 - 2024 Elytrium
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package net.elytrium.limboapi.injection.tablist;

import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.tablist.KeyedVelocityTabList;
import java.util.Optional;
import java.util.UUID;

public class RewritingKeyedVelocityTabList extends KeyedVelocityTabList implements RewritingTabList {

public RewritingKeyedVelocityTabList(ConnectedPlayer player, ProxyServer proxyServer) {
super(player, proxyServer);
}

@Override
public void addEntry(TabListEntry entry) {
super.addEntry(this.rewriteEntry(entry));
}

@Override
public Optional<TabListEntry> getEntry(UUID uuid) {
return super.getEntry(this.rewriteUuid(uuid));
}

@Override
public boolean containsEntry(UUID uuid) {
return super.containsEntry(this.rewriteUuid(uuid));
}

@Override
public Optional<TabListEntry> removeEntry(UUID uuid) {
return super.removeEntry(this.rewriteUuid(uuid));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2021 - 2024 Elytrium
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package net.elytrium.limboapi.injection.tablist;

import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.api.util.GameProfile;
import java.util.UUID;
import net.elytrium.limboapi.LimboAPI;

public interface RewritingTabList {

Player getPlayer();

default TabListEntry rewriteEntry(TabListEntry entry) {
if (entry == null || entry.getProfile() == null || !this.getPlayer().getUniqueId().equals(entry.getProfile().getId())) {
return entry;
}

TabListEntry.Builder builder = TabListEntry.builder();
builder.tabList(entry.getTabList());
builder.profile(new GameProfile(this.rewriteUuid(entry.getProfile().getId()),
entry.getProfile().getName(), entry.getProfile().getProperties()));
builder.listed(entry.isListed());
builder.latency(entry.getLatency());
builder.gameMode(entry.getGameMode());
entry.getDisplayNameComponent().ifPresent(builder::displayName);
builder.chatSession(entry.getChatSession());

return builder.build();
}

default UUID rewriteUuid(UUID uuid) {
if (this.getPlayer().getUniqueId().equals(uuid)) {
return LimboAPI.INITIAL_ID.getOrDefault(this.getPlayer(), uuid);
}

return uuid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2021 - 2024 Elytrium
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package net.elytrium.limboapi.injection.tablist;

import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.tablist.VelocityTabList;
import java.util.Optional;
import java.util.UUID;

public class RewritingVelocityTabList extends VelocityTabList implements RewritingTabList {

public RewritingVelocityTabList(ConnectedPlayer player) {
super(player);
}

@Override
public void addEntry(TabListEntry entry) {
super.addEntry(this.rewriteEntry(entry));
}

@Override
public Optional<TabListEntry> getEntry(UUID uuid) {
return super.getEntry(this.rewriteUuid(uuid));
}

@Override
public boolean containsEntry(UUID uuid) {
return super.containsEntry(this.rewriteUuid(uuid));
}

@Override
public Optional<TabListEntry> removeEntry(UUID uuid) {
return super.removeEntry(this.rewriteUuid(uuid));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2021 - 2024 Elytrium
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package net.elytrium.limboapi.injection.tablist;

import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.tablist.VelocityTabListLegacy;
import java.util.Optional;
import java.util.UUID;

public class RewritingVelocityTabListLegacy extends VelocityTabListLegacy implements RewritingTabList {

public RewritingVelocityTabListLegacy(ConnectedPlayer player, ProxyServer proxyServer) {
super(player, proxyServer);
}

@Override
public void addEntry(TabListEntry entry) {
super.addEntry(this.rewriteEntry(entry));
}

@Override
public Optional<TabListEntry> getEntry(UUID uuid) {
return super.getEntry(this.rewriteUuid(uuid));
}

@Override
public boolean containsEntry(UUID uuid) {
return super.containsEntry(this.rewriteUuid(uuid));
}

@Override
public Optional<TabListEntry> removeEntry(UUID uuid) {
return super.removeEntry(this.rewriteUuid(uuid));
}
}

0 comments on commit ca691d8

Please sign in to comment.