Skip to content

Commit

Permalink
The uuid mapping database
Browse files Browse the repository at this point in the history
  • Loading branch information
Fallen-Breath committed Aug 3, 2024
1 parent 667bdbd commit f9bae0b
Show file tree
Hide file tree
Showing 15 changed files with 586 additions and 74 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ A list of small tweaks I made:
- Implement player UUID rewrite, like what bungeecord does.
Make setup with online velocity + offline Minecraft server work correctly
(`online-mode=true` on velocity + `online-mode=false` on backend mc servers + `player-info-forwarding-mode=none`)
- TabList packets rewrite
- Affects `LegacyPlayerListItemPacket`, `UpsertPlayerInfoPacket`, `RemovePlayerInfoPacket` packets
- Rewrites player UUIDs inside those packets to their UUIDs in the velocity server
- Entity packets rewrite
- Rewrites player UUIDs inside player creation packets and spectator teleport packets, to their UUIDs in the velocity server
- Packets to rewrite:
- TabList packets
- Affects `LegacyPlayerListItemPacket`, `UpsertPlayerInfoPacket`, `RemovePlayerInfoPacket` packets
- Rewrites player UUIDs inside those packets to their UUIDs in the velocity server
- Entity packets
- Rewrites player UUIDs inside player creation packets and spectator teleport packets, to their UUIDs in the velocity server
- All related configs are under section `uuid-rewrite` in `velocity.toml`
- Optional external uuid mapping sqlite database support
- Enabled with `databaseEnabled = true`, database path configurable with `databasePath`
- Mapping between online / offline uuid will be updated on player connected
- The sqlite database file can be shared between multiple velocity instances
- UUID rewrite can be disabled by setting `enabled = false`

# Velocity

Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ nightconfig = "com.electronwill.night-config:toml:3.6.7"
slf4j = "org.slf4j:slf4j-api:2.0.12"
snakeyaml = "org.yaml:snakeyaml:1.33"
spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3"
sqlite-jdbc = "org.xerial:sqlite-jdbc:3.46.0.1" # [fallen's fork] player uuid rewrite - uuid database
terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0"

[bundles]
Expand Down
1 change: 1 addition & 0 deletions proxy/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ dependencies {
implementation(libs.lmbda)
implementation(libs.asm)
implementation(libs.bundles.flare)
implementation(libs.sqlite.jdbc) // [fallen's fork] player uuid rewrite - uuid database
compileOnly(libs.spotbugs.annotations)
compileOnly(libs.auto.service.annotations)
testImplementation(libs.mockito)
Expand Down
16 changes: 13 additions & 3 deletions proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.plugin.VelocityPluginManager;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.TabListUuidRewriter;
import com.velocitypowered.proxy.protocol.util.FaviconSerializer;
import com.velocitypowered.proxy.protocol.util.GameProfileSerializer;
import com.velocitypowered.proxy.scheduler.VelocityScheduler;
Expand All @@ -64,6 +63,7 @@
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiter;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiters;
import com.velocitypowered.proxy.uuidrewrite.UuidRewriteHooks;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
Expand Down Expand Up @@ -264,6 +264,9 @@ void start() {
this.cm.queryBind(configuration.getBind().getHostString(), configuration.getQueryPort());
}

// [fallen's fork] player uuid rewrite - lifecycle hook
UuidRewriteHooks.onServerStart(this);

Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics());
}

Expand Down Expand Up @@ -513,6 +516,9 @@ public void shutdown(boolean explicitExit, Component reason) {
player.disconnect(reason);
}

// [fallen's fork] player uuid rewrite - lifecycle hook
UuidRewriteHooks.onServerStop(this);

try {
boolean timedOut = false;

Expand Down Expand Up @@ -643,6 +649,10 @@ public boolean registerConnection(ConnectedPlayer connection) {
connectionsByName.put(lowerName, connection);
connectionsByUuid.put(connection.getUniqueId(), connection);
}

// [fallen's fork] player uuid rewrite - lifecycle hook
UuidRewriteHooks.onPlayerConnect(this, connection);

return true;
}

Expand All @@ -656,8 +666,8 @@ public void unregisterConnection(ConnectedPlayer connection) {
connectionsByUuid.remove(connection.getUniqueId(), connection);
connection.disconnected();

// [fallen's fork] player uuid rewrite -
TabListUuidRewriter.onPlayerDisconnect(this, connection);
// [fallen's fork] player uuid rewrite - lifecycle hook
UuidRewriteHooks.onPlayerDisconnect(this, connection);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ public class VelocityConfiguration implements ProxyConfig {
// [fallen's fork] mojang auth proxy
@Expose
private final AuthProxy authProxy;
// [fallen's fork] player uuid rewrite
@Expose
private final UuidRewrite uuidRewrite;

@Expose
private final Query query;
Expand All @@ -99,11 +102,13 @@ public class VelocityConfiguration implements ProxyConfig {

private VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced,
AuthProxy authProxy, // [fallen's fork] mojang auth proxy
UuidRewrite uuidRewrite, // // [fallen's fork] player uuid rewrite
Query query, Metrics metrics) {
this.servers = servers;
this.forcedHosts = forcedHosts;
this.advanced = advanced;
this.authProxy = authProxy; // [fallen's fork] mojang auth proxy
this.uuidRewrite = uuidRewrite; // [fallen's fork] player uuid rewrite
this.query = query;
this.metrics = metrics;
}
Expand All @@ -115,6 +120,7 @@ private VelocityConfiguration(String bind, String motd, int showMaxPlayers, bool
boolean enablePlayerAddressLogging, Servers servers, ForcedHosts forcedHosts,
Advanced advanced,
AuthProxy authProxy, // [fallen's fork] mojang auth proxy
UuidRewrite uuidRewrite, // // [fallen's fork] player uuid rewrite
Query query, Metrics metrics, boolean forceKeyAuthentication) {
this.bind = bind;
this.motd = motd;
Expand All @@ -131,6 +137,7 @@ private VelocityConfiguration(String bind, String motd, int showMaxPlayers, bool
this.forcedHosts = forcedHosts;
this.advanced = advanced;
this.authProxy = authProxy; // [fallen's fork] mojang auth proxy
this.uuidRewrite = uuidRewrite; // [fallen's fork] player uuid rewrite
this.query = query;
this.metrics = metrics;
this.forceKeyAuthentication = forceKeyAuthentication;
Expand Down Expand Up @@ -431,6 +438,24 @@ public int getAuthProxyPort() {
}
// [fallen's fork] mojang auth proxy ends

// // [fallen's fork] player uuid rewrite starts
public boolean isUuidRewriteEnabled() {
return uuidRewrite.isEnabled();
}

public boolean isUuidRewriteDatabaseEnabled() {
return uuidRewrite.isDatabaseEnabled();
}

public void setUuidRewriteDatabaseEnabled(boolean b) {
uuidRewrite.setDatabaseEnabled(b);
}

public String getUuidRewriteDatabasePath() {
return uuidRewrite.getDatabasePath();
}
// // [fallen's fork] player uuid rewrite ends

public boolean isForceKeyAuthentication() {
return forceKeyAuthentication;
}
Expand Down Expand Up @@ -524,7 +549,8 @@ public static VelocityConfiguration read(Path path) throws IOException {
final CommentedConfig serversConfig = config.get("servers");
final CommentedConfig forcedHostsConfig = config.get("forced-hosts");
final CommentedConfig advancedConfig = config.get("advanced");
final CommentedConfig autoProxy = config.get("auth-proxy");
final CommentedConfig autoProxy = config.get("auth-proxy"); // [fallen's fork] mojang auth proxy
final CommentedConfig uuidRewrite = config.get("uuid-rewrite"); // [fallen's fork] player uuid rewrite
final CommentedConfig queryConfig = config.get("query");
final CommentedConfig metricsConfig = config.get("metrics");
final PlayerInfoForwarding forwardingMode = config.getEnumOrElse(
Expand Down Expand Up @@ -566,7 +592,8 @@ public static VelocityConfiguration read(Path path) throws IOException {
new Servers(serversConfig),
new ForcedHosts(forcedHostsConfig),
new Advanced(advancedConfig),
new AuthProxy(autoProxy),
new AuthProxy(autoProxy), // [fallen's fork] mojang auth proxy
new UuidRewrite(uuidRewrite), // [fallen's fork] player uuid rewrite
new Query(queryConfig),
new Metrics(metricsConfig),
forceKeyAuthentication
Expand Down Expand Up @@ -894,6 +921,42 @@ public int getPort() {
}
}

/**
* [fallen's fork] player uuid rewrite - config.
*/
private static class UuidRewrite {
@Expose
private boolean enabled = true;
@Expose
private boolean databaseEnabled = false;
@Expose
private String databasePath = "uuid_mapping.db";

public UuidRewrite(CommentedConfig config) {
if (config != null) {
this.enabled = config.getOrElse("enabled", true);
this.databaseEnabled = config.getOrElse("databaseEnabled", false);
this.databasePath = config.getOrElse("databasePath", "uuid_mapping.db");
}
}

public boolean isEnabled() {
return enabled;
}

public boolean isDatabaseEnabled() {
return databaseEnabled;
}

public void setDatabaseEnabled(boolean databaseEnabled) {
this.databaseEnabled = databaseEnabled;
}

public String getDatabasePath() {
return databasePath;
}
}

private static class Query {

@Expose
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@
import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.EntityPacketUuidRewriter;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.TabListUuidRewriter;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnEntityS2CPacket;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnPlayerS2CPacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import com.velocitypowered.proxy.uuidrewrite.EntityPacketUuidRewriter;
import com.velocitypowered.proxy.uuidrewrite.TabListUuidRewriter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@
import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerCommandPacket;
import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket;
import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.EntityPacketUuidRewriter;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpectatorTeleportC2SPacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import com.velocitypowered.proxy.util.CharacterUtil;
import com.velocitypowered.proxy.uuidrewrite.EntityPacketUuidRewriter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,73 +15,59 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.velocitypowered.proxy.protocol.packet.uuidrewrite;
package com.velocitypowered.proxy.uuidrewrite;

import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.protocol.packet.uuidrewrite.PacketToRewriteEntityUuid;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.UUID;
import java.util.function.Function;

/**
* [fallen's fork] player uuid rewrite - entity packets: rewrite logic.
*/
public class EntityPacketUuidRewriter {

private static final boolean DEBUG = false;
private static final Logger logger = LogManager.getLogger(EntityPacketUuidRewriter.class);

public static void rewriteS2C(VelocityServer server, Player connectionPlayer, PacketToRewriteEntityUuid packet) {
rewrite(server, connectionPlayer, packet, Player::getOfflineUuid, Player::getUniqueId);
rewrite(server, connectionPlayer, packet, RewriteDirection.S2C);
}

public static void rewriteC2S(VelocityServer server, Player connectionPlayer, PacketToRewriteEntityUuid packet) {
rewrite(server, connectionPlayer, packet, Player::getUniqueId, Player::getOfflineUuid);
rewrite(server, connectionPlayer, packet, RewriteDirection.C2S);
}

private static void rewrite(VelocityServer server, Player connectionPlayer, PacketToRewriteEntityUuid packet,
Function<Player, UUID> uuidFrom, Function<Player, UUID> uuidTo) {
RewriteDirection direction) {
if (DEBUG) {
logger.info("EPUR for {} start, packet {} ({} {})", connectionPlayer.getUsername(), packet.getClass().getSimpleName(), packet.isPlayer(), packet.getEntityUuid());
logger.info("EPUR for {} start, packet {} ({} {})", connectionPlayer.getUsername(), packet.getClass().getSimpleName(),
packet.isPlayer(), packet.getEntityUuid());
}

var config = server.getConfiguration();
if (!(config.isOnlineMode() && config.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE)) {
return; // early return for performance optimization
}
if (!packet.isPlayer()) {
if (!UuidRewriteUtils.isUuidRewriteEnabled(server.getConfiguration())) {
return;
}
UUID uuid = packet.getEntityUuid();
if (uuid == null) {
if (!packet.isPlayer()) {
return;
}

if (DEBUG) {
logger.info("EPUR for {} check pass, uuid to rewrite: {}", connectionPlayer.getUsername(), uuid);
final UUID oldUuid = packet.getEntityUuid();
if (oldUuid == null) {
return;
}

// FIXME: inefficient implementation using for loop. Replace it with map lookup?
var rewriter = UuidRewriter.create(server);
for (Player player : server.getAllPlayers()) {
UUID serverUuid = uuidFrom.apply(player);
if (DEBUG) {
logger.info("EPUR for {} checking {} {}", connectionPlayer.getUsername(), player.getUsername(), serverUuid);
}
if (serverUuid.equals(uuid)) {
if (DEBUG) {
logger.info("EPUR for {} match {}", connectionPlayer.getUsername(), player.getUsername());
}

UUID newUuid = uuidTo.apply(player);
if (!newUuid.equals(uuid)) {
if (direction.getSourceUuid(player).equals(oldUuid)) {
var newUuid = rewriter.rewrite(player, direction);
if (newUuid != null && !newUuid.equals(oldUuid)) {
packet.setEntityUuid(newUuid);
}
break;
}
}

if (DEBUG) {
logger.info("EPUR for {} check end", connectionPlayer.getUsername());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2024 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.velocitypowered.proxy.uuidrewrite;

import com.velocitypowered.api.proxy.Player;
import java.util.UUID;
import java.util.function.Function;

/**
* [fallen's fork] player uuid rewrite - implementation.
*/
public enum RewriteDirection {
OFFLINE_TO_ONLINE(Player::getOfflineUuid),
ONLINE_TO_OFFLINE(Player::getUniqueId);

public static final RewriteDirection S2C = OFFLINE_TO_ONLINE;
public static final RewriteDirection C2S = ONLINE_TO_OFFLINE;

private final Function<Player, UUID> uuidExtractor;

RewriteDirection(Function<Player, UUID> uuidExtractor) {
this.uuidExtractor = uuidExtractor;
}

UUID getSourceUuid(Player player) {
return this.uuidExtractor.apply(player);
}
}
Loading

0 comments on commit f9bae0b

Please sign in to comment.