Added version tracking to client and server and updated registry.

This commit is contained in:
Andrew Lalis 2021-07-08 22:59:16 +02:00
parent c6d54d3f38
commit ec86b8cf69
16 changed files with 118 additions and 48 deletions

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId>
<version>5.0</version>
<version>0.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -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<PublicServerInfo> 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;

View File

@ -149,6 +149,7 @@ public class PublicServerListModel extends AbstractListModel<PublicServerInfo> {
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,

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId>
<version>5.0</version>
<version>0.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -7,7 +7,7 @@
<groupId>nl.andrewlalis</groupId>
<artifactId>ace-of-shades</artifactId>
<packaging>pom</packaging>
<version>5.0</version>
<version>0.5.0</version>
<modules>
<module>server</module>
<module>client</module>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId>
<version>5.0</version>
<version>0.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -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;
}
}

View File

@ -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<ServerInfoResponse> getData(int size, int page, String searchQuery, String order, String orderDir) throws SQLException, IOException {
private List<ServerInfoResponse> getData(int size, int page, String searchQuery, String versionQuery, String order, String orderDir) throws SQLException, IOException {
final List<ServerInfoResponse> 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<String> conditions = new ArrayList<>();
List<Object> 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.");

View File

@ -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,

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId>
<version>5.0</version>
<version>0.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -66,6 +66,7 @@ public class RegistryManager {
Map<String, Object> 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());

View File

@ -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<ClientHandler> 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();
}

View File

@ -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();