Skip to content

Commit 5a53010

Browse files
committed
Migrate config system to YAML via Jackson
Config is now read via directly instantiating it. There is now also the possibility to specify the SQL type for the underlying database implementation. Currently supported is PostgreSQL, MariaDB, MySQL and SQLite. Version catalogue was moved into its own file.
1 parent 41f1f19 commit 5a53010

File tree

10 files changed

+286
-77
lines changed

10 files changed

+286
-77
lines changed

api/src/main/java/de/jvstvshd/velocitypunishment/api/message/MessageProvider.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,13 @@ public interface MessageProvider {
5050

5151
@NotNull
5252
Component provide(String key, CommandSource source, boolean withPrefix, Component... args);
53+
54+
@NotNull
55+
default Component prefixed(CommandSource source, Component... args) {
56+
var comp = prefix(source);
57+
for (Component arg : args) {
58+
comp = comp.append(arg);
59+
}
60+
return comp;
61+
}
5362
}

gradle/libs.versions.toml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Generate from settings.gradle.kts
2+
3+
[versions]
4+
velocity-api = "3.3.0-SNAPSHOT"
5+
luckperms-api = "5.4"
6+
jackson-databind = "2.13.4"
7+
jackson-datatype-jsr310 = "2.13.4"
8+
jackson-yaml = "2.13.4"
9+
postgresql = "42.5.0"
10+
hikari = "5.0.1"
11+
sadu = "1.4.1"
12+
junit = "5.9.1"
13+
paper-api = "1.19.2-R0.1-SNAPSHOT"
14+
brigadier = "1.0.17"
15+
mariadb = "3.4.0"
16+
sqlite = "3.45.3.0"
17+
mysql = "8.4.0"
18+
19+
[libraries]
20+
21+
velocity-api = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity-api" }
22+
luckperms-api = { group = "net.luckperms", name = "api", version.ref = "luckperms-api" }
23+
jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson-databind" }
24+
jackson-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson-yaml" }
25+
jackson-datatype-jsr310 = { group = "com.fasterxml.jackson.datatype", name = "jackson-datatype-jsr310", version.ref = "jackson-datatype-jsr310" }
26+
postgresql = { group = "org.postgresql", name = "postgresql", version.ref = "postgresql" }
27+
hikari = { group = "com.zaxxer", name = "HikariCP", version.ref = "hikari" }
28+
sadu = { group = "de.chojo.sadu", name = "sadu", version.ref = "sadu" }
29+
junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
30+
junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }
31+
paper-api = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper-api" }
32+
brigadier = { group = "com.mojang", name = "brigadier", version.ref = "brigadier" }
33+
mariadb = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version.ref = "mariadb" }
34+
sqlite = { group = "org.xerial", name = "sqlite-jdbc", version.ref = "sqlite" }
35+
mysql = { group = "com.mysql", name = "mysql-connector-j", version.ref = "mysql" }
36+
37+
[bundles]
38+
39+
database = ["postgresql", "hikari", "sadu", "mariadb", "sqlite", "mysql"]
40+
jackson = ["jackson-databind", "jackson-yaml", "jackson-datatype-jsr310"]

plugin/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
plugins {
22
java
33
id("com.github.johnrengelman.shadow") version "7.1.2"
4-
id("xyz.jpenilla.run-velocity") version "2.1.0"
4+
id("xyz.jpenilla.run-velocity") version "2.3.0"
55
`java-library`
66
}
77

@@ -37,7 +37,7 @@ tasks {
3737
// Configure the Velocity version for our task.
3838
// This is the only required configuration besides applying the plugin.
3939
// Your plugin's jar (or shadowJar if present) will be used automatically.
40-
velocityVersion("3.2.0-SNAPSHOT")
40+
velocityVersion("3.3.0-SNAPSHOT")
4141
}
4242

4343
val copyServerJar = task<Copy>("copyServerJar") {

plugin/src/main/java/de/jvstvshd/velocitypunishment/VelocityPunishmentPlugin.java

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@
3535
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
3636
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
3737
import com.zaxxer.hikari.HikariDataSource;
38+
import de.chojo.sadu.databases.Database;
3839
import de.chojo.sadu.databases.PostgreSql;
40+
import de.chojo.sadu.databases.SqLite;
3941
import de.chojo.sadu.datasource.DataSourceCreator;
42+
import de.chojo.sadu.datasource.stage.ConfigurationStage;
43+
import de.chojo.sadu.jdbc.RemoteJdbcConfig;
4044
import de.chojo.sadu.updater.SqlUpdater;
4145
import de.chojo.sadu.wrapper.QueryBuilderConfig;
4246
import de.jvstvshd.velocitypunishment.api.VelocityPunishment;
@@ -56,7 +60,8 @@
5660

5761
import java.io.IOException;
5862
import java.nio.file.Path;
59-
import java.nio.file.Paths;
63+
import java.sql.Connection;
64+
import java.sql.PreparedStatement;
6065
import java.sql.SQLException;
6166
import java.util.concurrent.ExecutorService;
6267
import java.util.concurrent.Executors;
@@ -68,12 +73,14 @@ public class VelocityPunishmentPlugin implements VelocityPunishment {
6873
private final Logger logger;
6974
private final ConfigurationManager configurationManager;
7075
private final ExecutorService service = Executors.newCachedThreadPool();
76+
private final Path dataDirectory;
7177
public static final ChannelIdentifier MUTE_DATA_CHANNEL_IDENTIFIER = MinecraftChannelIdentifier.from(MuteData.MUTE_DATA_CHANNEL_IDENTIFIER);
7278
private PunishmentManager punishmentManager;
7379
private HikariDataSource dataSource;
7480
private PlayerResolver playerResolver;
7581
private MessageProvider messageProvider;
7682

83+
7784
private static final String MUTES_DISABLED_STRING = """
7885
Since 1.19.1, cancelling chat messages on proxy is not possible anymore. Therefore, we have to listen to the chat event on the actual game server. This means
7986
that there has to be a spigot/paper extension to this plugin which is not yet available unless there's a possibility. Therefore all mute related features won't work at the moment.
@@ -92,14 +99,15 @@ public class VelocityPunishmentPlugin implements VelocityPunishment {
9299
public VelocityPunishmentPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
93100
this.server = server;
94101
this.logger = logger;
95-
this.configurationManager = new ConfigurationManager(Paths.get(dataDirectory.toAbsolutePath().toString(), "config.json"));
102+
this.configurationManager = new ConfigurationManager(dataDirectory.resolve("config.yml"));
96103
this.playerResolver = new DefaultPlayerResolver(server);
97104
this.communicator = new MessagingChannelCommunicator(server, logger);
105+
this.dataDirectory = dataDirectory;
98106
}
99107

100108
@Subscribe
101109
public void onProxyInitialization(ProxyInitializeEvent event) {
102-
Thread.setDefaultUncaughtExceptionHandler((t, e) -> logger.error("An error occurred in thread " + t.getName(), e));
110+
Thread.setDefaultUncaughtExceptionHandler((t, e) -> logger.error("An error occurred in thread {}", t.getName(), e));
103111
try {
104112
configurationManager.load();
105113
if (configurationManager.getConfiguration().isWhitelistActivated()) {
@@ -114,9 +122,9 @@ public void onProxyInitialization(ProxyInitializeEvent event) {
114122
punishmentManager = new DefaultPunishmentManager(server, dataSource, this);
115123
try {
116124
updateDatabase();
117-
//initDataSource();
125+
initDataSource();
118126
} catch (SQLException | IOException e) {
119-
logger.error("Could not create table velocity_punishment in database " + dataSource.getDataSourceProperties().get("dataSource.databaseName"), e);
127+
logger.error("Could not create table velocity_punishment in database {}", dataSource.getDataSourceProperties().get("dataSource.databaseName"), e);
120128
}
121129
setup(server.getCommandManager(), server.getEventManager());
122130
logger.info("Velocity Punishment Plugin v1.0.0 has been loaded");
@@ -139,28 +147,59 @@ private void setup(CommandManager commandManager, EventManager eventManager) {
139147
commandManager.register(WhitelistCommand.whitelistCommand(this));
140148
}
141149

150+
@SuppressWarnings({"unchecked", "UnstableApiUsage"})
142151
private HikariDataSource createDataSource() {
143152
var dbData = configurationManager.getConfiguration().getDataBaseData();
144-
//TODO add config option for sql type
145-
return DataSourceCreator.create(PostgreSql.get())
146-
.configure(jdbcConfig -> jdbcConfig.host(dbData.getHost())
147-
.port(dbData.getPort())
148-
.database(dbData.getDatabase())
149-
.user(dbData.getUsername())
150-
.password(dbData.getPassword())
151-
.applicationName("Velocity Punishment Plugin"))
152-
.create().withMaximumPoolSize(dbData.getMaxPoolSize())
153+
ConfigurationStage stage = switch (dbData.sqlType().name().toLowerCase()) {
154+
case "sqlite" -> DataSourceCreator.create(SqLite.get())
155+
.configure(sqLiteJdbc -> sqLiteJdbc.path(dataDirectory.resolve("punishment.db")))
156+
.create();
157+
case "postgresql" ->
158+
DataSourceCreator.create(PostgreSql.get()).configure(jdbcConfig -> jdbcConfig.host(dbData.getHost())
159+
160+
.port(dbData.getPort())
161+
.database(dbData.getDatabase())
162+
.user(dbData.getUsername())
163+
.password(dbData.getPassword())
164+
)
165+
.create();
166+
167+
default ->
168+
DataSourceCreator.create((Database<RemoteJdbcConfig<?>, ?>) dbData.sqlType()).configure(jdbcConfig -> jdbcConfig.host(dbData.getHost())
169+
.port(dbData.getPort())
170+
.database(dbData.getDatabase())
171+
.user(dbData.getUsername())
172+
.password(dbData.getPassword()))
173+
.create();
174+
};
175+
return stage.withMaximumPoolSize(dbData.getMaxPoolSize())
153176
.withMinimumIdle(dbData.getMinIdle())
154177
.withPoolName("velocity-punishment-hikari")
178+
.forSchema(dbData.getPostgresSchema())
155179
.build();
156180
}
157181

182+
@SuppressWarnings("UnstableApiUsage")
158183
private void updateDatabase() throws IOException, SQLException {
159-
//TODO config option for sql type
160-
SqlUpdater.builder(dataSource, PostgreSql.get())
161-
.setSchemas("punishment")
162-
.execute();
184+
if (configurationManager.getConfiguration().getDataBaseData().sqlType().name().equalsIgnoreCase("postgresql")) {
185+
SqlUpdater.builder(dataSource, PostgreSql.get())
186+
.setSchemas(configurationManager.getConfiguration().getDataBaseData().getPostgresSchema())
187+
.execute();
188+
} else {
189+
logger.warn("Database type is not (yet) supported for automatic updates. Please update the database manually.");
190+
}
191+
}
163192

193+
private void initDataSource() throws SQLException {
194+
try (Connection connection = dataSource.getConnection(); PreparedStatement statement =
195+
connection.prepareStatement("CREATE TABLE IF NOT EXISTS velocity_punishment (uuid VARCHAR (36), name VARCHAR (16), type VARCHAR (1000), expiration DATETIME (6), " +
196+
"reason VARCHAR (1000), punishment_id VARCHAR (36))")) {
197+
statement.execute();
198+
}
199+
try (Connection connection = dataSource.getConnection(); PreparedStatement statement =
200+
connection.prepareStatement("CREATE TABLE IF NOT EXISTS velocity_punishment_whitelist (uuid VARCHAR (36))")) {
201+
statement.execute();
202+
}
164203
}
165204

166205
@Override
@@ -219,4 +258,8 @@ public Logger getLogger() {
219258
public boolean whitelistActive() {
220259
return configurationManager.getConfiguration().isWhitelistActivated();
221260
}
261+
262+
public ConfigurationManager getConfig() {
263+
return configurationManager;
264+
}
222265
}

plugin/src/main/java/de/jvstvshd/velocitypunishment/commands/WhitelistCommand.java

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,56 +38,83 @@
3838
import net.kyori.adventure.text.Component;
3939
import net.kyori.adventure.text.format.NamedTextColor;
4040

41+
import java.io.IOException;
4142
import java.util.List;
43+
import java.util.Locale;
4244

4345
public class WhitelistCommand {
4446

45-
public static final List<String> options = ImmutableList.of("add", "remove");
47+
public static final List<String> options = ImmutableList.of("add", "remove", "on", "off");
4648

4749

4850
public static BrigadierCommand whitelistCommand(VelocityPunishmentPlugin plugin) {
4951
var node = Util.permissibleCommand("whitelist", "velocitypunishment.command.whitelist")
50-
.then(Util.playerArgument(plugin.getServer()).executes(context -> execute(context, plugin))
51-
.then(RequiredArgumentBuilder.<CommandSource, String>argument("option", StringArgumentType.word()).executes(context -> execute(context, plugin))
52-
.suggests((context, builder) -> {
53-
for (String option : options) {
54-
builder.suggest(option);
55-
}
56-
return builder.buildFuture();
57-
})));
52+
.then(RequiredArgumentBuilder.<CommandSource, String>argument("option", StringArgumentType.word())
53+
.executes(context -> execute(context, plugin))
54+
.suggests((context, builder) -> {
55+
for (String option : options) {
56+
if (context.getArguments().containsKey("option") && !context.getArgument("option", String.class).isEmpty() &&
57+
!option.startsWith(context.getArgument("option", String.class).toLowerCase(Locale.ROOT)))
58+
continue;
59+
builder.suggest(option);
60+
}
61+
return builder.buildFuture();
62+
})
63+
.then(Util.playerArgument(plugin.getServer()).executes(context -> execute(context, plugin))));
5864
return new BrigadierCommand(node);
5965
}
6066

6167
private static int execute(CommandContext<CommandSource> context, VelocityPunishmentPlugin plugin) {
6268
var source = context.getSource();
63-
if (!plugin.whitelistActive()) {
64-
source.sendMessage(Component.text("Whitelist is not active. You may activate it by setting 'whitelistActivated' in 'plugins/velocity-punishment/config.json' to true.", NamedTextColor.RED));
65-
return Command.SINGLE_SUCCESS;
69+
String player;
70+
if (context.getArguments().containsKey("player")) {
71+
player = context.getArgument("player", String.class);
72+
} else {
73+
player = null;
6674
}
67-
var player = context.getArgument("player", String.class);
6875
if (context.getArguments().containsKey("option")) {
6976
var option = context.getArgument("option", String.class).toLowerCase();
7077
switch (option) {
71-
case "add", "remove" ->
72-
plugin.getPlayerResolver().getOrQueryPlayerUuid(player, plugin.getService()).whenCompleteAsync((uuid, throwable) -> {
73-
if (Util.sendErrorMessageIfErrorOccurred(context, uuid, throwable, plugin)) return;
74-
var builder = QueryBuilder.builder(plugin.getDataSource())
75-
.configure(QueryBuilderConfig.defaultConfig())
76-
.query(option.equalsIgnoreCase("remove") ? "DELETE FROM velocity_punishment_whitelist WHERE uuid = ?;" :
77-
"INSERT INTO velocity_punishment_whitelist (uuid) VALUES (?);")
78-
.parameter(paramBuilder -> paramBuilder.setUuidAsString(uuid));
79-
if (option.equalsIgnoreCase("remove")) {
80-
builder.delete().send();
81-
plugin.getServer().getPlayer(uuid).ifPresent(pl -> pl.disconnect(Component.text("You have been blacklisted.").color(NamedTextColor.DARK_RED)));
82-
} else {
83-
builder.insert().send();
84-
}
85-
}, plugin.getService());
78+
case "add", "remove" -> {
79+
if (player == null) {
80+
source.sendMessage(plugin.getMessageProvider().provide("command.whitelist.usage", source, true));
81+
return Command.SINGLE_SUCCESS;
82+
}
83+
plugin.getPlayerResolver().getOrQueryPlayerUuid(player, plugin.getService()).whenCompleteAsync((uuid, throwable) -> {
84+
if (Util.sendErrorMessageIfErrorOccurred(context, uuid, throwable, plugin)) return;
85+
var builder = QueryBuilder.builder(plugin.getDataSource())
86+
.configure(QueryBuilderConfig.defaultConfig())
87+
.query(option.equalsIgnoreCase("remove") ? "DELETE FROM velocity_punishment_whitelist WHERE uuid = ?;" :
88+
"INSERT INTO velocity_punishment_whitelist (uuid) VALUES (?);")
89+
.parameter(paramBuilder -> paramBuilder.setUuidAsString(uuid));
90+
if (option.equalsIgnoreCase("remove")) {
91+
builder.delete().send();
92+
plugin.getServer().getPlayer(uuid).ifPresent(pl -> pl.disconnect(Component.text("You have been blacklisted.").color(NamedTextColor.DARK_RED)));
93+
} else {
94+
builder.insert().send();
95+
}
96+
}, plugin.getService());
97+
}
98+
case "on", "off" -> {
99+
var config = plugin.getConfig();
100+
config.getConfiguration().setWhitelistActivated(option.equals("on"));
101+
try {
102+
config.save();
103+
source.sendMessage(plugin.getMessageProvider().prefixed(source, Component.text("The whitelist is now " + option).color(NamedTextColor.GRAY)));
104+
} catch (IOException e) {
105+
source.sendMessage(plugin.getMessageProvider().internalError(source, true));
106+
plugin.getLogger().error("Could not save the configuration.", e);
107+
}
108+
}
86109
default ->
87110
source.sendMessage(plugin.getMessageProvider().provide("command.whitelist.usage", source, true));
88111
}
89112
return Command.SINGLE_SUCCESS;
90113
}
114+
if (player == null) {
115+
source.sendMessage(plugin.getMessageProvider().provide("command.whitelist.usage", source, true));
116+
return Command.SINGLE_SUCCESS;
117+
}
91118
plugin.getPlayerResolver().getOrQueryPlayerUuid(player, plugin.getService()).whenCompleteAsync((uuid, throwable) -> {
92119
if (Util.sendErrorMessageIfErrorOccurred(context, uuid, throwable, plugin)) return;
93120
QueryBuilder.builder(plugin.getDataSource(), Boolean.class)

plugin/src/main/java/de/jvstvshd/velocitypunishment/config/ConfigData.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,36 @@
2424

2525
package de.jvstvshd.velocitypunishment.config;
2626

27+
import com.fasterxml.jackson.annotation.JsonAlias;
28+
import com.fasterxml.jackson.annotation.JsonProperty;
29+
2730
import java.util.Locale;
2831

29-
@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
3032
public class ConfigData {
3133

32-
private DataBaseData dataBaseData = new DataBaseData();
33-
private Locale forcedLanguage = null;
34+
@JsonProperty("database")
35+
@JsonAlias("dataBaseData")
36+
private final DataBaseData dataBaseData;
37+
38+
@JsonProperty("forced-language")
39+
@JsonAlias("forcedLanguage")
40+
private final Locale forcedLanguage;
41+
42+
@JsonProperty("whitelist-activated")
43+
@JsonAlias("whitelistActivated")
44+
private boolean whitelistActivated;
45+
46+
public ConfigData(DataBaseData dataBaseData, Locale forcedLanguage, boolean whitelistActivated) {
47+
this.dataBaseData = dataBaseData;
48+
this.forcedLanguage = forcedLanguage;
49+
this.whitelistActivated = whitelistActivated;
50+
}
3451

35-
private boolean whitelistActivated = false;
52+
public ConfigData() {
53+
this(new DataBaseData(), Locale.ENGLISH, false);
54+
}
3655

37-
public DataBaseData getDataBaseData() {
56+
public final DataBaseData getDataBaseData() {
3857
return dataBaseData;
3958
}
4059

@@ -45,4 +64,8 @@ public Locale getForcedLanguage() {
4564
public boolean isWhitelistActivated() {
4665
return whitelistActivated;
4766
}
67+
68+
public void setWhitelistActivated(boolean whitelistActivated) {
69+
this.whitelistActivated = whitelistActivated;
70+
}
4871
}

0 commit comments

Comments
 (0)