66import com .hypixel .hytale .server .core .HytaleServer ;
77import com .hypixel .hytale .server .core .io .ServerManager ;
88import com .hypixel .hytale .server .core .universe .Universe ;
9+ import org .bson .Document ;
910
1011import javax .annotation .Nonnull ;
1112import java .net .InetSocketAddress ;
1213import java .net .URI ;
1314import java .net .http .HttpClient ;
1415import java .net .http .HttpRequest ;
1516import java .net .http .HttpResponse ;
17+ import java .security .SecureRandom ;
1618import java .time .Duration ;
1719import java .util .concurrent .CompletableFuture ;
1820import java .util .logging .Level ;
2224 */
2325public 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}
0 commit comments