142 lines
5.4 KiB
Java
142 lines
5.4 KiB
Java
package nl.andrewlalis.aos_server;
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
import javax.imageio.ImageIO;
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.IOException;
|
|
import java.net.URI;
|
|
import java.net.http.HttpClient;
|
|
import java.net.http.HttpRequest;
|
|
import java.net.http.HttpResponse;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.time.Duration;
|
|
import java.util.Base64;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* The registry manager is responsible for keeping the server registry up to
|
|
* date with this server's information, by sending periodic update HTTP messages.
|
|
*/
|
|
public class RegistryManager {
|
|
/**
|
|
* The list of retry timings that will be used if the registry server cannot
|
|
* be reached. Using the retryTimingIndex, we'll start at 5, and increment
|
|
* each time the connection fails.
|
|
*/
|
|
public static final long[] RETRY_TIMINGS = new long[]{5, 10, 30, 60, 120, 300};
|
|
private int retryTimingIndex = 0;
|
|
|
|
private final ScheduledExecutorService executorService;
|
|
private final Server server;
|
|
|
|
private final ObjectMapper mapper;
|
|
private final HttpClient httpClient;
|
|
|
|
public RegistryManager(Server server) {
|
|
this.server = server;
|
|
this.mapper = new ObjectMapper();
|
|
this.executorService = Executors.newScheduledThreadPool(3);
|
|
this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(3)).build();
|
|
this.executorService.submit(this::sendInfo);
|
|
this.executorService.scheduleAtFixedRate(
|
|
this::sendUpdate,
|
|
server.getSettings().getRegistrySettings().getUpdateInterval(),
|
|
server.getSettings().getRegistrySettings().getUpdateInterval(),
|
|
TimeUnit.SECONDS
|
|
);
|
|
}
|
|
|
|
public void sendInfo() {
|
|
try {
|
|
Map<String, Object> data = new HashMap<>();
|
|
data.put("name", this.server.getSettings().getRegistrySettings().getName());
|
|
data.put("address", this.server.getSettings().getRegistrySettings().getAddress());
|
|
data.put("description", this.server.getSettings().getRegistrySettings().getDescription());
|
|
data.put("location", this.server.getSettings().getRegistrySettings().getLocation());
|
|
data.put("icon", this.getIconData());
|
|
data.put("maxPlayers", this.server.getSettings().getMaxPlayers());
|
|
data.put("currentPlayers", 0);
|
|
HttpRequest request = HttpRequest.newBuilder()
|
|
.uri(new URI(this.server.getSettings().getRegistrySettings().getRegistryUri() + "/serverInfo"))
|
|
.POST(HttpRequest.BodyPublishers.ofByteArray(this.mapper.writeValueAsBytes(data)))
|
|
.header("Content-Type", "application/json")
|
|
.build();
|
|
try {
|
|
System.out.println("Sending server information to registry...");
|
|
var response = this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
if (response.statusCode() != 200) {
|
|
System.err.println("Non-OK status when sending registry info:\n" + response.body() + "\nAttempting to send again in 10 seconds...");
|
|
this.executorService.schedule(this::sendInfo, 10, TimeUnit.SECONDS);
|
|
} else if (this.retryTimingIndex > 0) {
|
|
this.retryTimingIndex = 0; // Reset the retry timing index if we successfully sent our server info.
|
|
}
|
|
} catch (IOException e) {
|
|
long retryTiming = RETRY_TIMINGS[this.retryTimingIndex];
|
|
System.err.println("Could not send info to registry server. Registry may be offline, or this server may not have internet access. Attempting to resend info in " + retryTiming + " seconds...");
|
|
this.executorService.schedule(this::sendInfo, retryTiming, TimeUnit.SECONDS);
|
|
if (this.retryTimingIndex < RETRY_TIMINGS.length - 1) {
|
|
this.retryTimingIndex++;
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void sendUpdate() {
|
|
try {
|
|
Map<String, Object> data = new HashMap<>();
|
|
data.put("name", this.server.getSettings().getRegistrySettings().getName());
|
|
data.put("address", this.server.getSettings().getRegistrySettings().getAddress());
|
|
data.put("currentPlayers", server.getPlayerCount());
|
|
HttpRequest request = HttpRequest.newBuilder()
|
|
.uri(new URI(this.server.getSettings().getRegistrySettings().getRegistryUri() + "/serverInfo"))
|
|
.PUT(HttpRequest.BodyPublishers.ofByteArray(this.mapper.writeValueAsBytes(data)))
|
|
.header("Content-Type", "application/json")
|
|
.build();
|
|
try {
|
|
var response = this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
if (response.statusCode() != 200) {
|
|
System.err.println("Received non-OK status when sending registry update:\n" + response.body());
|
|
}
|
|
} catch (IOException e) {
|
|
System.err.println("Error sending update to registry server: " + e);
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private String getIconData() throws IOException {
|
|
Path iconFile = Path.of("icon.png");
|
|
if (Files.exists(iconFile)) {
|
|
byte[] imageBytes = Files.readAllBytes(iconFile);
|
|
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));
|
|
if (image.getWidth() == 64 && image.getHeight() == 64) {
|
|
return Base64.getUrlEncoder().encodeToString(imageBytes);
|
|
} else {
|
|
System.err.println("icon.png must be 64 x 64.");
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void shutdown() {
|
|
this.executorService.shutdown();
|
|
try {
|
|
while (!this.executorService.awaitTermination(3, TimeUnit.SECONDS)) {
|
|
System.out.println("Waiting for scheduler to terminate.");
|
|
}
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|