mirror of
https://github.com/CivMC/Civ.git
synced 2026-07-18 00:20:44 +00:00
wip
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
plugins {
|
||||
alias(libs.plugins.shadow)
|
||||
}
|
||||
version = "1.0.0"
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT")
|
||||
compileOnly("us.ajg0702.queue.api:api:2.8.0")
|
||||
implementation(libs.hikaricp)
|
||||
api(libs.configurate.yaml)
|
||||
compileOnly(libs.luckperms.api)
|
||||
annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT")
|
||||
}
|
||||
|
||||
@@ -2,30 +2,22 @@ package net.civmc.civproxy;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.player.KickedFromServerEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.plugin.Dependency;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.model.data.TemporaryNodeMergeStrategy;
|
||||
import net.luckperms.api.model.user.UserManager;
|
||||
import net.luckperms.api.node.types.PermissionNode;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import javax.sql.DataSource;
|
||||
import net.civmc.civproxy.renamer.PlayerRenamer;
|
||||
import org.slf4j.Logger;
|
||||
import us.ajg0702.queue.api.AjQueueAPI;
|
||||
import us.ajg0702.queue.api.QueueManager;
|
||||
import us.ajg0702.queue.api.players.AdaptedPlayer;
|
||||
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
@Plugin(id = "civproxy", name = "CivProxy", version = "1.0.0", authors = {"Okx"}, dependencies = {@Dependency(id = "ajqueue"), @Dependency(id = "luckperms")})
|
||||
public class CivProxyPlugin {
|
||||
@@ -33,16 +25,17 @@ public class CivProxyPlugin {
|
||||
private final ProxyServer server;
|
||||
private final Logger logger;
|
||||
|
||||
private CommentedConfigurationNode config;
|
||||
|
||||
private DataSource source;
|
||||
|
||||
@Inject
|
||||
public CivProxyPlugin(ProxyServer server, Logger logger) {
|
||||
public CivProxyPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private final Map<Player, QueueRecord> players = new ConcurrentHashMap<>();
|
||||
|
||||
record QueueRecord(Instant instant, String server) {
|
||||
|
||||
loadConfig(dataDirectory);
|
||||
loadConnection();
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
@@ -52,131 +45,63 @@ public class CivProxyPlugin {
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
new PlayerCount(this, server).start();
|
||||
new PlayerRenamer(this, server, source).start();
|
||||
new QueueListener(this, server).start();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onToMain(KickedFromServerEvent event) {
|
||||
// Other than banned players, kicked servers should go to the queue on the PvP server
|
||||
private void loadConnection() {
|
||||
CommentedConfigurationNode database = config.node("database");
|
||||
|
||||
String name = event.getServer().getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl("jdbc:" + database.node("driver").getString("mysql") + "://" + database.node("host").getString("localhost") + ":" +
|
||||
database.node("port").getInt(3306) + "/" + database.node("database").getString("minecraft"));
|
||||
config.setConnectionTimeout(database.node("connection_timeout").getInt(10_000));
|
||||
config.setIdleTimeout(database.node("idle_timeout").getInt(600_000));
|
||||
config.setMaxLifetime(database.node("max_lifetime").getInt(7_200_000));
|
||||
config.setMaximumPoolSize(database.node("poolsize").getInt(10));
|
||||
config.setUsername(database.node("username").getString("root"));
|
||||
String password = database.node("password").getString();
|
||||
if (password != null && !password.isBlank()) {
|
||||
config.setPassword(password);
|
||||
}
|
||||
if (event.getPlayer().getCurrentServer().isPresent()) {
|
||||
return;
|
||||
}
|
||||
event.setResult(KickedFromServerEvent.RedirectPlayer.create(server.getServer("pvp").get()));
|
||||
|
||||
players.put(event.getPlayer(), new QueueRecord(Instant.now(), name));
|
||||
this.source = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onFromPvP(KickedFromServerEvent event) {
|
||||
String name = event.getServer().getServerInfo().getName();
|
||||
if (!name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.getResult() instanceof KickedFromServerEvent.RedirectPlayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setResult(KickedFromServerEvent.DisconnectPlayer.create(event.getServerKickReason().orElse(Component.text("Disconnected"))));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConnect(ServerPreConnectEvent event) {
|
||||
// If we are close to the cap, don't allow direct connections to the server and force them to go to the queue
|
||||
// This prevents players being able to snipe positions and bypass the queue
|
||||
|
||||
if (event.getPreviousServer() != null) {
|
||||
return;
|
||||
}
|
||||
RegisteredServer mainServer = event.getOriginalServer();
|
||||
String name = mainServer.getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mainServer.getPlayersConnected().size() >= 145 && !event.getPlayer().hasPermission("joinbypass.use")) {
|
||||
// RegisteredServer mini = server.getServer("mini").orElse(null);
|
||||
// if (mini != null && mini.getPlayersConnected().size() < 110) {
|
||||
// event.setResult(ServerPreConnectEvent.ServerResult.allowed(server.getServer("mini").get()));
|
||||
// } else {
|
||||
event.setResult(ServerPreConnectEvent.ServerResult.allowed(server.getServer("pvp").get()));
|
||||
// }
|
||||
|
||||
players.put(event.getPlayer(), new QueueRecord(Instant.now(), name));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConnect(ServerPostConnectEvent event) {
|
||||
// Add players coming from the main server to the queue
|
||||
|
||||
QueueRecord record = players.remove(event.getPlayer());
|
||||
if (record != null && record.instant().isAfter(Instant.now().minusSeconds(60))) {
|
||||
QueueManager queueManager = AjQueueAPI.getInstance().getQueueManager();
|
||||
AdaptedPlayer player = AjQueueAPI.getInstance().getPlatformMethods().getPlayer(event.getPlayer().getUniqueId());
|
||||
queueManager.addToQueue(player, record.server());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChangeFromMain(ServerPreConnectEvent event) {
|
||||
if (event.getPreviousServer() == null) {
|
||||
return;
|
||||
}
|
||||
String name = event.getPreviousServer().getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
addPriority(event.getPlayer(), name);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onKick(KickedFromServerEvent event) {
|
||||
RegisteredServer server = event.getServer();
|
||||
String name = server.getServerInfo().getName();
|
||||
if (name.equals("pvp")
|
||||
|| event.kickedDuringServerConnect()
|
||||
|| event.getPlayer().getCurrentServer().map(c -> c.getServerInfo().getName().equals("pvp")).orElse(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
addPriority(event.getPlayer(), name);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDisconnect(DisconnectEvent event) {
|
||||
ServerConnection server = event.getPlayer().getCurrentServer().orElse(null);
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
String name = server.getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
addPriority(event.getPlayer(), name);
|
||||
}
|
||||
|
||||
private void addPriority(Player player, String server) {
|
||||
// Players who just disconnected get 5 minutes of queue priority
|
||||
|
||||
UserManager userManager = LuckPermsProvider.get().getUserManager();
|
||||
userManager.loadUser(player.getUniqueId()).thenAccept(user -> {
|
||||
if (user == null) {
|
||||
return;
|
||||
/**
|
||||
* Loads the config from disk, and creates it if necessary
|
||||
*/
|
||||
private void loadConfig(Path dataDirectory) {
|
||||
try {
|
||||
// ensure data directory exists
|
||||
if (!Files.exists(dataDirectory)) {
|
||||
Files.createDirectories(dataDirectory);
|
||||
}
|
||||
user.data().add(
|
||||
PermissionNode.builder()
|
||||
.permission("ajqueue.serverpriority." + server + ".10")
|
||||
.expiry(5, TimeUnit.MINUTES)
|
||||
.build(),
|
||||
TemporaryNodeMergeStrategy.REPLACE_EXISTING_IF_DURATION_LONGER);
|
||||
userManager.saveUser(user);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not create data directory: {}", dataDirectory, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// create config file if it doesn't exist
|
||||
Path configFile = dataDirectory.resolve("config.yml");
|
||||
if (!Files.exists(configFile)) {
|
||||
try (InputStream in = getClass().getResourceAsStream("/config.yml")) {
|
||||
if (in != null) {
|
||||
Files.copy(in, configFile);
|
||||
logger.info("Default configuration file created.");
|
||||
} else {
|
||||
logger.error("Default configuration file is missing in resources!");
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not create default configuration file: {}", configFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder().path(configFile).build();
|
||||
try {
|
||||
config = loader.load();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not load configuration file: " + configFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package net.civmc.civproxy;
|
||||
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.player.KickedFromServerEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.model.data.TemporaryNodeMergeStrategy;
|
||||
import net.luckperms.api.model.user.UserManager;
|
||||
import net.luckperms.api.node.types.PermissionNode;
|
||||
import us.ajg0702.queue.api.AjQueueAPI;
|
||||
import us.ajg0702.queue.api.QueueManager;
|
||||
import us.ajg0702.queue.api.players.AdaptedPlayer;
|
||||
|
||||
public class QueueListener {
|
||||
|
||||
private final Map<Player, QueueRecord> players = new ConcurrentHashMap<>();
|
||||
|
||||
private final CivProxyPlugin plugin;
|
||||
private final ProxyServer server;
|
||||
|
||||
public QueueListener(CivProxyPlugin plugin, ProxyServer server) {
|
||||
this.plugin = plugin;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
record QueueRecord(Instant instant, String server) {
|
||||
|
||||
}
|
||||
|
||||
public void start() {
|
||||
server.getEventManager().register(plugin, this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onToMain(KickedFromServerEvent event) {
|
||||
// Other than banned players, kicked servers should go to the queue on the PvP server
|
||||
|
||||
String name = event.getServer().getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
if (event.getPlayer().getCurrentServer().isPresent()) {
|
||||
return;
|
||||
}
|
||||
event.setResult(KickedFromServerEvent.RedirectPlayer.create(server.getServer("pvp").get()));
|
||||
|
||||
players.put(event.getPlayer(), new QueueRecord(Instant.now(), name));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onFromPvP(KickedFromServerEvent event) {
|
||||
String name = event.getServer().getServerInfo().getName();
|
||||
if (!name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.getResult() instanceof KickedFromServerEvent.RedirectPlayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setResult(KickedFromServerEvent.DisconnectPlayer.create(event.getServerKickReason().orElse(Component.text("Disconnected"))));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConnect(ServerPreConnectEvent event) {
|
||||
// If we are close to the cap, don't allow direct connections to the server and force them to go to the queue
|
||||
// This prevents players being able to snipe positions and bypass the queue
|
||||
|
||||
if (event.getPreviousServer() != null) {
|
||||
return;
|
||||
}
|
||||
RegisteredServer mainServer = event.getOriginalServer();
|
||||
String name = mainServer.getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mainServer.getPlayersConnected().size() >= 145 && !event.getPlayer().hasPermission("joinbypass.use")) {
|
||||
// RegisteredServer mini = server.getServer("mini").orElse(null);
|
||||
// if (mini != null && mini.getPlayersConnected().size() < 110) {
|
||||
// event.setResult(ServerPreConnectEvent.ServerResult.allowed(server.getServer("mini").get()));
|
||||
// } else {
|
||||
event.setResult(ServerPreConnectEvent.ServerResult.allowed(server.getServer("pvp").get()));
|
||||
// }
|
||||
|
||||
players.put(event.getPlayer(), new QueueRecord(Instant.now(), name));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConnect(ServerPostConnectEvent event) {
|
||||
// Add players coming from the main server to the queue
|
||||
|
||||
QueueRecord record = players.remove(event.getPlayer());
|
||||
if (record != null && record.instant().isAfter(Instant.now().minusSeconds(60))) {
|
||||
QueueManager queueManager = AjQueueAPI.getInstance().getQueueManager();
|
||||
AdaptedPlayer player = AjQueueAPI.getInstance().getPlatformMethods().getPlayer(event.getPlayer().getUniqueId());
|
||||
queueManager.addToQueue(player, record.server());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChangeFromMain(ServerPreConnectEvent event) {
|
||||
if (event.getPreviousServer() == null) {
|
||||
return;
|
||||
}
|
||||
String name = event.getPreviousServer().getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
addPriority(event.getPlayer(), name);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onKick(KickedFromServerEvent event) {
|
||||
RegisteredServer server = event.getServer();
|
||||
String name = server.getServerInfo().getName();
|
||||
if (name.equals("pvp")
|
||||
|| event.kickedDuringServerConnect()
|
||||
|| event.getPlayer().getCurrentServer().map(c -> c.getServerInfo().getName().equals("pvp")).orElse(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
addPriority(event.getPlayer(), name);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDisconnect(DisconnectEvent event) {
|
||||
ServerConnection server = event.getPlayer().getCurrentServer().orElse(null);
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
String name = server.getServerInfo().getName();
|
||||
if (name.equals("pvp")) {
|
||||
return;
|
||||
}
|
||||
|
||||
addPriority(event.getPlayer(), name);
|
||||
}
|
||||
|
||||
private void addPriority(Player player, String server) {
|
||||
// Players who just disconnected get 5 minutes of queue priority
|
||||
|
||||
UserManager userManager = LuckPermsProvider.get().getUserManager();
|
||||
userManager.loadUser(player.getUniqueId()).thenAccept(user -> {
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
user.data().add(
|
||||
PermissionNode.builder()
|
||||
.permission("ajqueue.serverpriority." + server + ".10")
|
||||
.expiry(5, TimeUnit.MINUTES)
|
||||
.build(),
|
||||
TemporaryNodeMergeStrategy.REPLACE_EXISTING_IF_DURATION_LONGER);
|
||||
userManager.saveUser(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package net.civmc.civproxy.database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class Migrator {
|
||||
|
||||
private final Map<String, NavigableMap<Integer, String[]>> migrations = new HashMap<>();
|
||||
|
||||
public void registerMigration(String namespace, int id, String... sql) {
|
||||
Objects.requireNonNull(namespace);
|
||||
Objects.requireNonNull(sql);
|
||||
if (namespace.length() > 64) {
|
||||
throw new IllegalArgumentException("namespace must not be longer than 64 characters");
|
||||
}
|
||||
if (id < 0) {
|
||||
throw new IllegalArgumentException("migation id must be at least 0");
|
||||
}
|
||||
boolean present = this.migrations.computeIfAbsent(namespace, k -> new TreeMap<>()).putIfAbsent(id, sql) != null;
|
||||
if (present) {
|
||||
throw new IllegalStateException("Migration already exists with namespace " + namespace + " and ID " + id);
|
||||
}
|
||||
}
|
||||
|
||||
public void migrate(Connection connection) throws SQLException {
|
||||
connection.setAutoCommit(false);
|
||||
connection.createStatement().executeUpdate("CREATE TABLE IF NOT EXISTS migrations (" +
|
||||
"namespace VARCHAR(64) PRIMARY KEY," +
|
||||
"id INT NOT NULL)");
|
||||
|
||||
for (Map.Entry<String, NavigableMap<Integer, String[]>> entry : migrations.entrySet()) {
|
||||
PreparedStatement getMigrationId = connection.prepareStatement("SELECT id FROM migrations WHERE namespace = ? FOR UPDATE");
|
||||
getMigrationId.setString(1, entry.getKey());
|
||||
ResultSet resultSet = getMigrationId.executeQuery();
|
||||
int minId;
|
||||
if (resultSet.next()) {
|
||||
minId = resultSet.getInt("id");
|
||||
} else {
|
||||
minId = -1;
|
||||
}
|
||||
|
||||
NavigableMap<Integer, String[]> value = entry.getValue().tailMap(minId, false);
|
||||
int maxId = entry.getValue().lastKey();
|
||||
for (String[] migration : value.sequencedValues()) {
|
||||
for (String sql : migration) {
|
||||
connection.createStatement().executeUpdate(sql);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxId != minId) {
|
||||
PreparedStatement setMigrationId = connection.prepareStatement("REPLACE INTO migations (namespace, id) VALUES (?, ?)");
|
||||
setMigrationId.setString(1, entry.getKey());
|
||||
setMigrationId.setInt(2, maxId);
|
||||
}
|
||||
}
|
||||
connection.setAutoCommit(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package net.civmc.civproxy.renamer;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.sql.DataSource;
|
||||
import net.civmc.civproxy.database.Migrator;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class AssociationList {
|
||||
|
||||
private DataSource db;
|
||||
private Logger logger;
|
||||
|
||||
private static final String addPlayer = "call addplayertotable(?, ?)"; // order player name, uuid
|
||||
private static final String getUUIDfromPlayer = "select uuid from Name_player where player=?";
|
||||
private static final String getPlayerfromUUID = "select player from Name_player where uuid=?";
|
||||
private static final String changePlayerName = "update Name_player set player=? where uuid=?";
|
||||
private static final String getAllPlayerInfo = "select * from Name_player";
|
||||
|
||||
public AssociationList(Logger logger, DataSource db) {
|
||||
this.db = db;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void migrate() {
|
||||
Migrator migrator = new Migrator();
|
||||
// creates the player table
|
||||
// Where uuid and host names will be stored
|
||||
migrator.registerMigration("renamer", 0,
|
||||
"CREATE TABLE IF NOT EXISTS `Name_player` (" +
|
||||
"`uuid` varchar(40) NOT NULL," +
|
||||
"`player` varchar(40) NOT NULL,"
|
||||
+ "UNIQUE KEY `uuid_player_combo` (`uuid`, `player`));",
|
||||
// this creates the table needed for when a player changes there name to a prexisting name before joining the server
|
||||
"create table if not exists playercountnames ("
|
||||
+ "player varchar(40) not null,"
|
||||
+ "amount int(10) not null,"
|
||||
+ "primary key (player));");
|
||||
|
||||
migrator.registerMigration("renamer", 1,
|
||||
"drop procedure if exists addplayertotable",
|
||||
"create definer=current_user procedure addplayertotable("
|
||||
+ "in pl varchar(40), in uu varchar(40)) sql security invoker begin "
|
||||
+ ""
|
||||
+ "declare account varchar(40);"
|
||||
+ "declare nameamount int(10);"
|
||||
+ ""
|
||||
+ "set @@SESSION.max_sp_recursion_depth = 30;"
|
||||
+ ""
|
||||
+ "set nameamount=0;"
|
||||
+ "set nameamount=(select count(*) from Name_player p where p.uuid=uu);"
|
||||
+ ""
|
||||
+ "if (nameamount < 1) then"
|
||||
+ " setName: loop"
|
||||
+ " set account =(select uuid from Name_player p where p.player=pl);"
|
||||
+ " if (account not like uu) then"
|
||||
+ ""
|
||||
+ " if (nameamount > 0) then"
|
||||
+ " set pl = (select concat(SUBSTRING(pl, 1, length(pl)-1)));"
|
||||
+ " end if;"
|
||||
+ ""
|
||||
+ " insert ignore into playercountnames (player, amount) values (pl, 0);"
|
||||
+ ""
|
||||
+ " update playercountnames set amount = nameamount+1 where player=pl;"
|
||||
+ ""
|
||||
+ " set nameamount=(select amount from playercountnames where player=pl);"
|
||||
+ ""
|
||||
+ " set pl = (select concat (pl,nameamount));"
|
||||
+ ""
|
||||
+ " set account =(select uuid from Name_player p where p.player=pl);"
|
||||
+ ""
|
||||
+ " if (account not like uu) then"
|
||||
+ " iterate setName;"
|
||||
+ " end if;"
|
||||
+ " else"
|
||||
+ " insert ignore into Name_player (player, uuid) values (pl, uu);"
|
||||
+ " leave SetName;"
|
||||
+ " end if;"
|
||||
+ "END LOOP setName;"
|
||||
+ "end if;"
|
||||
+ "end");
|
||||
|
||||
try {
|
||||
migrator.migrate(db.getConnection());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns null if no uuid was found
|
||||
*
|
||||
* @param playername the player's name
|
||||
* @return the UUID of the player, or null
|
||||
*/
|
||||
public UUID getUUID(String playername) {
|
||||
try (Connection connection = db.getConnection();
|
||||
PreparedStatement getUUIDfromPlayer = connection.prepareStatement(AssociationList.getUUIDfromPlayer);) {
|
||||
getUUIDfromPlayer.setString(1, playername);
|
||||
try (ResultSet set = getUUIDfromPlayer.executeQuery();) {
|
||||
if (!set.next() || set.wasNull()) return null;
|
||||
String uuid = set.getString("uuid");
|
||||
return UUID.fromString(uuid);
|
||||
} catch (SQLException se) {
|
||||
logger.warn("Failed to get UUID for playername " + playername, se);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Failed to set up query to get UUID for playername " + playername, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns null if no playername was found
|
||||
*
|
||||
* @param uuid get the current server's name for this UUId
|
||||
* @return the player's name if found
|
||||
*/
|
||||
public String getCurrentName(UUID uuid) {
|
||||
try (Connection connection = db.getConnection();
|
||||
PreparedStatement getPlayerfromUUID = connection.prepareStatement(AssociationList.getPlayerfromUUID);) {
|
||||
getPlayerfromUUID.setString(1, uuid.toString());
|
||||
try (ResultSet set = getPlayerfromUUID.executeQuery();) {
|
||||
if (!set.next()) return null;
|
||||
String playername = set.getString("player");
|
||||
return playername;
|
||||
} catch (SQLException se) {
|
||||
logger.warn("Failed to get current player name for UUID " + uuid, se);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Failed to set up query to get current player name for UUID " + uuid, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addPlayer(String playername, UUID uuid) {
|
||||
try (Connection connection = db.getConnection();
|
||||
PreparedStatement addPlayer = connection.prepareStatement(AssociationList.addPlayer);) {
|
||||
addPlayer.setString(1, playername);
|
||||
addPlayer.setString(2, uuid.toString());
|
||||
addPlayer.execute();
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Failed to add new player mapping {0} <==> {1}, due to {2}",
|
||||
new Object[]{playername, uuid, e.getMessage()});
|
||||
logger.warn("Add new player failure: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void changePlayer(String newName, UUID uuid) {
|
||||
try (Connection connection = db.getConnection();
|
||||
PreparedStatement changePlayerName = connection.prepareStatement(AssociationList.changePlayerName);) {
|
||||
changePlayerName.setString(1, newName);
|
||||
changePlayerName.setString(2, uuid.toString());
|
||||
changePlayerName.execute();
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Failed to change player name mapping {0} <==> {1}, due to {2}",
|
||||
new Object[]{newName, uuid, e.getMessage()});
|
||||
logger.warn("Change player failure: ", e);
|
||||
return; // don't add on failure
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns all player info in the table. It is used mainly
|
||||
* by NameAPI class to prepopulate the maps.
|
||||
* As such PlayerMappingInfo.nameMapping will return Map<String, UUID>
|
||||
* while PlayerMappingInfo.uuidMapping will return Map<UUID, String>
|
||||
*
|
||||
* @return the player mapping info is possible
|
||||
*/
|
||||
public PlayerMappingInfo getAllPlayerInfo() {
|
||||
Map<String, UUID> nameMapping = new HashMap<String, UUID>();
|
||||
Map<UUID, String> uuidMapping = new HashMap<UUID, String>();
|
||||
try (Connection connection = db.getConnection();
|
||||
PreparedStatement getAllPlayerInfo = connection.prepareStatement(AssociationList.getAllPlayerInfo);
|
||||
ResultSet set = getAllPlayerInfo.executeQuery();) {
|
||||
while (set.next()) {
|
||||
UUID uuid = UUID.fromString(set.getString("uuid"));
|
||||
String playername = set.getString("player");
|
||||
nameMapping.put(playername, uuid);
|
||||
uuidMapping.put(uuid, playername);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Failed to get all player info", e);
|
||||
}
|
||||
return new PlayerMappingInfo(nameMapping, uuidMapping);
|
||||
}
|
||||
|
||||
public static class PlayerMappingInfo {
|
||||
|
||||
public final Map<String, UUID> nameMapping;
|
||||
public final Map<UUID, String> uuidMapping;
|
||||
|
||||
public PlayerMappingInfo(Map<String, UUID> nameMap, Map<UUID, String> uuidMap) {
|
||||
this.nameMapping = nameMap;
|
||||
this.uuidMapping = uuidMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.civmc.civproxy.renamer;
|
||||
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChangePlayerNameCommand implements SimpleCommand {
|
||||
|
||||
private final ProxyServer server;
|
||||
private final AssociationList associationList;
|
||||
|
||||
public ChangePlayerNameCommand(ProxyServer server, AssociationList associationList) {
|
||||
this.server = server;
|
||||
this.associationList = associationList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Invocation invocation) {
|
||||
CommandSource source = invocation.source();
|
||||
String[] args = invocation.arguments();
|
||||
if (args.length < 2) {
|
||||
source.sendPlainMessage("Usage: /" + invocation.alias() + " <old player name> <new player name>");
|
||||
return;
|
||||
}
|
||||
|
||||
UUID uuid = associationList.getUUID(args[0]);
|
||||
if (uuid == null) {
|
||||
source.sendPlainMessage("Player not found");
|
||||
return;
|
||||
}
|
||||
String newName = args[1].length() >= 16 ? args[1].substring(0, 16) : args[1];
|
||||
associationList.changePlayer(newName, uuid);
|
||||
source.sendPlainMessage("Changed name of " + args[0] + " to " + newName);
|
||||
|
||||
server.getPlayer(uuid).ifPresent(player ->
|
||||
player.disconnect(Component.text("Your name has been changed! Please rejoin.")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Invocation invocation) {
|
||||
return invocation.source().hasPermission("civproxy.changeplayername");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.civmc.civproxy.renamer;
|
||||
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import javax.sql.DataSource;
|
||||
import net.civmc.civproxy.CivProxyPlugin;
|
||||
|
||||
public class PlayerRenamer {
|
||||
|
||||
private final CivProxyPlugin plugin;
|
||||
private final ProxyServer server;
|
||||
|
||||
private final AssociationList associations;
|
||||
|
||||
public PlayerRenamer(CivProxyPlugin plugin, ProxyServer server, DataSource source) {
|
||||
this.plugin = plugin;
|
||||
this.server = server;
|
||||
this.associations = new AssociationList(plugin.getLogger(), source);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void on(GameProfileRequestEvent requestEvent) {
|
||||
GameProfile profile = requestEvent.getGameProfile();
|
||||
associations.addPlayer(profile.getName(), profile.getId());
|
||||
|
||||
String name = associations.getCurrentName(profile.getId());
|
||||
if (name == null) {
|
||||
associations.addPlayer(profile.getName(), profile.getId());
|
||||
name = associations.getCurrentName(profile.getId());
|
||||
}
|
||||
|
||||
requestEvent.setGameProfile(requestEvent.getGameProfile().withName(name));
|
||||
}
|
||||
|
||||
public void start() {
|
||||
server.getEventManager().register(plugin, this);
|
||||
server.getCommandManager().register(server.getCommandManager().metaBuilder("changeplayername").aliases("nlcpn").plugin(this).build(),
|
||||
new ChangePlayerNameCommand(server, associations));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user