Initial Stargate plugin: Paper gate plugin + Velocity/Bungee cross-server bridges

This commit is contained in:
Michael Burgess
2026-08-09 09:53:55 -04:00
commit 83a0392ee9
34 changed files with 2317 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
plugins {
id("com.gradleup.shadow") version "8.3.5"
}
dependencies {
implementation(project(":stargate-common"))
compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT")
annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT")
}
tasks.shadowJar {
archiveClassifier.set("")
}
tasks.build {
dependsOn(tasks.shadowJar)
}
@@ -0,0 +1,110 @@
package dev.skywalker3200.stargate.velocity;
import com.google.inject.Inject;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import dev.skywalker3200.stargate.common.network.StargateChannel;
import org.slf4j.Logger;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Relays stargate cross-server teleport requests: when a backend server asks to move a
* player to another backend, this plugin connects them there, then forwards a "deliver"
* message to the new backend once they land so the Paper plugin can warp them to the gate.
*/
@Plugin(id = "stargate-velocity", name = "Stargate Velocity Bridge", version = "1.0.0", authors = {"skywalker3200"})
public class StargateVelocityPlugin {
private final ProxyServer server;
private final Logger logger;
private final ChannelIdentifier channel = MinecraftChannelIdentifier.from(StargateChannel.CHANNEL);
// playerId -> (targetServerName, gateId) pending until they finish connecting
private final Map<UUID, PendingWarp> pending = new ConcurrentHashMap<>();
private record PendingWarp(String gateId) {}
@Inject
public StargateVelocityPlugin(ProxyServer server, Logger logger) {
this.server = server;
this.logger = logger;
}
@Subscribe
public void onInit(ProxyInitializeEvent event) {
server.getChannelRegistrar().register(channel);
logger.info("Stargate Velocity bridge ready on channel {}", StargateChannel.CHANNEL);
}
@Subscribe
public void onPluginMessage(PluginMessageEvent event) {
if (!event.getIdentifier().equals(channel)) return;
event.setResult(PluginMessageEvent.ForwardResult.handled());
byte[] data = event.getData();
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
byte op = in.readByte();
if (op != StargateChannel.OP_TELEPORT_REQUEST) return;
UUID playerId = UUID.fromString(in.readUTF());
String targetServer = in.readUTF();
String gateId = in.readUTF();
Optional<Player> playerOpt = server.getPlayer(playerId);
Optional<RegisteredServer> targetOpt = server.getServer(targetServer);
if (playerOpt.isEmpty() || targetOpt.isEmpty()) {
logger.warn("Stargate teleport request for unknown player/server ({}/{})", playerId, targetServer);
return;
}
pending.put(playerId, new PendingWarp(gateId));
playerOpt.get().createConnectionRequest(targetOpt.get()).connectWithIndication().thenAccept(success -> {
if (Boolean.TRUE.equals(success)) {
deliver(playerId, targetOpt.get());
} else {
pending.remove(playerId);
}
});
} catch (Exception e) {
logger.warn("Failed to process stargate plugin message", e);
}
}
private void deliver(UUID playerId, RegisteredServer server) {
PendingWarp warp = pending.remove(playerId);
if (warp == null) return;
Optional<ServerConnection> connection = server.getPlayersConnected().stream()
.filter(p -> p.getUniqueId().equals(playerId))
.findFirst()
.flatMap(Player::getCurrentServer);
if (connection.isEmpty()) return;
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bytes);
out.writeByte(StargateChannel.OP_TELEPORT_DELIVER);
out.writeUTF(playerId.toString());
out.writeUTF(warp.gateId());
connection.get().sendPluginMessage(channel, bytes.toByteArray());
} catch (Exception e) {
logger.warn("Failed to deliver stargate teleport", e);
}
}
}