3 Commits

Author SHA1 Message Date
6510c4bf8c Updated Source Files for Version 0.3-Pre 2025-06-07 11:29:10 -07:00
5e5ac05b72 Update README.md 2025-06-05 19:31:25 -07:00
cd4cb7482d Update README.md 2025-06-05 19:30:42 -07:00
16 changed files with 523 additions and 132 deletions

View File

@ -27,4 +27,5 @@ VelocityBroadcast Reborn is a unique and lightweight plugin that allows people w
- /vb {message} - When typed and paired with a message, it will send a message across all servers on a network (Legacy Color Codes and MiniMessage compatible) - vb.broadcast - /vb {message} - When typed and paired with a message, it will send a message across all servers on a network (Legacy Color Codes and MiniMessage compatible) - vb.broadcast
- /vp prefix {new prefix} - This allows whoever has the required permission to change the prefix in front of the message broadcasted - vb.admin - /vp prefix {new prefix} - This allows whoever has the required permission to change the prefix in front of the message broadcasted - vb.admin
- /vb reload - Allows people with the correct permission node to reload the config if it was edited manually - vb.admin - /vb reload - Allows people with the correct permission node to reload the config if it was edited manually - vb.admin
***Currently due to the limitations of Velocity, you can not run the commands while being opped. You MUST have the correct permission nodes for it to work.*** ***Currently due to the limitations of Velocity, you can not run the commands while being opped. You MUST have the correct permission nodes for it to work.***

View File

@ -4,7 +4,7 @@
<groupId>com.adzel.velocitybroadcast</groupId> <groupId>com.adzel.velocitybroadcast</groupId>
<artifactId>velocitybroadcast</artifactId> <artifactId>velocitybroadcast</artifactId>
<name>VelocityBroadcast</name> <name>VelocityBroadcast</name>
<version>0.2-pre</version> <version>0.3-pre</version>
<description>Broadcast plugin for Minecraft Velocity proxy.</description> <description>Broadcast plugin for Minecraft Velocity proxy.</description>
<build> <build>
<plugins> <plugins>
@ -25,7 +25,7 @@
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration> <configuration>
<minimizeJar>true</minimizeJar> <minimizeJar>false</minimizeJar>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -37,6 +37,10 @@
<id>velocitypowered-repo</id> <id>velocitypowered-repo</id>
<url>https://repo.velocitypowered.com/releases/</url> <url>https://repo.velocitypowered.com/releases/</url>
</repository> </repository>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>

39
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>com.adzel.velocitybroadcast</groupId> <groupId>com.adzel.velocitybroadcast</groupId>
<artifactId>velocitybroadcast</artifactId> <artifactId>velocitybroadcast</artifactId>
<version>0.2-pre</version> <version>0.3-pre</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>VelocityBroadcast</name> <name>VelocityBroadcast</name>
<description>Broadcast plugin for Minecraft Velocity proxy.</description> <description>Broadcast plugin for Minecraft Velocity proxy.</description>
@ -21,6 +21,10 @@
<id>velocitypowered-repo</id> <id>velocitypowered-repo</id>
<url>https://repo.velocitypowered.com/releases/</url> <url>https://repo.velocitypowered.com/releases/</url>
</repository> </repository>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -45,6 +49,34 @@
<artifactId>adventure-text-minimessage</artifactId> <artifactId>adventure-text-minimessage</artifactId>
<version>4.15.0</version> <version>4.15.0</version>
</dependency> </dependency>
<!-- JSON Wrapper -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
<!-- SQLite -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.43.2.2</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version>
</dependency>
<!-- Snake YAML -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.2</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -71,8 +103,7 @@
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration> <configuration>
<minimizeJar>true</minimizeJar> <minimizeJar>false</minimizeJar>
<!-- No relocations anymore -->
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -80,3 +111,5 @@
</plugins> </plugins>
</build> </build>
</project> </project>
<!-- This is a Maven POM file for the VelocityBroadcast plugin.
It defines the project structure, dependencies, and build configuration. -->

View File

@ -2,8 +2,10 @@ package com.adzel.velocitybroadcast;
import java.util.List; import java.util.List;
import com.adzel.velocitybroadcast.util.DatabaseManager;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
@ -11,9 +13,11 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
public class BroadcastCommand implements SimpleCommand { public class BroadcastCommand implements SimpleCommand {
private final VelocityBroadcast plugin; private final VelocityBroadcast plugin;
private final DatabaseManager databaseManager;
public BroadcastCommand(VelocityBroadcast plugin) { public BroadcastCommand(VelocityBroadcast plugin) {
this.plugin = plugin; this.plugin = plugin;
this.databaseManager = plugin.getDatabaseManager();
} }
@Override @Override
@ -42,5 +46,13 @@ public class BroadcastCommand implements SimpleCommand {
if (plugin.getConfigHandler().isDebugEnabled()) { if (plugin.getConfigHandler().isDebugEnabled()) {
plugin.getLogger().info("[Broadcast] Sent: " + fullMessage); plugin.getLogger().info("[Broadcast] Sent: " + fullMessage);
} }
// ✅ Log to the database
String sender = (source instanceof Player)
? ((Player) source).getUsername()
: "Console";
String sourceServer = plugin.getServer().getBoundAddress().toString(); // optional
databaseManager.logBroadcast(sender, messageRaw, sourceServer);
} }
} }

View File

@ -1,16 +1,16 @@
package com.adzel.velocitybroadcast; package com.adzel.velocitybroadcast;
import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
public class ConfigHandler { public class ConfigHandler {
private final Path configPath; private final Path configPath;
@ -20,14 +20,34 @@ public class ConfigHandler {
private boolean versionCheckEnabled = true; private boolean versionCheckEnabled = true;
private String prefix = "&9&l[&3&lServer&9&l]&r "; private String prefix = "&9&l[&3&lServer&9&l]&r ";
private String dbType = "sqlite";
private String dbHost = "localhost";
private int dbPort = 3306;
private String dbName = "velocitybroadcast";
private String dbUser = "vb_user";
private String dbPassword = "securepassword";
private static final String CURRENT_VERSION = VelocityBroadcast.PLUGIN_VERSION; private static final String CURRENT_VERSION = VelocityBroadcast.PLUGIN_VERSION;
private static final String VERSION_LINE = "# DO NOT EDIT\nPlugin Version: '" + CURRENT_VERSION + "' # Do not edit this value, as it will mess up version checking and break the plugin"; private static final String VERSION_LINE = "# DO NOT EDIT\nPlugin Version: '" + CURRENT_VERSION + "' # Do not edit this value, as it will mess up version checking and break the plugin";
private final Yaml yaml;
public ConfigHandler(Path configPath, Logger logger) { public ConfigHandler(Path configPath, Logger logger) {
this.configPath = configPath; this.configPath = configPath;
this.logger = logger; this.logger = logger;
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setIndent(2);
options.setPrettyFlow(true);
Representer representer = new Representer(options);
representer.getPropertyUtils().setSkipMissingProperties(true);
yaml = new Yaml(representer, options);
} }
@SuppressWarnings("unchecked")
public void load() { public void load() {
try { try {
Files.createDirectories(configPath.getParent()); Files.createDirectories(configPath.getParent());
@ -39,38 +59,34 @@ public class ConfigHandler {
return; return;
} }
List<String> lines = Files.readAllLines(configPath); String fileVersion = Files.readAllLines(configPath).stream()
String fileVersion = null; .filter(line -> line.startsWith("Plugin Version:"))
.map(line -> line.replaceAll(".*'(.*?)'.*", "$1").trim())
for (String line : lines) { .findFirst()
if (line.trim().startsWith("Plugin Version:")) { .orElse(null);
fileVersion = line.replaceAll(".*'(.*?)'.*", "$1").trim();
break;
}
}
if (fileVersion == null || !fileVersion.equals(CURRENT_VERSION)) { if (fileVersion == null || !fileVersion.equals(CURRENT_VERSION)) {
shouldSave = true; shouldSave = true;
} }
try (BufferedReader reader = Files.newBufferedReader(configPath)) { Map<String, Object> root = yaml.load(Files.newBufferedReader(configPath));
Map<String, String> configMap = reader.lines() if (root == null) root = new LinkedHashMap<>();
.filter(line -> line.contains(":") && !line.trim().startsWith("#"))
.map(line -> line.replaceAll("#.*", "").split(":", 2))
.collect(Collectors.toMap(
a -> a[0].trim(),
a -> a[1].trim().replaceAll("^['\"]|['\"]$", ""),
(a, b) -> b,
LinkedHashMap::new
));
debugEnabled = Boolean.parseBoolean(configMap.getOrDefault("debug-messages-enabled", "false")); Map<String, Object> general = (Map<String, Object>) root.getOrDefault("general", new LinkedHashMap<>());
versionCheckEnabled = Boolean.parseBoolean(configMap.getOrDefault("version-check-enabled", "true")); debugEnabled = Boolean.parseBoolean(String.valueOf(general.getOrDefault("debug-messages-enabled", "false")));
prefix = configMap.getOrDefault("prefix", "&9&l[&3&lServer&9&l]&r "); versionCheckEnabled = Boolean.parseBoolean(String.valueOf(general.getOrDefault("version-check-enabled", "true")));
} prefix = String.valueOf(general.getOrDefault("prefix", "&9&l[&3&lServer&9&l]&r "));
Map<String, Object> database = (Map<String, Object>) root.getOrDefault("database", new LinkedHashMap<>());
dbType = String.valueOf(database.getOrDefault("type", "sqlite"));
dbHost = String.valueOf(database.getOrDefault("host", "localhost"));
dbPort = Integer.parseInt(String.valueOf(database.getOrDefault("port", "3306")));
dbName = String.valueOf(database.getOrDefault("name", "velocitybroadcast"));
dbUser = String.valueOf(database.getOrDefault("user", "vb_user"));
dbPassword = String.valueOf(database.getOrDefault("password", "securepassword"));
if (shouldSave) { if (shouldSave) {
save(); // Update config with new version and preserve user values save();
} }
} catch (IOException e) { } catch (IOException e) {
@ -82,26 +98,28 @@ public class ConfigHandler {
try { try {
Files.createDirectories(configPath.getParent()); Files.createDirectories(configPath.getParent());
String editableSection = ""; Map<String, Object> general = new LinkedHashMap<>();
if (Files.exists(configPath)) { general.put("debug-messages-enabled", debugEnabled);
editableSection = Files.readAllLines(configPath).stream() general.put("version-check-enabled", versionCheckEnabled);
.dropWhile(line -> !line.trim().equalsIgnoreCase("# ONLY EDIT BELOW THIS LINE")) general.put("prefix", prefix);
.skip(1)
.collect(Collectors.joining("\n")); Map<String, Object> database = new LinkedHashMap<>();
} database.put("type", dbType);
database.put("host", dbHost);
database.put("port", dbPort);
database.put("name", dbName);
database.put("user", dbUser);
database.put("password", dbPassword);
Map<String, Object> root = new LinkedHashMap<>();
root.put("general", general);
root.put("database", database);
try (BufferedWriter writer = Files.newBufferedWriter(configPath)) { try (BufferedWriter writer = Files.newBufferedWriter(configPath)) {
writer.write(VERSION_LINE + "\n\n"); writer.write(VERSION_LINE + "\n\n");
writer.write("# ONLY EDIT BELOW THIS LINE\n"); yaml.dump(root, writer);
}
if (!editableSection.isEmpty()) {
writer.write(editableSection + "\n");
} else {
writer.write("debug-messages-enabled: false # Enables/disables debug messages (Default: false)\n");
writer.write("version-check-enabled: true # Toggles version update messages for admins (Default: true)\n");
writer.write("prefix: '&9&l[&3&lServer&9&l]&r ' # The prefix for broadcasts and messages\n");
}
}
} catch (IOException e) { } catch (IOException e) {
logger.error("Failed to save VelocityBroadcast config!", e); logger.error("Failed to save VelocityBroadcast config!", e);
} }
@ -127,4 +145,28 @@ public class ConfigHandler {
this.prefix = newPrefix; this.prefix = newPrefix;
save(); save();
} }
public String getDbType() {
return dbType;
}
public String getDbHost() {
return dbHost;
}
public int getDbPort() {
return dbPort;
}
public String getDbName() {
return dbName;
}
public String getDbUser() {
return dbUser;
}
public String getDbPassword() {
return dbPassword;
}
} }

View File

@ -0,0 +1,50 @@
package com.adzel.velocitybroadcast;
import com.adzel.velocitybroadcast.util.DatabaseManager;
import com.adzel.velocitybroadcast.util.UpdateChecker;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
public class LoginListener {
private final ProxyServer server;
private final String currentVersion;
private final UpdateChecker updateChecker;
private final DatabaseManager databaseManager;
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
public LoginListener(ProxyServer server, String currentVersion, UpdateChecker updateChecker, DatabaseManager databaseManager) {
this.server = server;
this.currentVersion = currentVersion;
this.updateChecker = updateChecker;
this.databaseManager = databaseManager;
}
@Subscribe
public void onPlayerJoin(PostLoginEvent event) {
Player player = event.getPlayer();
if (player.hasPermission("vb.admin")) {
// ✅ Log admin join to DB
String serverName = server.getBoundAddress().toString(); // Optional
databaseManager.logAdminJoin(player.getUsername(), serverName);
// ✅ Notify if update is available
updateChecker.checkForUpdate().thenAccept(latest -> {
if (latest != null && !latest.equalsIgnoreCase(currentVersion)) {
Component message = MINI_MESSAGE.deserialize(
"<bold><gold>[VelocityBroadcast]</gold></bold> " +
"<gradient:yellow:red>A new version is available: </gradient:yellow:red>" +
"<bold><green>" + latest + "</green></bold> " +
"<gray>(You are on <red>" + currentVersion + "</red>)</gray>"
);
player.sendMessage(message);
}
});
}
}
}

View File

@ -0,0 +1,63 @@
package com.adzel.velocitybroadcast.util;
import com.velocitypowered.api.proxy.ProxyServer;
import net.kyori.adventure.text.Component;
import com.velocitypowered.api.proxy.Player;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
import org.json.JSONObject;
import org.json.JSONTokener;
public class UpdateChecker {
private static final String ENDPOINT = "https://api.github.com/repos/AdzelFirestar/velocitybroadcast-reborn/releases/latest";
private final String currentVersion;
private final Logger logger;
public UpdateChecker(String currentVersion, Logger logger) {
this.currentVersion = currentVersion;
this.logger = logger;
}
public CompletableFuture<String> checkForUpdate() {
return CompletableFuture.supplyAsync(() -> {
try {
HttpURLConnection connection = (HttpURLConnection) new URL(ENDPOINT).openConnection();
connection.setRequestProperty("Accept", "application/json");
connection.setConnectTimeout(3000);
connection.setReadTimeout(3000);
try (InputStreamReader reader = new InputStreamReader(connection.getInputStream())) {
JSONObject json = new JSONObject(new JSONTokener(reader));
String latestVersion = json.getString("tag_name").trim();
if (!latestVersion.equalsIgnoreCase(currentVersion)) {
logger.info("[VelocityBroadcast] Update available: " + latestVersion + " (you are on " + currentVersion + ")");
return latestVersion;
} else {
logger.info("[VelocityBroadcast] You are running the latest version (" + currentVersion + ").");
return null;
}
}
} catch (Exception e) {
logger.warning("[VelocityBroadcast] Failed to check for updates: " + e.getMessage());
return null;
}
});
}
public void notifyPlayerIfOutdated(ProxyServer server, String latestVersion) {
if (latestVersion != null && !latestVersion.equalsIgnoreCase(currentVersion)) {
String message = String.format("[VelocityBroadcast] A new version (%s) is available! You are running %s.", latestVersion, currentVersion);
Component component = Component.text(message);
for (Player player : server.getAllPlayers()) {
if (player.hasPermission("vb.admin")) {
player.sendMessage(component);
}
}
}
}
}

View File

@ -2,8 +2,10 @@ package com.adzel.velocitybroadcast;
import java.util.List; import java.util.List;
import com.adzel.velocitybroadcast.util.DatabaseManager;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
@ -11,10 +13,12 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
public class VBCommand implements SimpleCommand { public class VBCommand implements SimpleCommand {
private final VelocityBroadcast plugin; private final VelocityBroadcast plugin;
private final DatabaseManager databaseManager;
private final MiniMessage mm = MiniMessage.miniMessage(); private final MiniMessage mm = MiniMessage.miniMessage();
public VBCommand(VelocityBroadcast plugin) { public VBCommand(VelocityBroadcast plugin) {
this.plugin = plugin; this.plugin = plugin;
this.databaseManager = plugin.getDatabaseManager();
} }
@Override @Override
@ -22,6 +26,12 @@ public class VBCommand implements SimpleCommand {
CommandSource source = invocation.source(); CommandSource source = invocation.source();
List<String> args = List.of(invocation.arguments()); List<String> args = List.of(invocation.arguments());
String fullCommand = "/vb" + (args.isEmpty() ? "" : " " + String.join(" ", args));
String username = (source instanceof Player) ? ((Player) source).getUsername() : "Console";
boolean success = true;
try {
if (args.isEmpty() || args.get(0).equalsIgnoreCase("help")) { if (args.isEmpty() || args.get(0).equalsIgnoreCase("help")) {
source.sendMessage(parseFormatted("<gold><bold>VelocityBroadcast Commands:</bold></gold>")); source.sendMessage(parseFormatted("<gold><bold>VelocityBroadcast Commands:</bold></gold>"));
source.sendMessage(parseFormatted("<yellow>/vb <message></yellow> <gray>- Broadcast a message to all players</gray>")); source.sendMessage(parseFormatted("<yellow>/vb <message></yellow> <gray>- Broadcast a message to all players</gray>"));
@ -30,6 +40,8 @@ public class VBCommand implements SimpleCommand {
source.sendMessage(parseFormatted("<yellow>/vb prefix <newPrefix></yellow> <gray>- Change the broadcast prefix</gray>")); source.sendMessage(parseFormatted("<yellow>/vb prefix <newPrefix></yellow> <gray>- Change the broadcast prefix</gray>"));
source.sendMessage(parseFormatted("<yellow>/vb reload</yellow> <gray>- Reload the plugin config</gray>")); source.sendMessage(parseFormatted("<yellow>/vb reload</yellow> <gray>- Reload the plugin config</gray>"));
} }
success = false;
return; return;
} }
@ -37,53 +49,68 @@ public class VBCommand implements SimpleCommand {
List<String> subArgs = args.subList(1, args.size()); List<String> subArgs = args.subList(1, args.size());
switch (sub) { switch (sub) {
case "prefix": case "prefix" -> {
if (!source.hasPermission("vb.admin")) { if (!source.hasPermission("vb.admin")) {
source.sendMessage(parseFormatted("<red>You don't have permission to change the broadcast prefix.</red>")); source.sendMessage(parseFormatted("<red>You don't have permission to change the broadcast prefix.</red>"));
success = false;
return; return;
} }
if (subArgs.isEmpty()) { if (subArgs.isEmpty()) {
source.sendMessage(parseFormatted("<red>Usage: /vb prefix <newPrefix></red>")); source.sendMessage(parseFormatted("<red>Usage: /vb prefix <newPrefix></red>"));
success = false;
return; return;
} }
String newPrefix = String.join(" ", subArgs); String newPrefix = String.join(" ", subArgs);
plugin.getConfigHandler().setPrefix(newPrefix); plugin.getConfigHandler().setPrefix(newPrefix);
source.sendMessage(parseFormatted("<green>Prefix updated to:</green> <gray>" + newPrefix + "</gray>")); source.sendMessage(parseFormatted("<green>Prefix updated to:</green> <gray>" + newPrefix + "</gray>"));
if (plugin.getConfigHandler().isDebugEnabled()) { if (plugin.getConfigHandler().isDebugEnabled()) {
plugin.getLogger().info("[Prefix] Updated prefix to: " + newPrefix); plugin.getLogger().info("[Prefix] Updated prefix to: " + newPrefix);
} }
break; }
case "reload": case "reload" -> {
if (!source.hasPermission("vb.admin")) { if (!source.hasPermission("vb.admin")) {
source.sendMessage(parseFormatted("<red>You don't have permission to reload the config.</red>")); source.sendMessage(parseFormatted("<red>You don't have permission to reload the config.</red>"));
success = false;
return; return;
} }
plugin.getConfigHandler().reload(); plugin.getConfigHandler().reload();
source.sendMessage(parseFormatted("<green>VelocityBroadcast config reloaded.</green>")); source.sendMessage(parseFormatted("<green>VelocityBroadcast config reloaded.</green>"));
if (plugin.getConfigHandler().isDebugEnabled()) { if (plugin.getConfigHandler().isDebugEnabled()) {
plugin.getLogger().info("[Reload] Config reloaded by " + source.toString()); plugin.getLogger().info("[Reload] Config reloaded by " + source.toString());
} }
break; }
default: default -> {
// Treat as broadcast message // Treat as broadcast message
if (!source.hasPermission("vb.broadcast")) { if (!source.hasPermission("vb.broadcast")) {
source.sendMessage(parseFormatted("<red>You don't have permission to broadcast.</red>")); source.sendMessage(parseFormatted("<red>You don't have permission to broadcast.</red>"));
success = false;
return; return;
} }
String fullMessage = plugin.getConfigHandler().getPrefix() + String.join(" ", args); String fullMessage = plugin.getConfigHandler().getPrefix() + String.join(" ", args);
Component broadcast = parseFormatted(fullMessage); Component broadcast = parseFormatted(fullMessage);
plugin.getServer().getAllPlayers().forEach(p -> p.sendMessage(broadcast)); plugin.getServer().getAllPlayers().forEach(p -> p.sendMessage(broadcast));
if (plugin.getConfigHandler().isDebugEnabled()) { if (plugin.getConfigHandler().isDebugEnabled()) {
plugin.getLogger().info("[Broadcast] Sent: " + fullMessage); plugin.getLogger().info("[Broadcast] Sent: " + fullMessage);
} }
break; }
}
} catch (Exception e) {
plugin.getLogger().error("Error executing /vb command", e);
source.sendMessage(parseFormatted("<red>An error occurred while executing the command.</red>"));
success = false;
} finally {
// ✅ Log the command usage
databaseManager.logCommand(username, fullCommand, success);
} }
} }

View File

@ -4,6 +4,8 @@ import java.nio.file.Path;
import org.slf4j.Logger; import org.slf4j.Logger;
import com.adzel.velocitybroadcast.util.DatabaseManager;
import com.adzel.velocitybroadcast.util.UpdateChecker;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.Subscribe;
@ -18,19 +20,21 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
@Plugin( @Plugin(
id = "velocitybroadcast", id = "velocitybroadcast",
name = "VelocityBroadcast", name = "VelocityBroadcast",
version = "0.2-pre", version = "0.3-pre",
description = "A proxy-wide broadcast plugin for Velocity.", description = "A proxy-wide broadcast plugin for Velocity.",
authors = {"Adzel"} authors = {"Adzel"}
) )
public class VelocityBroadcast { public class VelocityBroadcast {
public static final String PLUGIN_VERSION = "0.2-pre"; public static final String PLUGIN_VERSION = "0.3-pre";
public static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); public static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
private final ProxyServer server; private final ProxyServer server;
private final Logger logger; private final Logger logger;
private final Path dataDirectory; private final Path dataDirectory;
private ConfigHandler config; private ConfigHandler config;
private DatabaseManager databaseManager;
@Inject @Inject
public VelocityBroadcast(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) { public VelocityBroadcast(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
@ -49,7 +53,25 @@ public class VelocityBroadcast {
logger.info("[VelocityBroadcast] Debug mode is enabled."); logger.info("[VelocityBroadcast] Debug mode is enabled.");
} }
// Register the root /vb command handler // Initialize database manager
java.util.logging.Logger jdkLogger = java.util.logging.Logger.getLogger("VelocityBroadcast");
this.databaseManager = new DatabaseManager(config, jdkLogger, dataDirectory);
databaseManager.initialize();
// Check for updates (async)
UpdateChecker updateChecker = new UpdateChecker(PLUGIN_VERSION, jdkLogger);
updateChecker.checkForUpdate().thenAccept(latest -> {
if (latest != null) {
logger.warn("[VelocityBroadcast] A new version is available: " + latest + " (You are on " + PLUGIN_VERSION + ")");
} else {
logger.info("[VelocityBroadcast] You are running the latest version (" + PLUGIN_VERSION + ").");
}
});
// ✅ Register login listener with DatabaseManager
server.getEventManager().register(this, new LoginListener(server, PLUGIN_VERSION, updateChecker, databaseManager));
// Register root command
server.getCommandManager().register( server.getCommandManager().register(
server.getCommandManager().metaBuilder("vb").plugin(this).build(), server.getCommandManager().metaBuilder("vb").plugin(this).build(),
new VBCommand(this) new VBCommand(this)
@ -78,4 +100,8 @@ public class VelocityBroadcast {
public ConfigHandler getConfigHandler() { public ConfigHandler getConfigHandler() {
return config; return config;
} }
public DatabaseManager getDatabaseManager() {
return databaseManager;
}
} }

View File

@ -0,0 +1,127 @@
package com.adzel.velocitybroadcast.util;
import java.nio.file.Path;
import java.sql.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import com.adzel.velocitybroadcast.ConfigHandler;
public class DatabaseManager {
private final Logger logger;
private final ConfigHandler config;
private final Path dataDirectory;
private Connection connection;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public DatabaseManager(ConfigHandler config, Logger logger, Path dataDirectory) {
this.config = config;
this.logger = logger;
this.dataDirectory = dataDirectory;
}
public void initialize() {
try {
if (config.getDbType().equalsIgnoreCase("mysql")) {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection(
"jdbc:mysql://" + config.getDbHost() + ":" + config.getDbPort() + "/" + config.getDbName() + "?useSSL=false&autoReconnect=true",
config.getDbUser(),
config.getDbPassword()
);
} else {
Class.forName("org.sqlite.JDBC");
Path dbFile = dataDirectory.resolve(config.getDbName() + ".db");
connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.toAbsolutePath());
}
logger.info("[VelocityBroadcast] Connected to " + config.getDbType().toUpperCase() + " database.");
initializeTables();
} catch (Exception e) {
logger.warning("[VelocityBroadcast] Failed to connect to database: " + e.getMessage());
}
}
private void initializeTables() {
boolean isMySQL = config.getDbType().equalsIgnoreCase("mysql");
String idSyntax = isMySQL ? "INT AUTO_INCREMENT PRIMARY KEY" : "INTEGER PRIMARY KEY AUTOINCREMENT";
String broadcastTable = "CREATE TABLE IF NOT EXISTS vb_broadcast_logs (" +
"id " + idSyntax + "," +
"timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP," +
"sender VARCHAR(64) NOT NULL," +
"message TEXT NOT NULL," +
"source_server VARCHAR(64)" +
");";
String commandTable = "CREATE TABLE IF NOT EXISTS vb_command_logs (" +
"id " + idSyntax + "," +
"timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP," +
"user VARCHAR(64) NOT NULL," +
"command TEXT NOT NULL," +
"success BOOLEAN" +
");";
String joinTable = "CREATE TABLE IF NOT EXISTS vb_admin_joins (" +
"id " + idSyntax + "," +
"timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP," +
"username VARCHAR(64) NOT NULL," +
"server VARCHAR(64)" +
");";
executeUpdate(broadcastTable);
executeUpdate(commandTable);
executeUpdate(joinTable);
}
public void logBroadcast(String sender, String message, String sourceServer) {
String sql = "INSERT INTO vb_broadcast_logs (sender, message, source_server) VALUES (?, ?, ?)";
executeAsync(sql, sender, message, sourceServer);
}
public void logCommand(String user, String command, boolean success) {
String sql = "INSERT INTO vb_command_logs (user, command, success) VALUES (?, ?, ?)";
executeAsync(sql, user, command, success);
}
public void logAdminJoin(String username, String serverName) {
String sql = "INSERT INTO vb_admin_joins (username, server) VALUES (?, ?)";
executeAsync(sql, username, serverName);
}
private void executeAsync(String sql, Object... params) {
executor.submit(() -> {
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
for (int i = 0; i < params.length; i++) {
stmt.setObject(i + 1, params[i]);
}
stmt.executeUpdate();
} catch (SQLException e) {
logger.warning("[VelocityBroadcast] Database query failed: " + e.getMessage());
}
});
}
private void executeUpdate(String sql) {
executor.submit(() -> {
try (Statement stmt = connection.createStatement()) {
stmt.executeUpdate(sql);
} catch (SQLException e) {
logger.warning("[VelocityBroadcast] Failed to initialize table: " + e.getMessage());
}
});
}
public void close() {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
executor.shutdown();
}
} catch (SQLException e) {
logger.warning("[VelocityBroadcast] Failed to close database: " + e.getMessage());
}
}
}

View File

@ -1,7 +1,7 @@
{ {
"id": "velocitybroadcast", "id": "velocitybroadcast",
"name": "VelocityBroadcast", "name": "VelocityBroadcast",
"version": "0.2-pre", "version": "0.3-pre",
"authors": ["Adzel"], "authors": ["Adzel"],
"main": "com.adzel.velocitybroadcast.VelocityBroadcast", "main": "com.adzel.velocitybroadcast.VelocityBroadcast",
"description": "A proxy-wide broadcast plugin for Velocity.", "description": "A proxy-wide broadcast plugin for Velocity.",

View File

@ -1,7 +1,7 @@
{ {
"id": "velocitybroadcast", "id": "velocitybroadcast",
"name": "VelocityBroadcast", "name": "VelocityBroadcast",
"version": "0.2-pre", "version": "0.3-pre",
"authors": ["Adzel"], "authors": ["Adzel"],
"main": "com.adzel.velocitybroadcast.VelocityBroadcast", "main": "com.adzel.velocitybroadcast.VelocityBroadcast",
"description": "A proxy-wide broadcast plugin for Velocity.", "description": "A proxy-wide broadcast plugin for Velocity.",

View File

@ -1 +1 @@
{"id":"velocitybroadcast","name":"VelocityBroadcast","version":"0.2-pre","description":"A proxy-wide broadcast plugin for Velocity.","authors":["Adzel"],"dependencies":[],"main":"com.adzel.velocitybroadcast.VelocityBroadcast"} {"id":"velocitybroadcast","name":"VelocityBroadcast","version":"0.3-pre","description":"A proxy-wide broadcast plugin for Velocity.","authors":["Adzel"],"dependencies":[],"main":"com.adzel.velocitybroadcast.VelocityBroadcast"}

View File

@ -1,3 +1,3 @@
artifactId=velocitybroadcast artifactId=velocitybroadcast
groupId=com.adzel.velocitybroadcast groupId=com.adzel.velocitybroadcast
version=0.2-pre version=0.3-pre

View File

@ -4,4 +4,7 @@ com\adzel\velocitybroadcast\BroadcastCommand.class
com\adzel\velocitybroadcast\ConfigHandler.class com\adzel\velocitybroadcast\ConfigHandler.class
com\adzel\velocitybroadcast\ReloadCommand.class com\adzel\velocitybroadcast\ReloadCommand.class
velocity-plugin.json velocity-plugin.json
com\adzel\velocitybroadcast\util\DatabaseManager.class
com\adzel\velocitybroadcast\PrefixCommand.class com\adzel\velocitybroadcast\PrefixCommand.class
com\adzel\velocitybroadcast\util\UpdateChecker.class
com\adzel\velocitybroadcast\LoginListener.class

View File

@ -2,5 +2,8 @@ C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\veloci
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\ReloadCommand.java C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\ReloadCommand.java
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\BroadcastCommand.java C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\BroadcastCommand.java
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\ConfigHandler.java C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\ConfigHandler.java
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\UpdateChecker.java
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\util\DatabaseManager.java
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\LoginListener.java
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\VelocityBroadcast.java C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\VelocityBroadcast.java
C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\PrefixCommand.java C:\Users\theon\OneDrive\Desktop\VelocityBroadcast\src\main\java\com\adzel\velocitybroadcast\PrefixCommand.java