This commit is contained in:
parent
c7fe26ef8f
commit
330c3efc78
81
src/main/java/cc.fascinated/common/ColorUtils.java
Normal file
81
src/main/java/cc.fascinated/common/ColorUtils.java
Normal file
@ -0,0 +1,81 @@
|
||||
package cc.fascinated.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class ColorUtils {
|
||||
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)§[0-9A-FK-OR]");
|
||||
private static final Map<Character, String> COLOR_MAP = new HashMap<>();
|
||||
static {
|
||||
// Map each color to its corresponding hex code
|
||||
COLOR_MAP.put('0', "#000000"); // Black
|
||||
COLOR_MAP.put('1', "#0000AA"); // Dark Blue
|
||||
COLOR_MAP.put('2', "#00AA00"); // Dark Green
|
||||
COLOR_MAP.put('3', "#00AAAA"); // Dark Aqua
|
||||
COLOR_MAP.put('4', "#AA0000"); // Dark Red
|
||||
COLOR_MAP.put('5', "#AA00AA"); // Dark Purple
|
||||
COLOR_MAP.put('6', "#FFAA00"); // Gold
|
||||
COLOR_MAP.put('7', "#AAAAAA"); // Gray
|
||||
COLOR_MAP.put('8', "#555555"); // Dark Gray
|
||||
COLOR_MAP.put('9', "#5555FF"); // Blue
|
||||
COLOR_MAP.put('a', "#55FF55"); // Green
|
||||
COLOR_MAP.put('b', "#55FFFF"); // Aqua
|
||||
COLOR_MAP.put('c', "#FF5555"); // Red
|
||||
COLOR_MAP.put('d', "#FF55FF"); // Light Purple
|
||||
COLOR_MAP.put('e', "#FFFF55"); // Yellow
|
||||
COLOR_MAP.put('f', "#FFFFFF"); // White
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the color codes
|
||||
* from the given input.
|
||||
*
|
||||
* @param input the input to strip
|
||||
* @return the stripped input
|
||||
*/
|
||||
@NonNull
|
||||
public static String stripColor(@NonNull String input) {
|
||||
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given input into HTML format.
|
||||
* <p>
|
||||
* This will replace each color code with
|
||||
* a span tag with the respective color in
|
||||
* hex format.
|
||||
* </p>
|
||||
*
|
||||
* @param input the input to convert
|
||||
* @return the converted input
|
||||
*/
|
||||
@NonNull
|
||||
public static String toHTML(@NonNull String input) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean nextIsColor = false; // Is the next char a color code?
|
||||
|
||||
for (char character : input.toCharArray()) {
|
||||
// Found color symbol, next color is the color
|
||||
if (character == '§') {
|
||||
nextIsColor = true;
|
||||
continue;
|
||||
}
|
||||
if (nextIsColor) { // Map the current color to its hex code
|
||||
String color = COLOR_MAP.getOrDefault(Character.toLowerCase(character), "");
|
||||
builder.append("<span style=\"color:").append(color).append("\">");
|
||||
nextIsColor = false;
|
||||
continue;
|
||||
}
|
||||
builder.append(character); // Append the char...
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package cc.fascinated;
|
||||
package cc.fascinated.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
187
src/main/java/cc.fascinated/common/JavaMinecraftVersion.java
Normal file
187
src/main/java/cc.fascinated/common/JavaMinecraftVersion.java
Normal file
@ -0,0 +1,187 @@
|
||||
package cc.fascinated.common;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Protocol_version_numbers">Protocol Version Numbers</a>
|
||||
* @see <a href="https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-16">Spigot NMS (1.16+)</a>
|
||||
* @see <a href="https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-10-1-15">Spigot NMS (1.10 - 1.15)</a>
|
||||
* @see <a href="https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-legacy">Spigot NMS (1.8 - 1.9)</a>
|
||||
*/
|
||||
@RequiredArgsConstructor @Getter @ToString @Log4j2(topic = "Minecraft Version")
|
||||
public enum JavaMinecraftVersion {
|
||||
V1_20_3(765, "v1_20_R3"), // 1.20.3 & 1.20.4
|
||||
V1_20_2(764, "v1_20_R2"), // 1.20.2
|
||||
V1_20(763, "v1_20_R1"), // 1.20 & 1.20.1
|
||||
|
||||
V1_19_4(762, "v1_19_R3"), // 1.19.4
|
||||
V1_19_3(761, "v1_19_R2"), // 1.19.3
|
||||
V1_19_1(760, "v1_19_R1"), // 1.19.1 & 1.19.2
|
||||
V1_19(759, "v1_19_R1"), // 1.19
|
||||
|
||||
V1_18_2(758, "v1_18_R2"), // 1.18.2
|
||||
V1_18(757, "v1_18_R1"), // 1.18 & 1.18.1
|
||||
|
||||
V1_17_1(756, "v1_17_R1"), // 1.17.1
|
||||
V1_17(755, "v1_17_R1"), // 1.17
|
||||
|
||||
V1_16_4(754, "v1_16_R3"), // 1.16.4 & 1.16.5
|
||||
V1_16_3(753, "v1_16_R2"), // 1.16.3
|
||||
V1_16_2(751, "v1_16_R2"), // 1.16.2
|
||||
V1_16_1(736, "v1_16_R1"), // 1.16.1
|
||||
V1_16(735, "v1_16_R1"), // 1.16
|
||||
|
||||
V1_15_2(578, "v1_15_R1"), // 1.15.2
|
||||
V1_15_1(575, "v1_15_R1"), // 1.15.1
|
||||
V1_15(573, "v1_15_R1"), // 1.15
|
||||
|
||||
V1_14_4(498, "v1_14_R1"), // 1.14.4
|
||||
V1_14_3(490, "v1_14_R1"), // 1.14.3
|
||||
V1_14_2(485, "v1_14_R1"), // 1.14.2
|
||||
V1_14_1(480, "v1_14_R1"), // 1.14.1
|
||||
V1_14(477, "v1_14_R1"), // 1.14
|
||||
|
||||
V1_13_2(404, "v1_13_R2"), // 1.13.2
|
||||
V1_13_1(401, "v1_13_R2"), // 1.13.1
|
||||
V1_13(393, "v1_13_R1"), // 1.13
|
||||
|
||||
V1_12_2(340, "v1_12_R1"), // 1.12.2
|
||||
V1_12_1(338, "v1_12_R1"), // 1.12.1
|
||||
V1_12(335, "v1_12_R1"), // 1.12
|
||||
|
||||
V1_11_1(316, "v1_11_R1"), // 1.11.1 & 1.11.2
|
||||
V1_11(315, "v1_11_R1"), // 1.11
|
||||
|
||||
V1_10(210, "v1_10_R1"), // 1.10.x
|
||||
|
||||
V1_9_3(110, "v1_9_R2"), // 1.9.3 & 1.9.4
|
||||
V1_9_2(109, "v1_9_R1"), // 1.9.2
|
||||
V1_9_1(108, "v1_9_R1"), // 1.9.1
|
||||
V1_9(107, "v1_9_R1"), // 1.9
|
||||
|
||||
V1_8(47, "v1_8_R3"), // 1.8.x
|
||||
|
||||
UNKNOWN(-1, "Unknown");
|
||||
|
||||
// Game Updates
|
||||
public static final JavaMinecraftVersion TRAILS_AND_TALES = JavaMinecraftVersion.V1_20;
|
||||
public static final JavaMinecraftVersion THE_WILD_UPDATE = JavaMinecraftVersion.V1_19;
|
||||
public static final JavaMinecraftVersion CAVES_AND_CLIFFS_PT_2 = JavaMinecraftVersion.V1_18;
|
||||
public static final JavaMinecraftVersion CAVES_AND_CLIFFS_PT_1 = JavaMinecraftVersion.V1_17;
|
||||
public static final JavaMinecraftVersion NETHER_UPDATE = JavaMinecraftVersion.V1_16;
|
||||
public static final JavaMinecraftVersion BUZZY_BEES = JavaMinecraftVersion.V1_15;
|
||||
public static final JavaMinecraftVersion VILLAGE_AND_PILLAGE = JavaMinecraftVersion.V1_14;
|
||||
public static final JavaMinecraftVersion UPDATE_AQUATIC = JavaMinecraftVersion.V1_13;
|
||||
public static final JavaMinecraftVersion WORLD_OF_COLOR_UPDATE = JavaMinecraftVersion.V1_12;
|
||||
public static final JavaMinecraftVersion EXPLORATION_UPDATE = JavaMinecraftVersion.V1_11;
|
||||
public static final JavaMinecraftVersion FROSTBURN_UPDATE = JavaMinecraftVersion.V1_10;
|
||||
public static final JavaMinecraftVersion THE_COMBAT_UPDATE = JavaMinecraftVersion.V1_9;
|
||||
public static final JavaMinecraftVersion BOUNTIFUL_UPDATE = JavaMinecraftVersion.V1_8;
|
||||
|
||||
/**
|
||||
* The protocol number of this version.
|
||||
*/
|
||||
private final int protocol;
|
||||
|
||||
/**
|
||||
* The server version for this version.
|
||||
*/
|
||||
private final String nmsVersion;
|
||||
|
||||
/**
|
||||
* The cached name of this version.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Get the name of this protocol version.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
// We have a name
|
||||
if (this.name != null) {
|
||||
return this.name;
|
||||
}
|
||||
// Use the server version as the name if unknown
|
||||
if (this == UNKNOWN) {
|
||||
this.name = this.getNmsVersion();
|
||||
} else { // Parse the name
|
||||
this.name = name().substring(1);
|
||||
this.name = this.name.replace("_", ".");
|
||||
}
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this version legacy?
|
||||
*
|
||||
* @return whether this version is legacy
|
||||
*/
|
||||
public boolean isLegacy() {
|
||||
return this.isBelow(JavaMinecraftVersion.V1_16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this version is
|
||||
* above the one given.
|
||||
*
|
||||
* @param other the other version
|
||||
* @return true if above, otherwise false
|
||||
*/
|
||||
public boolean isAbove(JavaMinecraftVersion other) {
|
||||
return this.protocol > other.getProtocol();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this version is
|
||||
* or above the one given.
|
||||
*
|
||||
* @param other the other version
|
||||
* @return true if is or above, otherwise false
|
||||
*/
|
||||
public boolean isOrAbove(JavaMinecraftVersion other) {
|
||||
return this.protocol >= other.getProtocol();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this version is
|
||||
* below the one given.
|
||||
*
|
||||
* @param other the other version
|
||||
* @return true if below, otherwise false
|
||||
*/
|
||||
public boolean isBelow(JavaMinecraftVersion other) {
|
||||
return this.protocol < other.getProtocol();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this version is
|
||||
* or below the one given.
|
||||
*
|
||||
* @param other the other version
|
||||
* @return true if is or below, otherwise false
|
||||
*/
|
||||
public boolean isOrBelow(JavaMinecraftVersion other) {
|
||||
return this.protocol <= other.getProtocol();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version from the given protocol.
|
||||
*
|
||||
* @param protocol the protocol to get the version for
|
||||
* @return the version, null if none
|
||||
*/
|
||||
public static JavaMinecraftVersion byProtocol(int protocol) {
|
||||
for (JavaMinecraftVersion version : values()) {
|
||||
if (version.getProtocol() == protocol) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
30
src/main/java/cc.fascinated/common/ServerUtils.java
Normal file
30
src/main/java/cc.fascinated/common/ServerUtils.java
Normal file
@ -0,0 +1,30 @@
|
||||
package cc.fascinated.common;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class ServerUtils {
|
||||
|
||||
/**
|
||||
* Get the hostname and port from a hostname string
|
||||
*
|
||||
* @param hostname the hostname string
|
||||
* @return the hostname and port
|
||||
*/
|
||||
public static Tuple<String, Integer> getHostnameAndPort(String hostname) {
|
||||
String[] split = hostname.split(":");
|
||||
if (split.length == 1) {
|
||||
return new Tuple<>(split[0], 25565);
|
||||
}
|
||||
return new Tuple<>(split[0], Integer.parseInt(split[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address of the server.
|
||||
*
|
||||
* @return the address of the server
|
||||
*/
|
||||
public static String getAddress(String ip, int port) {
|
||||
return ip + (port == 25565 ? "" : ":" + port);
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import cc.fascinated.model.player.Skin;
|
||||
import cc.fascinated.service.PlayerService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -43,6 +44,7 @@ public class PlayerController {
|
||||
return ResponseEntity.ok()
|
||||
.cacheControl(cacheControl)
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=%s.png".formatted(player.getUsername()))
|
||||
.body(PlayerUtils.getSkinPartBytes(player.getSkin(), skinPart, size));
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
package cc.fascinated.controller;
|
||||
|
||||
import cc.fascinated.common.ServerUtils;
|
||||
import cc.fascinated.common.Tuple;
|
||||
import cc.fascinated.model.cache.CachedMinecraftServer;
|
||||
import cc.fascinated.model.server.JavaMinecraftServer;
|
||||
import cc.fascinated.model.server.MinecraftServer;
|
||||
import cc.fascinated.service.ServerService;
|
||||
import cc.fascinated.service.pinger.impl.JavaMinecraftServerPinger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -19,14 +23,19 @@ public class ServerController {
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/{platform}/{hostnameAndPort}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public CachedMinecraftServer getServer(@PathVariable String platform, @PathVariable String hostnameAndPort) {
|
||||
String[] split = hostnameAndPort.split(":");
|
||||
String hostname = split[0];
|
||||
int port = 25565;
|
||||
if (split.length == 2) {
|
||||
try {
|
||||
port = Integer.parseInt(split[1]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
return serverService.getServer(platform, hostname, port);
|
||||
Tuple<String, Integer> host = ServerUtils.getHostnameAndPort(hostnameAndPort);
|
||||
return serverService.getServer(platform, host.getLeft(), host.getRight());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/icon/{hostnameAndPort}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<?> getServerIcon(@PathVariable String hostnameAndPort) {
|
||||
Tuple<String, Integer> host = ServerUtils.getHostnameAndPort(hostnameAndPort);
|
||||
String hostname = host.getLeft();
|
||||
int port = host.getRight();
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=%s.png".formatted(ServerUtils.getAddress(hostname, port)))
|
||||
.body(serverService.getServerFavicon(hostname, port));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cc.fascinated.model.mojang;
|
||||
|
||||
import cc.fascinated.model.server.JavaMinecraftServer;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
@ -9,5 +10,24 @@ import lombok.ToString;
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public final class JavaServerStatusToken {
|
||||
|
||||
/**
|
||||
* The version of the server.
|
||||
*/
|
||||
private final JavaMinecraftServer.Version version;
|
||||
|
||||
/**
|
||||
* The players on the server.
|
||||
*/
|
||||
private final JavaMinecraftServer.Players players;
|
||||
|
||||
/**
|
||||
* The motd of the server.
|
||||
*/
|
||||
private final String description;
|
||||
}
|
||||
|
||||
/**
|
||||
* The favicon of the server.
|
||||
*/
|
||||
private final String favicon;
|
||||
}
|
||||
|
@ -1,10 +1,128 @@
|
||||
package cc.fascinated.model.server;
|
||||
|
||||
import cc.fascinated.common.JavaMinecraftVersion;
|
||||
import cc.fascinated.config.Config;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Setter @Getter
|
||||
public final class JavaMinecraftServer extends MinecraftServer {
|
||||
public JavaMinecraftServer(String hostname, String ip, int port, String motd) {
|
||||
|
||||
/**
|
||||
* The version of the server.
|
||||
*/
|
||||
@NonNull private final Version version;
|
||||
|
||||
/**
|
||||
* The players on the server.
|
||||
*/
|
||||
private Players players;
|
||||
|
||||
/**
|
||||
* The favicon of the server.
|
||||
*/
|
||||
private Favicon favicon;
|
||||
|
||||
public JavaMinecraftServer(String hostname, String ip, int port, MOTD motd, @NonNull Version version, Players players, Favicon favicon) {
|
||||
super(hostname, ip, port, motd);
|
||||
this.version = version;
|
||||
this.players = players;
|
||||
this.favicon = favicon;
|
||||
}
|
||||
|
||||
@AllArgsConstructor @Getter
|
||||
public static class Version {
|
||||
/**
|
||||
* The version name of the server.
|
||||
*/
|
||||
@NonNull
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* The server platform.
|
||||
*/
|
||||
private String platform;
|
||||
|
||||
/**
|
||||
* The protocol version.
|
||||
*/
|
||||
private final int protocol;
|
||||
|
||||
/**
|
||||
* The name of the protocol, null if not found.
|
||||
*/
|
||||
private final String protocolName;
|
||||
|
||||
/**
|
||||
* Create a more detailed
|
||||
* copy of this object.
|
||||
*
|
||||
* @return the detailed copy
|
||||
*/
|
||||
@NonNull
|
||||
public Version detailedCopy() {
|
||||
String platform = null;
|
||||
if (name.contains(" ")) { // Parse the server platform
|
||||
String[] split = name.split(" ");
|
||||
if (split.length == 2) {
|
||||
platform = split[0];
|
||||
}
|
||||
}
|
||||
JavaMinecraftVersion minecraftVersion = JavaMinecraftVersion.byProtocol(protocol);
|
||||
return new Version(name, platform, protocol, minecraftVersion == null ? null : minecraftVersion.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Getter @AllArgsConstructor
|
||||
public static class Players {
|
||||
|
||||
/**
|
||||
* The maximum amount of players the server can hold.
|
||||
*/
|
||||
private final int max;
|
||||
|
||||
/**
|
||||
* The amount of players currently online.
|
||||
*/
|
||||
private final int online;
|
||||
|
||||
/**
|
||||
* The sample of players currently online.
|
||||
*/
|
||||
private final String[] sample;
|
||||
}
|
||||
|
||||
@Getter @AllArgsConstructor
|
||||
public static class Favicon {
|
||||
|
||||
/**
|
||||
* The raw base64 of the favicon.
|
||||
*/
|
||||
private final String base64;
|
||||
|
||||
/**
|
||||
* The url to the favicon.
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* Create a new favicon for a server.
|
||||
*
|
||||
* @param base64 the base64 of the favicon
|
||||
* @param address the address of the server
|
||||
* @return the new favicon
|
||||
*/
|
||||
public static Favicon create(String base64, @NonNull String address) {
|
||||
if (base64 == null) { // The server doesn't have a favicon
|
||||
return null;
|
||||
}
|
||||
return new Favicon(base64, Config.INSTANCE.getWebPublicUrl() + "/server/icon/%s".formatted(address));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package cc.fascinated.model.server;
|
||||
|
||||
import cc.fascinated.common.ColorUtils;
|
||||
import cc.fascinated.service.pinger.MinecraftServerPinger;
|
||||
import cc.fascinated.service.pinger.impl.JavaMinecraftServerPinger;
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
@ -7,6 +8,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@ -15,7 +18,7 @@ public class MinecraftServer {
|
||||
private final String hostname;
|
||||
private final String ip;
|
||||
private final int port;
|
||||
private final String motd;
|
||||
private final MOTD motd;
|
||||
|
||||
/**
|
||||
* A platform a Minecraft
|
||||
@ -39,4 +42,39 @@ public class MinecraftServer {
|
||||
*/
|
||||
private final int defaultPort;
|
||||
}
|
||||
|
||||
@AllArgsConstructor @Getter
|
||||
public static class MOTD {
|
||||
|
||||
/**
|
||||
* The raw motd lines
|
||||
*/
|
||||
private final String[] raw;
|
||||
|
||||
/**
|
||||
* The clean motd lines
|
||||
*/
|
||||
private final String[] clean;
|
||||
|
||||
/**
|
||||
* The html motd lines
|
||||
*/
|
||||
private final String[] html;
|
||||
|
||||
/**
|
||||
* Create a new MOTD from a raw string.
|
||||
*
|
||||
* @param raw the raw motd string
|
||||
* @return the new motd
|
||||
*/
|
||||
@NonNull
|
||||
public static MOTD create(@NonNull String raw) {
|
||||
String[] rawLines = raw.split("\n"); // The raw lines
|
||||
return new MOTD(
|
||||
rawLines,
|
||||
Arrays.stream(rawLines).map(ColorUtils::stripColor).toArray(String[]::new),
|
||||
Arrays.stream(rawLines).map(ColorUtils::toHTML).toArray(String[]::new)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +1,24 @@
|
||||
package cc.fascinated.service;
|
||||
|
||||
import cc.fascinated.EnumUtils;
|
||||
import cc.fascinated.common.EnumUtils;
|
||||
import cc.fascinated.common.DNSUtils;
|
||||
import cc.fascinated.common.WebRequest;
|
||||
import cc.fascinated.exception.impl.BadRequestException;
|
||||
import cc.fascinated.exception.impl.ResourceNotFoundException;
|
||||
import cc.fascinated.model.cache.CachedMinecraftServer;
|
||||
import cc.fascinated.model.mojang.MojangProfile;
|
||||
import cc.fascinated.model.mojang.MojangUsernameToUuid;
|
||||
import cc.fascinated.model.server.JavaMinecraftServer;
|
||||
import cc.fascinated.model.server.MinecraftServer;
|
||||
import cc.fascinated.repository.MinecraftServerCacheRepository;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Base64;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service @Log4j2
|
||||
public class ServerService {
|
||||
private static final String DEFAULT_SERVER_ICON = "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";
|
||||
|
||||
private final MinecraftServerCacheRepository serverCacheRepository;
|
||||
|
||||
@ -62,4 +62,28 @@ public class ServerService {
|
||||
server.setCached(-1); // Indicate that the server is not cached
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server favicon.
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param port the port of the server
|
||||
* @return the server favicon, null if not found
|
||||
*/
|
||||
public byte[] getServerFavicon(String hostname, int port) {
|
||||
String icon = null; // The server base64 icon
|
||||
try {
|
||||
JavaMinecraftServer.Favicon favicon = ((JavaMinecraftServer) getServer(MinecraftServer.Platform.JAVA.name(), hostname, port).getValue()).getFavicon();
|
||||
if (favicon != null) { // Use the server's favicon
|
||||
icon = favicon.getBase64();
|
||||
icon = icon.substring(icon.indexOf(",") + 1); // Remove the data type from the server icon
|
||||
}
|
||||
} catch (BadRequestException | ResourceNotFoundException ignored) {
|
||||
// Safely ignore these, we will use the default server icon
|
||||
}
|
||||
if (icon == null) { // Use the default server icon
|
||||
icon = DEFAULT_SERVER_ICON;
|
||||
}
|
||||
return Base64.getDecoder().decode(icon); // Return the decoded favicon
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ package cc.fascinated.service.pinger.impl;
|
||||
|
||||
import cc.fascinated.Main;
|
||||
import cc.fascinated.common.DNSUtils;
|
||||
import cc.fascinated.common.ServerUtils;
|
||||
import cc.fascinated.common.packet.impl.java.JavaPacketHandshakingInSetProtocol;
|
||||
import cc.fascinated.common.packet.impl.java.JavaPacketStatusInStart;
|
||||
import cc.fascinated.exception.impl.BadRequestException;
|
||||
import cc.fascinated.exception.impl.ResourceNotFoundException;
|
||||
import cc.fascinated.model.mojang.JavaServerStatusToken;
|
||||
import cc.fascinated.model.server.JavaMinecraftServer;
|
||||
import cc.fascinated.model.server.MinecraftServer;
|
||||
import cc.fascinated.service.pinger.MinecraftServerPinger;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
@ -48,8 +50,17 @@ public final class JavaMinecraftServerPinger implements MinecraftServerPinger<Ja
|
||||
// Send the status request to the server, and await back the response
|
||||
JavaPacketStatusInStart packetStatusInStart = new JavaPacketStatusInStart();
|
||||
packetStatusInStart.process(inputStream, outputStream);
|
||||
System.out.println(packetStatusInStart.getResponse());
|
||||
JavaServerStatusToken token = Main.GSON.fromJson(packetStatusInStart.getResponse(), JavaServerStatusToken.class);
|
||||
return new JavaMinecraftServer(hostname, ip, port, token.getDescription());
|
||||
return new JavaMinecraftServer(
|
||||
hostname,
|
||||
ip,
|
||||
port,
|
||||
MinecraftServer.MOTD.create(token.getDescription()),
|
||||
token.getVersion().detailedCopy(),
|
||||
token.getPlayers(),
|
||||
JavaMinecraftServer.Favicon.create(token.getFavicon(), ServerUtils.getAddress(hostname, port))
|
||||
);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (ex instanceof UnknownHostException) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
<div class="flex flex-col mt-3">
|
||||
<p>Player Data: <a class="text-blue-600" target=”_blank” th:href="${player_example_url}" th:text="${player_example_url}">???</a></p>
|
||||
<p>Java Server: <a class="text-blue-600" target=”_blank” th:href="${java_server_example_url}" th:text="${java_server_example_url}">???</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user