diff --git a/client/pom.xml b/client/pom.xml index 906d536..a60db88 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -5,7 +5,7 @@ ace-of-shades nl.andrewlalis - 5.0 + 0.5.0 4.0.0 diff --git a/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerInfo.java b/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerInfo.java index 6d3f05e..e72d320 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerInfo.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerInfo.java @@ -6,6 +6,7 @@ import java.awt.*; public record PublicServerInfo( String name, String address, + String version, String description, String location, Image icon, @@ -14,35 +15,53 @@ public record PublicServerInfo( ) { public static ListCellRenderer cellRenderer() { return (list, value, index, isSelected, cellHasFocus) -> { - JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createTitledBorder(value.name())); + JPanel panel = new JPanel(new GridBagLayout()); + var c = new GridBagConstraints(); - panel.add(new JLabel("Address: " + value.address()), BorderLayout.NORTH); - - JPanel content = new JPanel(); + c.anchor = GridBagConstraints.CENTER; + c.insets = new Insets(1, 1, 1, 1); + c.gridx = 0; + c.gridy = 0; + c.gridheight = 4; + c.weightx = 0.25; + c.fill = GridBagConstraints.BOTH; if (value.icon() != null) { JLabel iconLabel = new JLabel(new ImageIcon(value.icon())); - content.add(iconLabel); + panel.add(iconLabel, c); } + c.anchor = GridBagConstraints.LINE_START; + c.gridx = 1; + c.gridy = 0; + c.gridheight = 1; + c.weightx = 0.75; + c.fill = GridBagConstraints.HORIZONTAL; + var nameLabel = new JLabel(value.name() + " [" + value.version() + "]"); + nameLabel.setFont(nameLabel.getFont().deriveFont(Font.BOLD)); + panel.add(nameLabel, c); + c.gridy++; + var addressLabel = new JLabel(value.address()); + addressLabel.setFont(new Font("monospaced", Font.PLAIN, 12)); + panel.add(addressLabel, c); + c.gridy++; JTextArea descriptionArea = new JTextArea(value.description()); descriptionArea.setEditable(false); descriptionArea.setWrapStyleWord(true); descriptionArea.setLineWrap(true); - content.add(descriptionArea); - panel.add(content, BorderLayout.CENTER); - - JPanel bottomPanel = new JPanel(); - bottomPanel.add(new JLabel(String.format("Current players: %d / %d", value.currentPlayers(), value.maxPlayers()))); - - panel.add(bottomPanel, BorderLayout.SOUTH); + panel.add(descriptionArea, c); + c.gridy++; + panel.add(new JLabel(String.format("%d / %d Players", value.currentPlayers(), value.maxPlayers())), c); if (isSelected) { panel.setBackground(list.getSelectionBackground()); panel.setForeground(list.getSelectionForeground()); + descriptionArea.setForeground(list.getSelectionForeground()); + descriptionArea.setBackground(list.getSelectionBackground()); } else { panel.setBackground(list.getBackground()); panel.setForeground(list.getForeground()); + descriptionArea.setForeground(list.getForeground()); + descriptionArea.setBackground(list.getBackground()); } return panel; diff --git a/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerListModel.java b/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerListModel.java index 6e2b26a..6d24c89 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerListModel.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/launcher/servers/PublicServerListModel.java @@ -149,6 +149,7 @@ public class PublicServerListModel extends AbstractListModel { PublicServerInfo info = new PublicServerInfo( node.get("name").asText(), node.get("address").asText(), + node.get("version").asText(), node.get("description").asText(), node.get("location").asText(), icon, diff --git a/core/pom.xml b/core/pom.xml index 9153652..ea6f593 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ ace-of-shades nl.andrewlalis - 5.0 + 0.5.0 4.0.0 diff --git a/pom.xml b/pom.xml index 6218cc7..b3bf81b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ nl.andrewlalis ace-of-shades pom - 5.0 + 0.5.0 server client diff --git a/server-registry/pom.xml b/server-registry/pom.xml index 96f59bb..83aa913 100644 --- a/server-registry/pom.xml +++ b/server-registry/pom.xml @@ -5,7 +5,7 @@ ace-of-shades nl.andrewlalis - 5.0 + 0.5.0 4.0.0 diff --git a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/ResponseStatusException.java b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/ResponseStatusException.java new file mode 100644 index 0000000..d578a9e --- /dev/null +++ b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/ResponseStatusException.java @@ -0,0 +1,21 @@ +package nl.andrewlalis.aos_server_registry.servlet; + +public class ResponseStatusException extends Exception { + private final int statusCode; + private final String message; + + public ResponseStatusException(int statusCode, String message) { + super(message); + this.statusCode = statusCode; + this.message = message; + } + + public int getStatusCode() { + return statusCode; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/ServerInfoServlet.java b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/ServerInfoServlet.java index 06e86f2..fe0cc93 100644 --- a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/ServerInfoServlet.java +++ b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/ServerInfoServlet.java @@ -40,7 +40,7 @@ public class ServerInfoServlet extends HttpServlet { )); String orderDir = Requests.getStringParam(req, "dir", "ASC", s -> s.equalsIgnoreCase("ASC") || s.equalsIgnoreCase("DESC")); try { - var results = this.getData(size, page, searchQuery, order, orderDir); + var results = this.getData(size, page, searchQuery, null, order, orderDir); Responses.ok(resp, new Page<>(results, page, size, order, orderDir)); } catch (SQLException t) { t.printStackTrace(); @@ -54,6 +54,8 @@ public class ServerInfoServlet extends HttpServlet { try { this.saveNewServer(info); Responses.ok(resp, Map.of("message", "Server icon saved.")); + } catch (ResponseStatusException e) { + Responses.json(resp, e.getStatusCode(), Map.of("message", e.getMessage())); } catch (SQLException e) { e.printStackTrace(); Responses.internalServerError(resp, "Database error."); @@ -66,11 +68,11 @@ public class ServerInfoServlet extends HttpServlet { this.updateServerStatus(status, resp); } - private List getData(int size, int page, String searchQuery, String order, String orderDir) throws SQLException, IOException { + private List getData(int size, int page, String searchQuery, String versionQuery, String order, String orderDir) throws SQLException, IOException { final List results = new ArrayList<>(20); var con = DataManager.getInstance().getConnection(); String selectQuery = """ - SELECT name, address, updated_at, description, location, icon, max_players, current_players + SELECT name, address, version, updated_at, description, location, icon, max_players, current_players FROM servers //CONDITIONS ORDER BY name @@ -78,20 +80,30 @@ public class ServerInfoServlet extends HttpServlet { OFFSET ? """; selectQuery = selectQuery.replace("ORDER BY name", "ORDER BY " + order + " " + orderDir); + List conditions = new ArrayList<>(); + List conditionParams = new ArrayList<>(); if (searchQuery != null && !searchQuery.isBlank()) { - selectQuery = selectQuery.replace("//CONDITIONS", "WHERE UPPER(name) LIKE ?"); + conditions.add("UPPER(name) LIKE ?"); + conditionParams.add("%" + searchQuery.toUpperCase() + "%"); + } + if (versionQuery != null && !versionQuery.isBlank()) { + conditions.add("version = ?"); + conditionParams.add(versionQuery); + } + if (!conditions.isEmpty()) { + selectQuery = selectQuery.replace("//CONDITIONS", "WHERE " + String.join(" AND ", conditions)); } PreparedStatement stmt = con.prepareStatement(selectQuery); int index = 1; - if (searchQuery != null && !searchQuery.isBlank()) { - stmt.setString(index++, "%" + searchQuery.toUpperCase() + "%"); + for (var param : conditionParams) { + stmt.setObject(index++, param); } stmt.setInt(index++, size); stmt.setInt(index, page * size); ResultSet rs = stmt.executeQuery(); while (rs.next()) { // Attempt to load the server's icon, if it is not null. - InputStream iconInputStream = rs.getBinaryStream(6); + InputStream iconInputStream = rs.getBinaryStream(7); String encodedIconImage = null; if (iconInputStream != null) { encodedIconImage = Base64.getUrlEncoder().encodeToString(iconInputStream.readAllBytes()); @@ -99,19 +111,20 @@ public class ServerInfoServlet extends HttpServlet { results.add(new ServerInfoResponse( rs.getString(1), rs.getString(2), - rs.getTimestamp(3).toInstant().atOffset(ZoneOffset.UTC).toString(), - rs.getString(4), + rs.getString(3), + rs.getTimestamp(4).toInstant().atOffset(ZoneOffset.UTC).toString(), rs.getString(5), + rs.getString(6), encodedIconImage, - rs.getInt(7), - rs.getInt(8) + rs.getInt(8), + rs.getInt(9) )); } stmt.close(); return results; } - private void saveNewServer(ServerInfoUpdate info) throws SQLException { + private void saveNewServer(ServerInfoUpdate info) throws SQLException, ResponseStatusException { var con = DataManager.getInstance().getConnection(); PreparedStatement stmt = con.prepareStatement("SELECT name, address FROM servers WHERE name = ? AND address = ?"); stmt.setString(1, info.name()); @@ -119,42 +132,45 @@ public class ServerInfoServlet extends HttpServlet { ResultSet rs = stmt.executeQuery(); boolean exists = rs.next(); stmt.close(); + String version = info.version() == null ? "Unknown" : info.version(); if (!exists) { PreparedStatement createStmt = con.prepareStatement(""" - INSERT INTO servers (name, address, description, location, icon, max_players, current_players) - VALUES (?, ?, ?, ?, ?, ?, ?); + INSERT INTO servers (name, address, version, description, location, icon, max_players, current_players) + VALUES (?, ?, ?, ?, ?, ?, ?, ?); """); createStmt.setString(1, info.name()); createStmt.setString(2, info.address()); - createStmt.setString(3, info.description()); - createStmt.setString(4, info.location()); + createStmt.setString(3, version); + createStmt.setString(4, info.description()); + createStmt.setString(5, info.location()); InputStream inputStream = null; if (info.icon() != null) { inputStream = new ByteArrayInputStream(Base64.getUrlDecoder().decode(info.icon())); } - createStmt.setBinaryStream(5, inputStream); - createStmt.setInt(6, info.maxPlayers()); - createStmt.setInt(7, info.currentPlayers()); + createStmt.setBinaryStream(6, inputStream); + createStmt.setInt(7, info.maxPlayers()); + createStmt.setInt(8, info.currentPlayers()); int rowCount = createStmt.executeUpdate(); createStmt.close(); if (rowCount != 1) throw new SQLException("Could not insert new server."); - log.info("Registered new server " + info.name() + " @ " + info.address()); + log.info("Registered new server " + info.name() + " @ " + info.address() + " running version " + version + "."); } else { PreparedStatement updateStmt = con.prepareStatement(""" - UPDATE servers SET description = ?, location = ?, icon = ?, max_players = ?, current_players = ? + UPDATE servers SET version = ?, description = ?, location = ?, icon = ?, max_players = ?, current_players = ? WHERE name = ? AND address = ?; """); - updateStmt.setString(1, info.description()); - updateStmt.setString(2, info.location()); + updateStmt.setString(1, version); + updateStmt.setString(2, info.description()); + updateStmt.setString(3, info.location()); InputStream inputStream = null; if (info.icon() != null) { inputStream = new ByteArrayInputStream(Base64.getUrlDecoder().decode(info.icon())); } - updateStmt.setBinaryStream(3, inputStream); - updateStmt.setInt(4, info.maxPlayers()); - updateStmt.setInt(5, info.currentPlayers()); - updateStmt.setString(6, info.name()); - updateStmt.setString(7, info.address()); + updateStmt.setBinaryStream(4, inputStream); + updateStmt.setInt(5, info.maxPlayers()); + updateStmt.setInt(6, info.currentPlayers()); + updateStmt.setString(7, info.name()); + updateStmt.setString(8, info.address()); int rowCount = updateStmt.executeUpdate(); updateStmt.close(); if (rowCount != 1) throw new SQLException("Could not update server."); diff --git a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoResponse.java b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoResponse.java index 6fd82d9..7d4ffd0 100644 --- a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoResponse.java +++ b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoResponse.java @@ -3,6 +3,7 @@ package nl.andrewlalis.aos_server_registry.servlet.dto; public record ServerInfoResponse( String name, String address, + String version, String updatedAt, String description, String location, diff --git a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoUpdate.java b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoUpdate.java index 0c0af2c..fa8e3b6 100644 --- a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoUpdate.java +++ b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/servlet/dto/ServerInfoUpdate.java @@ -3,6 +3,7 @@ package nl.andrewlalis.aos_server_registry.servlet.dto; public record ServerInfoUpdate ( String name, String address, + String version, String description, String location, String icon, diff --git a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/util/Responses.java b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/util/Responses.java index 98103b3..e439c4d 100644 --- a/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/util/Responses.java +++ b/server-registry/src/main/java/nl/andrewlalis/aos_server_registry/util/Responses.java @@ -21,6 +21,12 @@ public class Responses { mapper.writeValue(resp.getOutputStream(), body); } + public static void json(HttpServletResponse resp, int status, Object body) throws IOException { + resp.setStatus(status); + resp.setContentType("application/json"); + mapper.writeValue(resp.getOutputStream(), body); + } + public static void badRequest(HttpServletResponse resp, String msg) throws IOException { respond(resp, HttpServletResponse.SC_BAD_REQUEST, msg); } diff --git a/server-registry/src/main/resources/nl/andrewlalis/aos_server_registry/schema.sql b/server-registry/src/main/resources/nl/andrewlalis/aos_server_registry/schema.sql index 8e237a7..ac7b9d2 100644 --- a/server-registry/src/main/resources/nl/andrewlalis/aos_server_registry/schema.sql +++ b/server-registry/src/main/resources/nl/andrewlalis/aos_server_registry/schema.sql @@ -3,10 +3,11 @@ SET MODE MySQL; CREATE TABLE servers ( name VARCHAR(64) NOT NULL, address VARCHAR(255) NOT NULL, + version VARCHAR(64) NOT NULL DEFAULT 'Unknown Version', created_at TIMESTAMP(0) WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP(0), updated_at TIMESTAMP(0) WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP(0), description VARCHAR(1024), - location VARCHAR(64), + location VARCHAR(128), icon BLOB NULL DEFAULT NULL, max_players INTEGER NOT NULL, @@ -17,3 +18,4 @@ CREATE TABLE servers ( ); CREATE INDEX server_name_idx ON servers(name); +CREATE INDEX server_version_idx ON servers(version); diff --git a/server/pom.xml b/server/pom.xml index 90d9865..334f210 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -5,7 +5,7 @@ ace-of-shades nl.andrewlalis - 5.0 + 0.5.0 4.0.0 diff --git a/server/src/main/java/nl/andrewlalis/aos_server/RegistryManager.java b/server/src/main/java/nl/andrewlalis/aos_server/RegistryManager.java index 45ced45..1eb0760 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/RegistryManager.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/RegistryManager.java @@ -66,6 +66,7 @@ public class RegistryManager { Map data = new HashMap<>(); data.put("name", this.server.getSettings().getRegistrySettings().getName()); data.put("address", this.server.getSettings().getRegistrySettings().getAddress()); + data.put("version", Server.VERSION); data.put("description", this.server.getSettings().getRegistrySettings().getDescription()); data.put("location", this.server.getSettings().getRegistrySettings().getLocation()); data.put("icon", this.getIconData()); diff --git a/server/src/main/java/nl/andrewlalis/aos_server/Server.java b/server/src/main/java/nl/andrewlalis/aos_server/Server.java index e286d06..b2cfad6 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/Server.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/Server.java @@ -25,6 +25,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ThreadLocalRandom; public class Server { + public static final String VERSION = "0.5.0"; private final ServerSettings settings; private final List clientHandlers; @@ -263,6 +264,7 @@ public class Server { public static void main(String[] args) throws IOException { + System.out.println("Starting Ace of Shades Server version " + VERSION + "."); Server server = new Server(SettingsLoader.load()); server.run(); } diff --git a/server/src/main/java/nl/andrewlalis/aos_server/ServerCli.java b/server/src/main/java/nl/andrewlalis/aos_server/ServerCli.java index e9788d0..f52db83 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/ServerCli.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/ServerCli.java @@ -43,7 +43,7 @@ public class ServerCli extends Thread { public void run() { this.running = true; String input; - System.out.println("Server command-line-interface initialized. Type \"help\" for more information."); + System.out.println("AOS-Server command-line-interface initialized. Type \"help\" for more information."); while (this.running) { try { input = reader.readLine();