summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPugdoot <pugmatt@gmail.com>2025-05-23 17:16:16 -0400
committerPugdoot <pugmatt@gmail.com>2025-05-24 19:21:57 -0400
commitf7a20c2e8b9d306d8bc4e39f5383bdadabe3de87 (patch)
tree8a006ce76d12aa80524d4bcd1890a3c7db6c02a7
parentb0d51faef380b6da1a7337f58837d6568f0de91e (diff)
downloadBedrockConnect-f7a20c2e8b9d306d8bc4e39f5383bdadabe3de87.tar.gz
BedrockConnect-f7a20c2e8b9d306d8bc4e39f5383bdadabe3de87.tar.bz2
BedrockConnect-f7a20c2e8b9d306d8bc4e39f5383bdadabe3de87.zip
Implement optional MOTD screen
-rw-r--r--serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/Config.java51
-rw-r--r--serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/data/DataUtil.java92
-rw-r--r--serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/BCPlayer.java24
-rw-r--r--serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/PacketHandler.java13
-rw-r--r--serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/gui/UIForms.java17
-rw-r--r--serverlist-server/src/main/resources/language.json4
6 files changed, 192 insertions, 9 deletions
diff --git a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/Config.java b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/Config.java
index c723661..7ef67c0 100644
--- a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/Config.java
+++ b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/Config.java
@@ -1,7 +1,9 @@
package main.com.pyratron.pugmatt.bedrockconnect;
+import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -13,6 +15,7 @@ import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileReader;
import org.apache.logging.log4j.Level;
@@ -37,6 +40,9 @@ public class Config {
private String serverLimit = "100";
private boolean dbAutoReconnect = false;
private boolean noDB = false;
+ private String motdMessage = null;
+ private boolean motdFirstJoin = true;
+ private int motdCooldown = 0;
private boolean kickInactive = true;
private boolean userServers = true;
private boolean featuredServers = true;
@@ -73,6 +79,7 @@ public class Config {
String customServersFile = null;
String languageFile = null;
String whitelistFile = null;
+ String motdFile = null;
boolean nodbWarning = true;
boolean mysqlSettingWarning = false;
@@ -234,6 +241,15 @@ public class Config {
case "debug":
debug = setting.getValue().equalsIgnoreCase("true");
break;
+ case "motd":
+ motdFile = setting.getValue();
+ break;
+ case "motd_first_join":
+ motdFirstJoin = setting.getValue().equalsIgnoreCase("true");
+ break;
+ case "motd_cooldown":
+ motdCooldown = Integer.parseInt(setting.getValue());
+ break;
}
}
@@ -328,7 +344,24 @@ public class Config {
}
}
- BedrockConnect.loadDatabase(dbHost, dbName, dbUser, dbPass, dbType, dbAutoReconnect, noDB);
+ if (motdFile != null) {
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(motdFile), "UTF-8"));
+ String msg = "";
+ String line = "";
+ while ((line = in.readLine()) != null) {
+ msg += line + "\n";
+ }
+ motdMessage = msg + "\n";
+ in.close();
+ BedrockConnect.logger.debug("MOTD data: " + motdMessage.toString());
+ } catch (Exception e) {
+ BedrockConnect.logger.error("An error occurred reading the MOTD file", e);
+ System.exit(1);
+ }
+ }
+
+ BedrockConnect.loadDatasource(dbHost, dbName, dbUser, dbPass, dbType, dbAutoReconnect, noDB);
}
public String getPort() {
@@ -351,6 +384,22 @@ public class Config {
return noDB;
}
+ public String getMotdMessage() {
+ return motdMessage;
+ }
+
+ public boolean isShowingMotdFirstJoin() {
+ return motdFirstJoin;
+ }
+
+ public int getMotdCooldown() {
+ return motdCooldown;
+ }
+
+ public boolean isMotdCooldownEnabled() {
+ return getMotdCooldown() > 0;
+ }
+
public boolean canKickInactive() {
return kickInactive;
}
diff --git a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/data/DataUtil.java b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/data/DataUtil.java
index 00599ce..cb63906 100644
--- a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/data/DataUtil.java
+++ b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/data/DataUtil.java
@@ -13,10 +13,13 @@ import org.json.simple.parser.JSONParser;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
+import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -24,11 +27,29 @@ public class DataUtil {
private Database database;
+ // Some optional columns were added in a later version, consider their existance during data retrieval/updating
+ private boolean viewedMotdExists = false;
+
public DataUtil(Database database) {
this.database = database;
if (!BedrockConnect.getConfig().isNoDB()) {
try {
createTables((database.getType() == DatabaseTypes.postgres));
+
+ // Check for existance of columns added in later versions
+ DatabaseMetaData metaData = database.getConnection().getMetaData();
+ viewedMotdExists = columnExists(metaData, "viewedMotd");
+
+ // If configuration is set to consider motd viewing date but viewedMotd column does not currently exist, then add viewedMotd column to table
+ if (BedrockConnect.getConfig().isMotdCooldownEnabled() && !viewedMotdExists) {
+ String dateType = "DATETIME";
+ if (database.getType() == DatabaseTypes.postgres) dateType = "TIMESTAMP";
+
+ Statement stmt = database.getConnection().createStatement();
+ stmt.execute("ALTER TABLE servers ADD viewedMotd " + dateType + ";");
+
+ viewedMotdExists = true;
+ }
} catch (Exception e) {
errorAlert(e);
}
@@ -45,6 +66,7 @@ public class DataUtil {
+ " name TEXT,"
+ " servers TEXT,"
+ " serverLimit INTEGER,"
+ + " viewedMotd DATETIME,"
+ " INDEX (uuid(255))"
+ ");";
else
@@ -56,7 +78,8 @@ public class DataUtil {
" uuid TEXT,\n" +
" name TEXT,\n" +
" servers TEXT,\n" +
- " serverLimit INTEGER\n" +
+ " serverLimit INTEGER,\n" +
+ " viewedMotd TIMESTAMP\n" +
" );\n" +
" END IF;\n" +
"\n" +
@@ -71,8 +94,15 @@ public class DataUtil {
private BCPlayer getPlayer(ResultSet rs, String name, String uuid, BedrockServerSession session) {
try {
+ LocalDateTime viewedMotd = null;
+ if (viewedMotdExists) {
+ Timestamp ts = rs.getTimestamp("viewedMotd");
+ if (ts != null)
+ viewedMotd = ts.toLocalDateTime();
+ }
+
BCPlayer p = new BCPlayer(name, uuid, session, UIComponents.getFormData(rs.getString("servers")),
- rs.getInt("serverLimit"));
+ rs.getInt("serverLimit"), false, viewedMotd);
return p;
} catch (SQLException e) {
errorAlert(e);
@@ -89,7 +119,7 @@ public class DataUtil {
s.setInt(3, Integer.parseInt(BedrockConnect.getConfig().getServerLimit()));
s.executeUpdate();
BedrockConnect.logger.info("Added new user " + name + " (xuid: " + uuid + ") to Database");
- BCPlayer pl = new BCPlayer(name, uuid, session, new ArrayList<>(), Integer.parseInt(BedrockConnect.getConfig().getServerLimit()));
+ BCPlayer pl = new BCPlayer(name, uuid, session, new ArrayList<>(), Integer.parseInt(BedrockConnect.getConfig().getServerLimit()), true, null);
packetHandler.setPlayer(pl);
BedrockConnect.getServer().addPlayer(pl);
} catch (Exception e) {
@@ -159,7 +189,7 @@ public class DataUtil {
BedrockConnect.logger.info("Added new user " + name + " (xuid: " + uuid + ")");
- BCPlayer pl = new BCPlayer(name, uuid, session, new ArrayList<>(), Integer.parseInt(BedrockConnect.getConfig().getServerLimit()));
+ BCPlayer pl = new BCPlayer(name, uuid, session, new ArrayList<>(), Integer.parseInt(BedrockConnect.getConfig().getServerLimit()), true, null);
packetHandler.setPlayer(pl);
BedrockConnect.getServer().addPlayer(pl);
} else {
@@ -171,7 +201,12 @@ public class DataUtil {
JSONArray servers = (JSONArray) jo.get("servers");
- BCPlayer p = new BCPlayer(name, uuid, session, servers, serverLimit);
+ LocalDateTime viewedMotd = null;
+ if (jo.containsKey("viewedMotd")) {
+ viewedMotd = LocalDateTime.parse((String) jo.get("viewedMotd"));
+ }
+
+ BCPlayer p = new BCPlayer(name, uuid, session, servers, serverLimit, false, viewedMotd);
packetHandler.setPlayer(p);
BedrockConnect.getServer().addPlayer(p);
}
@@ -226,6 +261,53 @@ public class DataUtil {
errorAlert(e);
}
}).start();
+
+ }
+
+ public void setViewedMotd(String uuid) {
+ if (!BedrockConnect.getConfig().isNoDB()) {
+ if (viewedMotdExists) {
+ new Thread(() -> {
+ try {
+ PreparedStatement s = database.getConnection()
+ .prepareStatement("UPDATE servers SET viewedMotd = ? WHERE uuid= ?");
+ s.setTimestamp(1, Timestamp.valueOf(LocalDateTime.now()));
+ s.setString(2, uuid);
+
+ s.executeUpdate();
+ } catch (Exception e) {
+ errorAlert(e);
+ }
+ }).start();
+ }
+ } else {
+ try {
+ Object obj = new JSONParser().parse(new FileReader("players/" + uuid + ".json"));
+
+ JSONObject jo = (JSONObject) obj;
+
+ jo.put("viewedMotd", LocalDateTime.now().toString());
+
+ PrintWriter pw = new PrintWriter("players/" + uuid + ".json");
+ pw.write(jo.toJSONString());
+ pw.flush();
+ pw.close();
+ } catch (Exception e) {
+ BedrockConnect.logger.error("An error occurred saving to player file", e);
+ }
+ }
+ }
+
+ private boolean columnExists(DatabaseMetaData meta, String columnName) throws SQLException {
+ try (ResultSet cols = meta.getColumns(null, "%", "servers", "%")) {
+ while (cols.next()) {
+ String col = cols.getString("COLUMN_NAME");
+ if (col.equalsIgnoreCase(columnName)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
public void errorAlert(Exception e) {
diff --git a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/BCPlayer.java b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/BCPlayer.java
index 24446be..5d56c54 100644
--- a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/BCPlayer.java
+++ b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/BCPlayer.java
@@ -20,6 +20,7 @@ import org.cloudburstmc.protocol.common.util.OptionalBoolean;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
+import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
@@ -34,6 +35,7 @@ public class BCPlayer {
private String uuid;
private LocalTime lastAction;
+ private LocalDateTime viewedMotd;
private int currentForm = 0;
private LocalTime movementOpenCoolDown = LocalTime.now();
@@ -41,6 +43,8 @@ public class BCPlayer {
private int editingServer = -1;
private int selectedGroup = -1;
+ private boolean newPlayer = false;
+
private static final NbtMap EMPTY_TAG = NbtMap.EMPTY;
private static final byte[] EMPTY_LEVEL_CHUNK_DATA;
@@ -59,13 +63,15 @@ public class BCPlayer {
}
- public BCPlayer(String displayName, String uuid, BedrockServerSession session, List<String> serverList, int serverLimit) {
+ public BCPlayer(String displayName, String uuid, BedrockServerSession session, List<String> serverList, int serverLimit, boolean newPlayer, LocalDateTime viewedMotd) {
this.displayName = displayName;
this.uuid = uuid;
this.session = session;
this.serverList = serverList;
this.serverLimit = serverLimit;
this.lastAction = LocalTime.now();
+ this.viewedMotd = viewedMotd;
+ this.newPlayer = newPlayer;
if(session != null && session.isConnected())
joinGame();
@@ -160,6 +166,19 @@ public class BCPlayer {
public int getSelectedGroup() { return selectedGroup; }
+ public boolean isNewPlayer() { return newPlayer; }
+
+ public LocalDateTime getViewedMotd() { return viewedMotd; }
+
+ public boolean canShowMotd() {
+ if (!BedrockConnect.getConfig().isShowingMotdFirstJoin() && isNewPlayer()) return false;
+
+ if (BedrockConnect.getConfig().isMotdCooldownEnabled() && viewedMotd != null)
+ return LocalDateTime.now().isAfter(viewedMotd.plusDays(BedrockConnect.getConfig().getMotdCooldown()));
+
+ return true;
+ }
+
public void movementOpen() {
if(canMovementOpen()) {
@@ -205,6 +224,9 @@ public class BCPlayer {
case UIForms.SERVER_GROUP:
form = UIForms.createServerGroup((CustomServerGroup) BedrockConnect.getConfig().getCustomServers()[getSelectedGroup()], session);
break;
+ case UIForms.MOTD:
+ form = UIForms.createMotd();
+ break;
default:
form = UIForms.createMain(getServerList(), session);
break;
diff --git a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/PacketHandler.java b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/PacketHandler.java
index b9fbf35..e256c7b 100644
--- a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/PacketHandler.java
+++ b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/PacketHandler.java
@@ -121,6 +121,13 @@ public class PacketHandler implements BedrockPacketHandler {
player.resetMovementOpen();
switch (packet.getFormId()) {
+ case UIForms.MOTD:
+ // Store datetime of when motd was viewed, if we need it for tracking when to show it again
+ if (BedrockConnect.getConfig().isMotdCooldownEnabled()) {
+ BedrockConnect.getDataUtil().setViewedMotd(player.getUuid());
+ }
+ player.openForm(UIForms.MAIN);
+ break;
case UIForms.MAIN:
// Re-open window if closed
if (packet.getFormData() == null || packet.getFormData().contains("null")) {
@@ -429,7 +436,11 @@ public class PacketHandler implements BedrockPacketHandler {
@Override
public PacketSignal handle(SetLocalPlayerAsInitializedPacket packet) {
- session.sendPacketImmediately(UIForms.createMain(player.getServerList(), session));
+ if (BedrockConnect.getConfig().getMotdMessage() != null && player.canShowMotd()) {
+ player.openForm(UIForms.MOTD);
+ } else {
+ player.openForm(UIForms.MAIN);
+ }
return PacketSignal.HANDLED;
}
diff --git a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/gui/UIForms.java b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/gui/UIForms.java
index ee15255..874372f 100644
--- a/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/gui/UIForms.java
+++ b/serverlist-server/src/main/com/pyratron/pugmatt/bedrockconnect/server/gui/UIForms.java
@@ -15,7 +15,7 @@ import java.util.ArrayList;
import java.util.List;
public class UIForms {
- public static final int ERROR = 2, MAIN = 0, DIRECT_CONNECT = 1, REMOVE_SERVER = 3, MANAGE_SERVER = 4, EDIT_SERVER = 5, EDIT_CHOOSE_SERVER = 6, ADD_SERVER = 7, SERVER_GROUP = 8;
+ public static final int ERROR = 2, MAIN = 0, DIRECT_CONNECT = 1, REMOVE_SERVER = 3, MANAGE_SERVER = 4, EDIT_SERVER = 5, EDIT_CHOOSE_SERVER = 6, ADD_SERVER = 7, SERVER_GROUP = 8, MOTD = 9;
public static JsonArray mainMenuButtons = new JsonArray();
public static JsonArray manageListButtons = new JsonArray();
@@ -280,4 +280,19 @@ public class UIForms {
mf.setFormData(form.toString());
return mf;
}
+
+ public static ModalFormRequestPacket createMotd() {
+ ModalFormRequestPacket mf = new ModalFormRequestPacket();
+ mf.setFormId(UIForms.MOTD);
+ JsonObject form = UIComponents.createForm("form", BedrockConnect.getConfig().getLanguage().getWording("motd", "heading"));
+
+ form.addProperty("content", BedrockConnect.getConfig().getMotdMessage());
+
+ JsonArray buttons = new JsonArray();
+ buttons.add(UIComponents.createButton(BedrockConnect.getConfig().getLanguage().getWording("motd", "continueBtn")));
+ form.add("buttons", buttons);
+
+ mf.setFormData(form.toString());
+ return mf;
+ }
}
diff --git a/serverlist-server/src/main/resources/language.json b/serverlist-server/src/main/resources/language.json
index 97db330..6f85e7c 100644
--- a/serverlist-server/src/main/resources/language.json
+++ b/serverlist-server/src/main/resources/language.json
@@ -1,4 +1,8 @@
{
+ "motd": {
+ "heading": "Message of the Day",
+ "continueBtn": "Continue to Server List"
+ },
"serverInfo": {
"motd": "Join To Open Server List",
"subMotd": "BedrockConnect Server List"