Skip to content

Commit f04ca6a

Browse files
committed
feat: add server ID for duplicate prevention
1 parent bfc069a commit f04ca6a

5 files changed

Lines changed: 120 additions & 53 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<version>${revision}</version>
1010

1111
<properties>
12-
<revision>1.0.0-SNAPSHOT</revision>
12+
<revision>1.1.0-SNAPSHOT</revision>
1313
<maven.compiler.source>21</maven.compiler.source>
1414
<maven.compiler.target>21</maven.compiler.target>
1515
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

src/main/java/dev/hytaleone/query/HytaleOneQueryConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ public class HytaleOneQueryConfig {
1212
public static final BuilderCodec<HytaleOneQueryConfig> CODEC = BuilderCodec.builder(HytaleOneQueryConfig.class, HytaleOneQueryConfig::new)
1313
.addField(new KeyedCodec<>("RegisterOnStartup", Codec.BOOLEAN),
1414
(o, v) -> o.registerOnStartup = v, o -> o.registerOnStartup)
15+
.addField(new KeyedCodec<>("ServerIdDoNotChange", Codec.STRING),
16+
(o, v) -> o.serverId = v, o -> o.serverId)
1517
.build();
1618

1719
private boolean registerOnStartup = true;
20+
private String serverId = null;
1821

1922
public HytaleOneQueryConfig() {
2023
}
@@ -29,4 +32,15 @@ public boolean isRegisterOnStartup() {
2932
public void setRegisterOnStartup(boolean register) {
3033
this.registerOnStartup = register;
3134
}
35+
36+
/**
37+
* Unique server identifier for duplicate prevention.
38+
*/
39+
public String getServerId() {
40+
return serverId;
41+
}
42+
43+
public void setServerId(String serverId) {
44+
this.serverId = serverId;
45+
}
3246
}

src/main/java/dev/hytaleone/query/HytaleOneQueryPlugin.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ protected void start() {
5959

6060
// Register with server list service (if enabled)
6161
if (config.isRegisterOnStartup()) {
62-
HytaleOneServerListRegistration.register(getLogger());
62+
HytaleOneServerListRegistration.register(getLogger(), config, this::saveConfig);
6363
} else {
6464
getLogger().at(Level.INFO).log("Server list registration is disabled");
6565
}
@@ -101,6 +101,13 @@ private void loadConfig() {
101101
}
102102
}
103103

104+
private void saveConfig() {
105+
var serverConfig = HytaleServer.get().getConfig();
106+
var module = serverConfig.getModule(CONFIG_MODULE);
107+
module.encode(HytaleOneQueryConfig.CODEC, this.config);
108+
serverConfig.markChanged();
109+
}
110+
104111
@Nonnull
105112
public HytaleOneQueryConfig getConfig() {
106113
return config;

src/main/java/dev/hytaleone/query/HytaleOneServerListRegistration.java

Lines changed: 96 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import com.hypixel.hytale.server.core.HytaleServer;
77
import com.hypixel.hytale.server.core.io.ServerManager;
88
import com.hypixel.hytale.server.core.universe.Universe;
9+
import org.bson.Document;
910

1011
import javax.annotation.Nonnull;
1112
import java.net.InetSocketAddress;
1213
import java.net.URI;
1314
import java.net.http.HttpClient;
1415
import java.net.http.HttpRequest;
1516
import java.net.http.HttpResponse;
17+
import java.security.SecureRandom;
1618
import java.time.Duration;
1719
import java.util.concurrent.CompletableFuture;
1820
import java.util.logging.Level;
@@ -22,7 +24,7 @@
2224
*/
2325
public final class HytaleOneServerListRegistration {
2426

25-
private static final String ENDPOINT = "https://hytale.one/api/plugin/query/register";
27+
private static final String ENDPOINT = "http://hytale.one/api/plugin/query/register";
2628
private static final Duration TIMEOUT = Duration.ofSeconds(10);
2729

2830
private HytaleOneServerListRegistration() {
@@ -32,14 +34,24 @@ private HytaleOneServerListRegistration() {
3234
* Register the server with the server list service.
3335
* Runs asynchronously to not block server startup.
3436
*/
35-
public static void register(@Nonnull HytaleLogger logger) {
37+
public static void register(@Nonnull HytaleLogger logger, @Nonnull HytaleOneQueryConfig config, @Nonnull Runnable saveConfig) {
38+
// Generate server ID if missing
39+
if (config.getServerId() == null || config.getServerId().isBlank()) {
40+
String newId = "hytaleone_" + generateRandomHex(32);
41+
config.setServerId(newId);
42+
saveConfig.run();
43+
logger.at(Level.FINE).log("Generated new server ID: %s", newId);
44+
}
45+
46+
final String serverId = config.getServerId();
3647

3748
CompletableFuture.runAsync(() -> {
3849
try {
39-
String json = buildPayload();
50+
String json = buildPayload(serverId);
4051

4152
HttpClient client = HttpClient.newBuilder()
4253
.connectTimeout(TIMEOUT)
54+
.version(HttpClient.Version.HTTP_1_1)
4355
.build();
4456

4557
HttpRequest request = HttpRequest.newBuilder()
@@ -53,7 +65,7 @@ public static void register(@Nonnull HytaleLogger logger) {
5365
HttpResponse.BodyHandlers.ofString());
5466

5567
if (response.statusCode() >= 200 && response.statusCode() < 300) {
56-
logger.at(Level.INFO).log("Server registered with hytale.one");
68+
handleSuccessResponse(logger, response.body());
5769
} else {
5870
logger.at(Level.WARNING).log("Server list registration failed (status: %d)",
5971
response.statusCode());
@@ -64,29 +76,91 @@ public static void register(@Nonnull HytaleLogger logger) {
6476
});
6577
}
6678

79+
/**
80+
* Handle successful registration response.
81+
*/
82+
private static void handleSuccessResponse(@Nonnull HytaleLogger logger, @Nonnull String body) {
83+
String url = null;
84+
boolean claimed = false;
85+
86+
try {
87+
Document doc = Document.parse(body);
88+
url = doc.getString("url");
89+
claimed = doc.getBoolean("claimed", false);
90+
} catch (Exception e) {
91+
// Response parsing failed, just log simple message
92+
}
93+
94+
if (claimed) {
95+
printClaimedMessage(logger, url);
96+
} else if (url != null && !url.isBlank()) {
97+
printPromotion(logger, url);
98+
} else {
99+
logger.at(Level.INFO).log("Server registered with hytale.one");
100+
}
101+
}
102+
103+
/**
104+
* Print message for claimed servers.
105+
*/
106+
private static void printClaimedMessage(@Nonnull HytaleLogger logger, String url) {
107+
logger.at(Level.INFO).log("");
108+
logger.at(Level.INFO).log("Your server is listed on hytale.one server list!");
109+
if (url != null && !url.isBlank()) {
110+
logger.at(Level.INFO).log("Manage your server: %s", url);
111+
}
112+
logger.at(Level.INFO).log("");
113+
}
114+
115+
/**
116+
* Print promotional message to encourage claiming.
117+
*/
118+
private static void printPromotion(@Nonnull HytaleLogger logger, @Nonnull String url) {
119+
logger.at(Level.INFO).log("");
120+
logger.at(Level.INFO).log("╔═══════════════════════════════════════════════════════════════════════════╗");
121+
logger.at(Level.INFO).log("║ Get more players! Your server is waiting to be discovered. ║");
122+
logger.at(Level.INFO).log("║ ║");
123+
logger.at(Level.INFO).log("║ Claim your server on hytale.one to: ║");
124+
logger.at(Level.INFO).log("║ - Appear in the server list and attract new players ║");
125+
logger.at(Level.INFO).log("║ - Add banners, descriptions, and showcase your community ║");
126+
logger.at(Level.INFO).log("║ ║");
127+
logger.at(Level.INFO).log("║ Claim now: %-63s ║", url);
128+
logger.at(Level.INFO).log("╚═══════════════════════════════════════════════════════════════════════════╝");
129+
logger.at(Level.INFO).log("");
130+
}
131+
132+
/**
133+
* Generate random hex string for server ID.
134+
*/
135+
private static String generateRandomHex(int length) {
136+
SecureRandom random = new SecureRandom();
137+
byte[] bytes = new byte[length / 2];
138+
random.nextBytes(bytes);
139+
StringBuilder sb = new StringBuilder();
140+
for (byte b : bytes) {
141+
sb.append(String.format("%02x", b));
142+
}
143+
return sb.toString();
144+
}
145+
67146
/**
68147
* Build the JSON payload with server information.
69148
*/
70-
private static String buildPayload() {
149+
private static String buildPayload(@Nonnull String serverId) {
71150
var config = HytaleServer.get().getConfig();
72151

73-
String host = getHostAddress();
74-
int port = getHostPort();
75-
76-
// Manual JSON building to avoid external dependencies
77-
StringBuilder json = new StringBuilder();
78-
json.append("{");
79-
json.append("\"serverName\":").append(escapeJson(config.getServerName())).append(",");
80-
json.append("\"motd\":").append(escapeJson(config.getMotd())).append(",");
81-
json.append("\"host\":").append(escapeJson(host)).append(",");
82-
json.append("\"port\":").append(port).append(",");
83-
json.append("\"maxPlayers\":").append(config.getMaxPlayers()).append(",");
84-
json.append("\"currentPlayers\":").append(Universe.get().getPlayerCount()).append(",");
85-
json.append("\"version\":").append(escapeJson(getVersion())).append(",");
86-
json.append("\"protocolVersion\":").append(ProtocolSettings.PROTOCOL_VERSION);
87-
json.append("}");
88-
89-
return json.toString();
152+
Document doc = new Document()
153+
.append("serverId", serverId)
154+
.append("serverName", config.getServerName())
155+
.append("motd", config.getMotd())
156+
.append("host", getHostAddress())
157+
.append("port", getHostPort())
158+
.append("maxPlayers", config.getMaxPlayers())
159+
.append("currentPlayers", Universe.get().getPlayerCount())
160+
.append("version", getVersion())
161+
.append("protocolVersion", ProtocolSettings.PROTOCOL_VERSION);
162+
163+
return doc.toJson();
90164
}
91165

92166
private static String getVersion() {
@@ -115,32 +189,4 @@ private static int getHostPort() {
115189
}
116190
return 5520;
117191
}
118-
119-
/**
120-
* Escape a string for JSON.
121-
*/
122-
private static String escapeJson(String str) {
123-
if (str == null) {
124-
return "null";
125-
}
126-
StringBuilder sb = new StringBuilder("\"");
127-
for (char c : str.toCharArray()) {
128-
switch (c) {
129-
case '"' -> sb.append("\\\"");
130-
case '\\' -> sb.append("\\\\");
131-
case '\n' -> sb.append("\\n");
132-
case '\r' -> sb.append("\\r");
133-
case '\t' -> sb.append("\\t");
134-
default -> {
135-
if (c < 32) {
136-
sb.append(String.format("\\u%04x", (int) c));
137-
} else {
138-
sb.append(c);
139-
}
140-
}
141-
}
142-
}
143-
sb.append("\"");
144-
return sb.toString();
145-
}
146192
}

src/main/resources/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"Group": "HytaleOne",
33
"Name": "Query",
4-
"Version": "1.0.0",
4+
"Version": "1.1.0",
55
"Authors": [
66
{
77
"Name": "HytaleOne",

0 commit comments

Comments
 (0)