Ajoute la portee per-server pour les votes en attente + credits/licence

pending-votes.scope (config.properties Velocity) = global (defaut,
comportement historique) ou per-server. En per-server, un vote hors
ligne incremente le compteur de chaque serveur de direct-vote.servers
independamment (nouvelle table nb_votes_attente_serveur) ; /claim sur
un serveur ne remet a 0 que ce serveur-la. Necessite server-name
(config.yml) et storage.pending-votes-scope assortis cote Spigot.

Ajoute LICENSE (MIT) et credit Sar_Tron (plugin.yml, @Plugin Velocity,
pom parent, README) en vue de la publication publique.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
SarTron-NorthBlue
2026-07-12 14:33:22 +04:00
co-authored by Claude Sonnet 5
parent 74b2914529
commit 261e220d1a
11 changed files with 238 additions and 20 deletions
@@ -57,16 +57,24 @@ public class MySqlPendingVoteStore implements PendingVoteStore {
}
private void createTableIfNotExists() {
String sql = "CREATE TABLE IF NOT EXISTS nb_votes_attente (" +
String globalSql = "CREATE TABLE IF NOT EXISTS nb_votes_attente (" +
"player_uuid VARCHAR(36) NOT NULL PRIMARY KEY," +
"player_name VARCHAR(16) NOT NULL," +
"nombre_votes INT DEFAULT 0" +
")";
String perServerSql = "CREATE TABLE IF NOT EXISTS nb_votes_attente_serveur (" +
"player_uuid VARCHAR(36) NOT NULL," +
"server_name VARCHAR(64) NOT NULL," +
"player_name VARCHAR(16) NOT NULL," +
"nombre_votes INT DEFAULT 0," +
"PRIMARY KEY (player_uuid, server_name)" +
")";
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(sql);
statement.execute(globalSql);
statement.execute(perServerSql);
} catch (SQLException e) {
logger.error("Impossible de creer la table nb_votes_attente", e);
logger.error("Impossible de creer les tables de votes en attente", e);
}
}
@@ -77,20 +85,46 @@ public class MySqlPendingVoteStore implements PendingVoteStore {
logger.error("Impossible d'enregistrer le vote en attente pour {} : pool MySQL indisponible", playerName);
return;
}
String sql = "INSERT INTO nb_votes_attente (player_uuid, player_name, nombre_votes) " +
"VALUES (?, ?, 1) " +
"ON DUPLICATE KEY UPDATE nombre_votes = nombre_votes + 1, player_name = VALUES(player_name)";
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, uuid.toString());
statement.setString(2, playerName);
statement.executeUpdate();
} catch (SQLException e) {
logger.error("Erreur lors de l'insertion du vote en attente pour {}", playerName, e);
if (config.isPerServerScope()) {
addPendingVotePerServer(uuid, playerName);
} else {
addPendingVoteGlobal(uuid, playerName);
}
}, asyncExecutor);
}
private void addPendingVoteGlobal(UUID uuid, String playerName) {
String sql = "INSERT INTO nb_votes_attente (player_uuid, player_name, nombre_votes) " +
"VALUES (?, ?, 1) " +
"ON DUPLICATE KEY UPDATE nombre_votes = nombre_votes + 1, player_name = VALUES(player_name)";
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, uuid.toString());
statement.setString(2, playerName);
statement.executeUpdate();
} catch (SQLException e) {
logger.error("Erreur lors de l'insertion du vote en attente pour {}", playerName, e);
}
}
private void addPendingVotePerServer(UUID uuid, String playerName) {
String sql = "INSERT INTO nb_votes_attente_serveur (player_uuid, server_name, player_name, nombre_votes) " +
"VALUES (?, ?, ?, 1) " +
"ON DUPLICATE KEY UPDATE nombre_votes = nombre_votes + 1, player_name = VALUES(player_name)";
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
for (String server : config.getDirectVoteServers()) {
statement.setString(1, uuid.toString());
statement.setString(2, server);
statement.setString(3, playerName);
statement.addBatch();
}
statement.executeBatch();
} catch (SQLException e) {
logger.error("Erreur lors de l'insertion du vote en attente (per-server) pour {}", playerName, e);
}
}
@Override
public void close() {
if (dataSource != null) {
@@ -26,6 +26,7 @@ public class VoteConfig {
private String password;
private int poolSize;
private Set<String> directVoteServers;
private String pendingVotesScope;
public VoteConfig(Path dataDirectory, Logger logger) {
this.dataDirectory = dataDirectory;
@@ -65,6 +66,13 @@ public class VoteConfig {
.filter(s -> !s.isEmpty())
.map(s -> s.toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
this.pendingVotesScope = properties.getProperty("pending-votes.scope", "global");
if (isPerServerScope() && directVoteServers.isEmpty()) {
logger.warn("pending-votes.scope=per-server mais direct-vote.servers est vide : bascule sur 'global'. "
+ "Renseignez direct-vote.servers pour utiliser le mode per-server.");
this.pendingVotesScope = "global";
}
} catch (IOException e) {
logger.error("Impossible de charger la configuration Velocity", e);
}
@@ -119,6 +127,19 @@ public class VoteConfig {
return directVoteServers.isEmpty() || directVoteServers.contains(serverName.toLowerCase(Locale.ROOT));
}
/**
* true si chaque serveur doit avoir son propre compteur de votes en attente independant
* (un /claim sur un serveur ne remet a 0 que ce serveur-la). Sinon (par defaut), un seul
* compteur est partage entre tous les serveurs : /claim le remet a 0 partout a la fois.
*/
public boolean isPerServerScope() {
return "per-server".equalsIgnoreCase(pendingVotesScope);
}
public Set<String> getDirectVoteServers() {
return directVoteServers;
}
public String getJdbcUrl() {
return "jdbc:mysql://" + host + ":" + port + "/" + database
+ "?useSSL=false&autoReconnect=true&characterEncoding=utf8";
@@ -16,7 +16,7 @@ import java.nio.file.Path;
name = "VoteNetwork",
version = "1.0.0",
description = "Systeme de vote reseau Velocity <-> Spigot",
authors = {"VoteNetwork"}
authors = {"Sar_Tron"}
)
public class VoteNetworkVelocity {
@@ -20,3 +20,17 @@ mysql.pool-size=5
# Laisser vide = tous les serveurs sont consideres comme ayant le plugin (deconseille des
# qu'un lobby/hub sans VoteNetwork-Spigot existe sur le reseau).
direct-vote.servers=gen1,gen2
# Portee des votes en attente (joueur hors ligne au moment du vote), uniquement en storage.type=mysql :
# global (par defaut) : un seul compteur partage entre tous les serveurs. Un /claim sur
# N'IMPORTE QUEL serveur remet le compteur a 0 pour TOUS les serveurs a la fois.
# per-server : chaque serveur de direct-vote.servers a son propre compteur independant. Un
# vote hors ligne incremente le compteur de CHAQUE serveur de la liste. Un /claim
# sur le serveur 1 ne remet a 0 QUE le compteur du serveur 1 : le joueur garde ses
# votes en attente sur le serveur 2 et peut les reclamer separement la-bas.
# Exemple concret en per-server : le joueur vote 2 fois hors ligne -> 2 votes en attente sur
# CHAQUE serveur. Il fait /claim sur le serveur 1 -> recoit 2 recompenses, serveur 1 repasse a 0,
# serveur 2 reste a 2. S'il revote une fois -> serveur 1 = 1, serveur 2 = 3.
# Necessite direct-vote.servers rempli ET que chaque serveur Spigot declare son server-name
# (config.yml) correspondant exactement a un nom de cette liste.
pending-votes.scope=global