make the skin renderer less bad (thanks bray)
Some checks failed
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Failing after 17s
Some checks failed
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Failing after 17s
This commit is contained in:
34
src/main/java/cc/fascinated/model/cache/CachedMinecraftServer.java
vendored
Normal file
34
src/main/java/cc/fascinated/model/cache/CachedMinecraftServer.java
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package cc.fascinated.model.cache;
|
||||
|
||||
import cc.fascinated.model.server.MinecraftServer;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Setter @Getter @ToString
|
||||
@RedisHash(value = "server", timeToLive = 60L) // 1 minute (in seconds)
|
||||
public final class CachedMinecraftServer implements Serializable {
|
||||
/**
|
||||
* The id of this cached server.
|
||||
*/
|
||||
@Id @NonNull @JsonIgnore
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* The cached server.
|
||||
*/
|
||||
@NonNull
|
||||
private final MinecraftServer server;
|
||||
|
||||
/**
|
||||
* The unix timestamp of when this
|
||||
* server was cached, -1 if not cached.
|
||||
*/
|
||||
private long cached;
|
||||
}
|
34
src/main/java/cc/fascinated/model/cache/CachedPlayer.java
vendored
Normal file
34
src/main/java/cc/fascinated/model/cache/CachedPlayer.java
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package cc.fascinated.model.cache;
|
||||
|
||||
import cc.fascinated.model.mojang.MojangProfile;
|
||||
import cc.fascinated.model.player.Cape;
|
||||
import cc.fascinated.model.player.Player;
|
||||
import cc.fascinated.model.skin.Skin;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A cacheable {@link Player}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Setter @Getter
|
||||
@ToString(callSuper = true)
|
||||
@RedisHash(value = "player", timeToLive = 60L * 60L) // 1 hour (in seconds)
|
||||
public final class CachedPlayer extends Player implements Serializable {
|
||||
/**
|
||||
* The unix timestamp of when this
|
||||
* player was cached, -1 if not cached.
|
||||
*/
|
||||
private long cached;
|
||||
|
||||
public CachedPlayer(UUID uniqueId, String trimmedUniqueId, String username, Skin skin, Cape cape, MojangProfile.ProfileProperty[] rawProperties, long cached) {
|
||||
super(uniqueId, trimmedUniqueId, username, skin, cape, rawProperties);
|
||||
this.cached = cached;
|
||||
}
|
||||
}
|
27
src/main/java/cc/fascinated/model/cache/CachedPlayerName.java
vendored
Normal file
27
src/main/java/cc/fascinated/model/cache/CachedPlayerName.java
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package cc.fascinated.model.cache;
|
||||
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@ToString
|
||||
@RedisHash(value = "playerName", timeToLive = 60L * 60L) // 1 hour (in seconds)
|
||||
public final class CachedPlayerName {
|
||||
/**
|
||||
* The username of the player.
|
||||
*/
|
||||
@Id @NonNull private String username;
|
||||
|
||||
/**
|
||||
* The unique id of the player.
|
||||
*/
|
||||
@NonNull private UUID uniqueId;
|
||||
}
|
25
src/main/java/cc/fascinated/model/cache/CachedPlayerSkinPart.java
vendored
Normal file
25
src/main/java/cc/fascinated/model/cache/CachedPlayerSkinPart.java
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package cc.fascinated.model.cache;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@RedisHash(value = "player", timeToLive = 60L * 60L) // 1 hour (in seconds)
|
||||
public class CachedPlayerSkinPart {
|
||||
|
||||
/**
|
||||
* The ID of the skin part
|
||||
*/
|
||||
@Id @NonNull private String id;
|
||||
|
||||
/**
|
||||
* The skin part bytes
|
||||
*/
|
||||
private byte[] bytes;
|
||||
}
|
29
src/main/java/cc/fascinated/model/dns/DNSRecord.java
Normal file
29
src/main/java/cc/fascinated/model/dns/DNSRecord.java
Normal file
@ -0,0 +1,29 @@
|
||||
package cc.fascinated.model.dns;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter @Getter
|
||||
@NoArgsConstructor @AllArgsConstructor
|
||||
public abstract class DNSRecord {
|
||||
/**
|
||||
* The type of this record.
|
||||
*/
|
||||
@NonNull
|
||||
private Type type;
|
||||
|
||||
/**
|
||||
* The TTL (Time To Live) of this record.
|
||||
*/
|
||||
private long ttl;
|
||||
|
||||
/**
|
||||
* Types of a record.
|
||||
*/
|
||||
public enum Type {
|
||||
A, SRV
|
||||
}
|
||||
}
|
24
src/main/java/cc/fascinated/model/dns/impl/ARecord.java
Normal file
24
src/main/java/cc/fascinated/model/dns/impl/ARecord.java
Normal file
@ -0,0 +1,24 @@
|
||||
package cc.fascinated.model.dns.impl;
|
||||
|
||||
import cc.fascinated.model.dns.DNSRecord;
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
@Setter @Getter
|
||||
@NoArgsConstructor
|
||||
public final class ARecord extends DNSRecord {
|
||||
/**
|
||||
* The address of this record, null if unresolved.
|
||||
*/
|
||||
private String address;
|
||||
|
||||
public ARecord(@NonNull org.xbill.DNS.ARecord bootstrap) {
|
||||
super(Type.A, bootstrap.getTTL());
|
||||
InetAddress address = bootstrap.getAddress();
|
||||
this.address = address == null ? null : address.getHostAddress();
|
||||
}
|
||||
}
|
53
src/main/java/cc/fascinated/model/dns/impl/SRVRecord.java
Normal file
53
src/main/java/cc/fascinated/model/dns/impl/SRVRecord.java
Normal file
@ -0,0 +1,53 @@
|
||||
package cc.fascinated.model.dns.impl;
|
||||
|
||||
import cc.fascinated.model.dns.DNSRecord;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
@Setter @Getter
|
||||
@NoArgsConstructor
|
||||
public final class SRVRecord extends DNSRecord {
|
||||
/**
|
||||
* The priority of this record.
|
||||
*/
|
||||
private int priority;
|
||||
|
||||
/**
|
||||
* The weight of this record.
|
||||
*/
|
||||
private int weight;
|
||||
|
||||
/**
|
||||
* The port of this record.
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* The target of this record.
|
||||
*/
|
||||
@NonNull private String target;
|
||||
|
||||
public SRVRecord(@NonNull org.xbill.DNS.SRVRecord bootstrap) {
|
||||
super(Type.SRV, bootstrap.getTTL());
|
||||
priority = bootstrap.getPriority();
|
||||
weight = bootstrap.getWeight();
|
||||
port = bootstrap.getPort();
|
||||
target = bootstrap.getTarget().toString().replaceFirst("\\.$", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a socket address from
|
||||
* the target and port.
|
||||
*
|
||||
* @return the socket address
|
||||
*/
|
||||
@NonNull @JsonIgnore
|
||||
public InetSocketAddress getSocketAddress() {
|
||||
return new InetSocketAddress(target, port);
|
||||
}
|
||||
}
|
108
src/main/java/cc/fascinated/model/mojang/MojangProfile.java
Normal file
108
src/main/java/cc/fascinated/model/mojang/MojangProfile.java
Normal file
@ -0,0 +1,108 @@
|
||||
package cc.fascinated.model.mojang;
|
||||
|
||||
import cc.fascinated.Main;
|
||||
import cc.fascinated.common.Tuple;
|
||||
import cc.fascinated.common.UUIDUtils;
|
||||
import cc.fascinated.model.player.Cape;
|
||||
import cc.fascinated.model.skin.Skin;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
@Getter @NoArgsConstructor @AllArgsConstructor
|
||||
public class MojangProfile {
|
||||
|
||||
/**
|
||||
* The UUID of the player.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* The name of the player.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The properties of the player.
|
||||
*/
|
||||
private ProfileProperty[] properties = new ProfileProperty[0];
|
||||
|
||||
/**
|
||||
* Get the skin and cape of the player.
|
||||
*
|
||||
* @return the skin and cape of the player
|
||||
*/
|
||||
public Tuple<Skin, Cape> getSkinAndCape() {
|
||||
ProfileProperty textureProperty = getProfileProperty("textures");
|
||||
if (textureProperty == null) {
|
||||
return null;
|
||||
}
|
||||
JsonObject texturesJson = textureProperty.getDecodedValue().getAsJsonObject("textures"); // Parse the decoded JSON and get the texture object
|
||||
return new Tuple<>(Skin.fromJson(texturesJson.getAsJsonObject("SKIN")).populatePartUrls(this.getFormattedUuid()),
|
||||
Cape.fromJson(texturesJson.getAsJsonObject("CAPE")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the formatted UUID of the player.
|
||||
*
|
||||
* @return the formatted UUID
|
||||
*/
|
||||
public String getFormattedUuid() {
|
||||
return id.length() == 32 ? UUIDUtils.addDashes(id).toString() : id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a profile property for the player
|
||||
*
|
||||
* @return the profile property
|
||||
*/
|
||||
public ProfileProperty getProfileProperty(String name) {
|
||||
for (ProfileProperty property : properties) {
|
||||
if (property.getName().equals(name)) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Getter @NoArgsConstructor
|
||||
public static class ProfileProperty {
|
||||
/**
|
||||
* The name of the property.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The base64 value of the property.
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* The signature of the property.
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* Decodes the value for this property.
|
||||
*
|
||||
* @return the decoded value
|
||||
*/
|
||||
@JsonIgnore
|
||||
public JsonObject getDecodedValue() {
|
||||
return Main.GSON.fromJson(new String(Base64.getDecoder().decode(this.value)), JsonObject.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the property is signed.
|
||||
*
|
||||
* @return true if the property is signed, false otherwise
|
||||
*/
|
||||
public boolean isSigned() {
|
||||
return signature != null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cc.fascinated.model.mojang;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter @NoArgsConstructor
|
||||
public class MojangUsernameToUuid {
|
||||
|
||||
/**
|
||||
* The UUID of the player.
|
||||
*/
|
||||
@JsonProperty("id")
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* The name of the player.
|
||||
*/
|
||||
@JsonProperty("name")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* Check if the profile is valid.
|
||||
*
|
||||
* @return if the profile is valid
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return uuid != null && username != null;
|
||||
}
|
||||
}
|
27
src/main/java/cc/fascinated/model/player/Cape.java
Normal file
27
src/main/java/cc/fascinated/model/player/Cape.java
Normal file
@ -0,0 +1,27 @@
|
||||
package cc.fascinated.model.player;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter @AllArgsConstructor
|
||||
public class Cape {
|
||||
|
||||
/**
|
||||
* The URL of the cape
|
||||
*/
|
||||
private final String url;
|
||||
|
||||
/**
|
||||
* Gets the cape from a {@link JsonObject}.
|
||||
*
|
||||
* @param json the JSON object
|
||||
* @return the cape
|
||||
*/
|
||||
public static Cape fromJson(JsonObject json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return new Cape(json.get("url").getAsString());
|
||||
}
|
||||
}
|
61
src/main/java/cc/fascinated/model/player/Player.java
Normal file
61
src/main/java/cc/fascinated/model/player/Player.java
Normal file
@ -0,0 +1,61 @@
|
||||
package cc.fascinated.model.player;
|
||||
|
||||
import cc.fascinated.common.Tuple;
|
||||
import cc.fascinated.common.UUIDUtils;
|
||||
import cc.fascinated.model.mojang.MojangProfile;
|
||||
import cc.fascinated.model.skin.Skin;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter @AllArgsConstructor
|
||||
public class Player {
|
||||
|
||||
/**
|
||||
* The UUID of the player
|
||||
*/
|
||||
@Id private final UUID uniqueId;
|
||||
|
||||
/**
|
||||
* The trimmed UUID of the player
|
||||
*/
|
||||
private final String trimmedUniqueId;
|
||||
|
||||
/**
|
||||
* The username of the player
|
||||
*/
|
||||
private final String username;
|
||||
|
||||
/**
|
||||
* The skin of the player, null if the
|
||||
* player does not have a skin
|
||||
*/
|
||||
private Skin skin;
|
||||
|
||||
/**
|
||||
* The cape of the player, null if the
|
||||
* player does not have a cape
|
||||
*/
|
||||
private Cape cape;
|
||||
|
||||
/**
|
||||
* The raw properties of the player
|
||||
*/
|
||||
private MojangProfile.ProfileProperty[] rawProperties;
|
||||
|
||||
public Player(MojangProfile profile) {
|
||||
this.uniqueId = UUIDUtils.addDashes(profile.getId());
|
||||
this.trimmedUniqueId = UUIDUtils.removeDashes(this.uniqueId);
|
||||
this.username = profile.getName();
|
||||
this.rawProperties = profile.getProperties();
|
||||
|
||||
// Get the skin and cape
|
||||
Tuple<Skin, Cape> skinAndCape = profile.getSkinAndCape();
|
||||
if (skinAndCape != null) {
|
||||
this.skin = skinAndCape.getLeft();
|
||||
this.cape = skinAndCape.getRight();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cc.fascinated.model.response;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class ErrorResponse {
|
||||
/**
|
||||
* The status code of this error.
|
||||
*/
|
||||
@NonNull
|
||||
private final HttpStatus status;
|
||||
|
||||
/**
|
||||
* The HTTP code of this error.
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* The message of this error.
|
||||
*/
|
||||
@NonNull private final String message;
|
||||
|
||||
/**
|
||||
* The timestamp this error occurred.
|
||||
*/
|
||||
@NonNull private final Date timestamp;
|
||||
|
||||
public ErrorResponse(@NonNull HttpStatus status, @NonNull String message) {
|
||||
this.status = status;
|
||||
code = status.value();
|
||||
this.message = message;
|
||||
timestamp = new Date();
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package cc.fascinated.model.server;
|
||||
|
||||
import cc.fascinated.model.dns.DNSRecord;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* A Bedrock edition {@link MinecraftServer}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Getter @ToString(callSuper = true) @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true)
|
||||
public final class BedrockMinecraftServer extends MinecraftServer {
|
||||
/**
|
||||
* The unique ID of this server.
|
||||
*/
|
||||
@EqualsAndHashCode.Include @NonNull private final String id;
|
||||
|
||||
/**
|
||||
* The edition of this server.
|
||||
*/
|
||||
@NonNull private final Edition edition;
|
||||
|
||||
/**
|
||||
* The version information of this server.
|
||||
*/
|
||||
@NonNull private final Version version;
|
||||
|
||||
/**
|
||||
* The gamemode of this server.
|
||||
*/
|
||||
@NonNull private final GameMode gamemode;
|
||||
|
||||
private BedrockMinecraftServer(@NonNull String id, @NonNull String hostname, String ip, int port, @NonNull DNSRecord[] records,
|
||||
@NonNull Edition edition, @NonNull Version version, @NonNull Players players, @NonNull MOTD motd,
|
||||
@NonNull GameMode gamemode) {
|
||||
super(hostname, ip, port, records, motd, players);
|
||||
this.id = id;
|
||||
this.edition = edition;
|
||||
this.version = version;
|
||||
this.gamemode = gamemode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Bedrock Minecraft server.
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param ip the IP address of the server
|
||||
* @param port the port of the server
|
||||
* @param token the status token
|
||||
* @return the Bedrock Minecraft server
|
||||
*/
|
||||
@NonNull
|
||||
public static BedrockMinecraftServer create(@NonNull String hostname, String ip, int port, DNSRecord[] records, @NonNull String token) {
|
||||
String[] split = token.split(";"); // Split the token
|
||||
Edition edition = Edition.valueOf(split[0]);
|
||||
Version version = new Version(Integer.parseInt(split[2]), split[3]);
|
||||
Players players = new Players(Integer.parseInt(split[4]), Integer.parseInt(split[5]), null);
|
||||
MOTD motd = MOTD.create(split[1] + "\n" + split[7]);
|
||||
GameMode gameMode = new GameMode(split[8], Integer.parseInt(split[9]));
|
||||
return new BedrockMinecraftServer(
|
||||
split[6],
|
||||
hostname,
|
||||
ip,
|
||||
port,
|
||||
records,
|
||||
edition,
|
||||
version,
|
||||
players,
|
||||
motd,
|
||||
gameMode
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The edition of a Bedrock server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public enum Edition {
|
||||
/**
|
||||
* Minecraft: Pocket Edition.
|
||||
*/
|
||||
MCPE,
|
||||
|
||||
/**
|
||||
* Minecraft: Education Edition.
|
||||
*/
|
||||
MCEE
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class Version {
|
||||
/**
|
||||
* The protocol version of the server.
|
||||
*/
|
||||
private final int protocol;
|
||||
|
||||
/**
|
||||
* The version name of the server.
|
||||
*/
|
||||
@NonNull private final String name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The gamemode of a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class GameMode {
|
||||
/**
|
||||
* The name of this gamemode.
|
||||
*/
|
||||
@NonNull private final String name;
|
||||
|
||||
/**
|
||||
* The numeric of this gamemode.
|
||||
*/
|
||||
private final int numericId;
|
||||
}
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
package cc.fascinated.model.server;
|
||||
|
||||
import cc.fascinated.Main;
|
||||
import cc.fascinated.common.JavaMinecraftVersion;
|
||||
import cc.fascinated.common.ServerUtils;
|
||||
import cc.fascinated.config.Config;
|
||||
import cc.fascinated.model.dns.DNSRecord;
|
||||
import cc.fascinated.model.token.JavaServerStatusToken;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.*;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Setter @Getter
|
||||
public final class JavaMinecraftServer extends MinecraftServer {
|
||||
|
||||
/**
|
||||
* The version of the server.
|
||||
*/
|
||||
@NonNull private final Version version;
|
||||
|
||||
/**
|
||||
* The favicon of the server.
|
||||
*/
|
||||
private Favicon favicon;
|
||||
|
||||
/**
|
||||
* The mods running on this server.
|
||||
*/
|
||||
private ForgeModInfo modInfo;
|
||||
|
||||
/**
|
||||
* The mods running on this server.
|
||||
* <p>
|
||||
* This is only used for servers
|
||||
* running 1.13 and above.
|
||||
* </p>
|
||||
*/
|
||||
private ForgeData forgeData;
|
||||
|
||||
/**
|
||||
* Whether the server prevents chat reports.
|
||||
*/
|
||||
private boolean preventsChatReports;
|
||||
|
||||
/**
|
||||
* Whether the server enforces secure chat.
|
||||
*/
|
||||
private boolean enforcesSecureChat;
|
||||
|
||||
/**
|
||||
* Whether the server has previews chat enabled.
|
||||
* <p>
|
||||
* Chat Preview sends chat messages to the server as they are typed, even before they're sent.
|
||||
* <a href="https://www.minecraft.net/es-mx/article/minecraft-snapshot-22w19a">More information</a>
|
||||
* </p>
|
||||
*/
|
||||
private boolean previewsChat;
|
||||
|
||||
/**
|
||||
* The mojang blocked status for the server.
|
||||
*/
|
||||
private boolean mojangBlocked;
|
||||
|
||||
public JavaMinecraftServer(String hostname, String ip, int port, MOTD motd, Players players, DNSRecord[] records,
|
||||
@NonNull Version version, Favicon favicon, ForgeModInfo modInfo, ForgeData forgeData,
|
||||
boolean preventsChatReports, boolean enforcesSecureChat, boolean previewsChat) {
|
||||
super(hostname, ip, port, records, motd, players);
|
||||
this.version = version;
|
||||
this.favicon = favicon;
|
||||
this.modInfo = modInfo;
|
||||
this.forgeData = forgeData;
|
||||
this.preventsChatReports = preventsChatReports;
|
||||
this.enforcesSecureChat = enforcesSecureChat;
|
||||
this.previewsChat = previewsChat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Java Minecraft server.
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param ip the IP address of the server
|
||||
* @param port the port of the server
|
||||
* @param token the status token
|
||||
* @return the Java Minecraft server
|
||||
*/
|
||||
@NonNull
|
||||
public static JavaMinecraftServer create(@NonNull String hostname, String ip, int port, DNSRecord[] records, @NonNull JavaServerStatusToken token) {
|
||||
String motdString = token.getDescription() instanceof String ? (String) token.getDescription() : null;
|
||||
if (motdString == null) { // Not a string motd, convert from Json
|
||||
motdString = new TextComponent(ComponentSerializer.parse(Main.GSON.toJson(token.getDescription()))).toLegacyText();
|
||||
}
|
||||
return new JavaMinecraftServer(
|
||||
hostname,
|
||||
ip,
|
||||
port,
|
||||
MinecraftServer.MOTD.create(motdString),
|
||||
token.getPlayers(),
|
||||
records,
|
||||
token.getVersion().detailedCopy(),
|
||||
JavaMinecraftServer.Favicon.create(token.getFavicon(), ServerUtils.getAddress(hostname, port)),
|
||||
token.getModInfo(),
|
||||
token.getForgeData(),
|
||||
token.isPreventsChatReports(),
|
||||
token.isEnforcesSecureChat(),
|
||||
token.isPreviewsChat()
|
||||
);
|
||||
}
|
||||
|
||||
@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 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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forge mod information for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class ForgeModInfo {
|
||||
/**
|
||||
* The type of modded server this is.
|
||||
*/
|
||||
@NonNull private final String type;
|
||||
|
||||
/**
|
||||
* The list of mods on this server, null or empty if none.
|
||||
*/
|
||||
private final ForgeMod[] modList;
|
||||
|
||||
/**
|
||||
* A forge mod for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
private static class ForgeMod {
|
||||
/**
|
||||
* The id of this mod.
|
||||
*/
|
||||
@NonNull @SerializedName("modid") private final String name;
|
||||
|
||||
/**
|
||||
* The version of this mod.
|
||||
*/
|
||||
private final String version;
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor @Getter
|
||||
public static class ForgeData {
|
||||
|
||||
/**
|
||||
* The list of mod channels on this server, null or empty if none.
|
||||
*/
|
||||
private final Channel[] channels;
|
||||
|
||||
/**
|
||||
* The list of mods on this server, null or empty if none.
|
||||
*/
|
||||
private final Mod[] mods;
|
||||
|
||||
/**
|
||||
* Whether the mod list is truncated.
|
||||
*/
|
||||
private final boolean truncated;
|
||||
|
||||
/**
|
||||
* The version of the FML network.
|
||||
*/
|
||||
private final int fmlNetworkVersion;
|
||||
|
||||
@AllArgsConstructor @Getter
|
||||
public static class Channel {
|
||||
/**
|
||||
* The id of this mod channel.
|
||||
*/
|
||||
@NonNull @SerializedName("res") private final String name;
|
||||
|
||||
/**
|
||||
* The version of this mod channel.
|
||||
*/
|
||||
private final String version;
|
||||
|
||||
/**
|
||||
* Whether this mod channel is required to join.
|
||||
*/
|
||||
private boolean required;
|
||||
}
|
||||
|
||||
@AllArgsConstructor @Getter
|
||||
public static class Mod {
|
||||
/**
|
||||
* The id of this mod.
|
||||
*/
|
||||
@NonNull @SerializedName("modId") private final String name;
|
||||
|
||||
/**
|
||||
* The version of this mod.
|
||||
*/
|
||||
@SerializedName("modmarker") private final String version;
|
||||
}
|
||||
}
|
||||
}
|
153
src/main/java/cc/fascinated/model/server/MinecraftServer.java
Normal file
153
src/main/java/cc/fascinated/model/server/MinecraftServer.java
Normal file
@ -0,0 +1,153 @@
|
||||
package cc.fascinated.model.server;
|
||||
|
||||
import cc.fascinated.common.ColorUtils;
|
||||
import cc.fascinated.model.dns.DNSRecord;
|
||||
import cc.fascinated.service.pinger.MinecraftServerPinger;
|
||||
import cc.fascinated.service.pinger.impl.BedrockMinecraftServerPinger;
|
||||
import cc.fascinated.service.pinger.impl.JavaMinecraftServerPinger;
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter @Setter
|
||||
public class MinecraftServer {
|
||||
|
||||
/**
|
||||
* The hostname of the server.
|
||||
*/
|
||||
private final String hostname;
|
||||
|
||||
/**
|
||||
* The IP address of the server.
|
||||
*/
|
||||
private final String ip;
|
||||
|
||||
/**
|
||||
* The port of the server.
|
||||
*/
|
||||
private final int port;
|
||||
|
||||
/**
|
||||
* The DNS records for the server.
|
||||
*/
|
||||
private final DNSRecord[] records;
|
||||
|
||||
/**
|
||||
* The motd for the server.
|
||||
*/
|
||||
private final MOTD motd;
|
||||
|
||||
/**
|
||||
* The players on the server.
|
||||
*/
|
||||
private final Players players;
|
||||
|
||||
/**
|
||||
* A platform a Minecraft
|
||||
* server can operate on.
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public enum Platform {
|
||||
/**
|
||||
* The Java edition of Minecraft.
|
||||
*/
|
||||
JAVA(new JavaMinecraftServerPinger(), 25565),
|
||||
|
||||
/**
|
||||
* The Bedrock edition of Minecraft.
|
||||
*/
|
||||
BEDROCK(new BedrockMinecraftServerPinger(), 19132);
|
||||
|
||||
/**
|
||||
* The server pinger for this platform.
|
||||
*/
|
||||
@NonNull
|
||||
private final MinecraftServerPinger<?> pinger;
|
||||
|
||||
/**
|
||||
* The default server port for this platform.
|
||||
*/
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player count data for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public static class Players {
|
||||
/**
|
||||
* The online players on this server.
|
||||
*/
|
||||
private final int online;
|
||||
|
||||
/**
|
||||
* The maximum allowed players on this server.
|
||||
*/
|
||||
private final int max;
|
||||
|
||||
/**
|
||||
* A sample of players on this server, null or empty if no sample.
|
||||
*/
|
||||
private final Sample[] sample;
|
||||
|
||||
/**
|
||||
* A sample player.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class Sample {
|
||||
/**
|
||||
* The unique id of this player.
|
||||
*/
|
||||
@NonNull private final UUID id;
|
||||
|
||||
/**
|
||||
* The name of this player.
|
||||
*/
|
||||
@NonNull private final String name;
|
||||
}
|
||||
}
|
||||
}
|
199
src/main/java/cc/fascinated/model/skin/ISkinPart.java
Normal file
199
src/main/java/cc/fascinated/model/skin/ISkinPart.java
Normal file
@ -0,0 +1,199 @@
|
||||
package cc.fascinated.model.skin;
|
||||
|
||||
import cc.fascinated.common.renderer.SkinRenderer;
|
||||
import cc.fascinated.common.renderer.impl.BodyRenderer;
|
||||
import cc.fascinated.common.renderer.impl.IsometricHeadRenderer;
|
||||
import cc.fascinated.common.renderer.impl.SquareRenderer;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public interface ISkinPart {
|
||||
Enum<?>[][] TYPES = { Vanilla.values(), Custom.values() };
|
||||
|
||||
/**
|
||||
* The name of the part.
|
||||
*
|
||||
* @return the part name
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Should this part be hidden from the
|
||||
* player skin part urls list?
|
||||
*
|
||||
* @return whether this part should be hidden
|
||||
*/
|
||||
boolean hidden();
|
||||
|
||||
/**
|
||||
* Renders the skin part for the skin.
|
||||
*
|
||||
* @param skin the skin
|
||||
* @param renderOverlays should the overlays be rendered
|
||||
* @param size the size of the part
|
||||
* @return the rendered skin part
|
||||
*/
|
||||
BufferedImage render(Skin skin, boolean renderOverlays, int size);
|
||||
|
||||
/**
|
||||
* Get a skin part by the given name.
|
||||
*
|
||||
* @param name the name of the part
|
||||
* @return the part, null if none
|
||||
*/
|
||||
static ISkinPart getByName(String name) {
|
||||
name = name.toUpperCase();
|
||||
for (Enum<?>[] type : TYPES) {
|
||||
for (Enum<?> part : type) {
|
||||
if (!part.name().equals(name)) {
|
||||
continue;
|
||||
}
|
||||
return (ISkinPart) part;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Getter
|
||||
enum Vanilla implements ISkinPart {
|
||||
// Overlays
|
||||
HEAD_OVERLAY_FACE(true, new Coordinates(40, 8), 8, 8),
|
||||
|
||||
// Head
|
||||
HEAD_TOP(true, new Coordinates(8, 0), 8, 8),
|
||||
FACE(false, new Coordinates(8, 8), 8, 8, HEAD_OVERLAY_FACE),
|
||||
HEAD_LEFT(true, new Coordinates(0, 8), 8, 8),
|
||||
HEAD_RIGHT(true, new Coordinates(16, 8), 8, 8),
|
||||
HEAD_BOTTOM(true, new Coordinates(16, 0), 8, 8),
|
||||
HEAD_BACK(true, new Coordinates(24, 8), 8, 8),
|
||||
|
||||
// Body
|
||||
BODY_FRONT(true, new Coordinates(20, 20), 8, 12),
|
||||
|
||||
// Arms
|
||||
LEFT_ARM_TOP(true, new Coordinates(36, 48), 4, 4),
|
||||
RIGHT_ARM_TOP(true, new Coordinates(44, 16), 4, 4),
|
||||
|
||||
LEFT_ARM_FRONT(true, new Coordinates(44, 20), 4, 12),
|
||||
RIGHT_ARM_FRONT(true, new Coordinates(36, 52), new LegacyCoordinates(44, 20, true), 4, 12),
|
||||
|
||||
// Legs
|
||||
LEFT_LEG_FRONT(true, new Coordinates(4, 20), 4, 12), // Front
|
||||
RIGHT_LEG_FRONT(true, new Coordinates(20, 52), new LegacyCoordinates(4, 20, true), 4, 12); // Front
|
||||
|
||||
/**
|
||||
* Should this part be hidden from the
|
||||
* player skin part urls list?
|
||||
*/
|
||||
private final boolean hidden;
|
||||
|
||||
/**
|
||||
* The coordinates of the part.
|
||||
*/
|
||||
private final Coordinates coordinates;
|
||||
|
||||
/**
|
||||
* The legacy coordinates of the part.
|
||||
*/
|
||||
private final LegacyCoordinates legacyCoordinates;
|
||||
|
||||
/**
|
||||
* The width and height of the part.
|
||||
*/
|
||||
private final int width, height;
|
||||
|
||||
/**
|
||||
* The overlays of the part.
|
||||
*/
|
||||
private final Vanilla[] overlays;
|
||||
|
||||
Vanilla(boolean hidden, Coordinates coordinates, int width, int height, Vanilla... overlays) {
|
||||
this(hidden, coordinates, null, width, height, overlays);
|
||||
}
|
||||
|
||||
Vanilla(boolean hidden, Coordinates coordinates, LegacyCoordinates legacyCoordinates, int width, int height, Vanilla... overlays) {
|
||||
this.hidden = hidden;
|
||||
this.coordinates = coordinates;
|
||||
this.legacyCoordinates = legacyCoordinates;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.overlays = overlays;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hidden() {
|
||||
return this.isHidden();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage render(Skin skin, boolean renderOverlays, int size) {
|
||||
return SquareRenderer.INSTANCE.render(skin, this, renderOverlays, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this part a front arm?
|
||||
*
|
||||
* @return whether this part is a front arm
|
||||
*/
|
||||
public boolean isFrontArm() {
|
||||
return this == LEFT_ARM_FRONT || this == RIGHT_ARM_FRONT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this part have legacy coordinates?
|
||||
*
|
||||
* @return whether this part has legacy coordinates
|
||||
*/
|
||||
public boolean hasLegacyCoordinates() {
|
||||
return legacyCoordinates != null;
|
||||
}
|
||||
|
||||
@AllArgsConstructor @Getter
|
||||
public static class Coordinates {
|
||||
/**
|
||||
* The X and Y position of the part.
|
||||
*/
|
||||
private final int x, y;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class LegacyCoordinates extends Coordinates {
|
||||
/**
|
||||
* Should the part be flipped horizontally?
|
||||
*/
|
||||
private final boolean flipped;
|
||||
|
||||
public LegacyCoordinates(int x, int y) {
|
||||
this(x, y, false);
|
||||
}
|
||||
|
||||
public LegacyCoordinates(int x, int y, boolean flipped) {
|
||||
super(x, y);
|
||||
this.flipped = flipped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor @Getter
|
||||
enum Custom implements ISkinPart {
|
||||
HEAD(IsometricHeadRenderer.INSTANCE),
|
||||
BODY(BodyRenderer.INSTANCE);
|
||||
|
||||
/**
|
||||
* The renderer to use for this part
|
||||
*/
|
||||
private final SkinRenderer<Custom> renderer;
|
||||
|
||||
@Override
|
||||
public boolean hidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage render(Skin skin, boolean renderOverlays, int size) {
|
||||
return renderer.render(skin, this, renderOverlays, size);
|
||||
}
|
||||
}
|
||||
}
|
128
src/main/java/cc/fascinated/model/skin/Skin.java
Normal file
128
src/main/java/cc/fascinated/model/skin/Skin.java
Normal file
@ -0,0 +1,128 @@
|
||||
package cc.fascinated.model.skin;
|
||||
|
||||
import cc.fascinated.common.PlayerUtils;
|
||||
import cc.fascinated.config.Config;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@AllArgsConstructor @NoArgsConstructor
|
||||
@Getter @Log4j2
|
||||
public class Skin {
|
||||
/**
|
||||
* The default skin, usually used when the skin is not found.
|
||||
*/
|
||||
public static final Skin DEFAULT_SKIN = new Skin("http://textures.minecraft.net/texture/60a5bd016b3c9a1b9272e4929e30827a67be4ebb219017adbbc4a4d22ebd5b1",
|
||||
Model.DEFAULT);
|
||||
|
||||
/**
|
||||
* The URL for the skin
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* The model for the skin
|
||||
*/
|
||||
private Model model;
|
||||
|
||||
/**
|
||||
* The legacy status of the skin
|
||||
*/
|
||||
private boolean isLegacy = false;
|
||||
|
||||
/**
|
||||
* The skin image for the skin
|
||||
*/
|
||||
@JsonIgnore
|
||||
private byte[] skinImage;
|
||||
|
||||
/**
|
||||
* The part URLs of the skin
|
||||
*/
|
||||
@JsonProperty("parts")
|
||||
private Map<String, String> partUrls = new HashMap<>();
|
||||
|
||||
public Skin(String url, Model model) {
|
||||
this.url = url;
|
||||
this.model = model;
|
||||
|
||||
this.skinImage = PlayerUtils.getSkinImage(url);
|
||||
if (this.skinImage != null) {
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(this.skinImage));
|
||||
if (image.getWidth() == 64 && image.getHeight() == 32) { // Using the old skin format
|
||||
this.isLegacy = true;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the skin from a {@link JsonObject}.
|
||||
*
|
||||
* @param json the JSON object
|
||||
* @return the skin
|
||||
*/
|
||||
public static Skin fromJson(JsonObject json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
String url = json.get("url").getAsString();
|
||||
JsonObject metadata = json.getAsJsonObject("metadata");
|
||||
Model model = Model.fromName(metadata == null ? "default" : // Fall back to slim if the model is not found
|
||||
metadata.get("model").getAsString());
|
||||
return new Skin(url, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the part URLs for the skin.
|
||||
*
|
||||
* @param playerUuid the player's UUID
|
||||
*/
|
||||
public Skin populatePartUrls(String playerUuid) {
|
||||
for (Enum<?>[] type : ISkinPart.TYPES) {
|
||||
for (Enum<?> part : type) {
|
||||
ISkinPart skinPart = (ISkinPart) part;
|
||||
if (skinPart.hidden()) {
|
||||
continue;
|
||||
}
|
||||
String partName = part.name().toLowerCase();
|
||||
this.partUrls.put(partName, Config.INSTANCE.getWebPublicUrl() + "/player/" + partName + "/" + playerUuid);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The model of the skin.
|
||||
*/
|
||||
public enum Model {
|
||||
DEFAULT,
|
||||
SLIM;
|
||||
|
||||
/**
|
||||
* Gets the model from its name.
|
||||
*
|
||||
* @param name the name of the model
|
||||
* @return the model
|
||||
*/
|
||||
public static Model fromName(String name) {
|
||||
for (Model model : values()) {
|
||||
if (model.name().equalsIgnoreCase(name)) {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package cc.fascinated.model.token;
|
||||
|
||||
import cc.fascinated.model.server.JavaMinecraftServer;
|
||||
import cc.fascinated.model.server.MinecraftServer;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public final class JavaServerStatusToken {
|
||||
|
||||
/**
|
||||
* The version of the server.
|
||||
*/
|
||||
private final JavaMinecraftServer.Version version;
|
||||
|
||||
/**
|
||||
* The players on the server.
|
||||
*/
|
||||
private final MinecraftServer.Players players;
|
||||
|
||||
/**
|
||||
* The mods running on this server.
|
||||
*/
|
||||
@SerializedName("modinfo")
|
||||
private JavaMinecraftServer.ForgeModInfo modInfo;
|
||||
|
||||
/**
|
||||
* The mods running on this server.
|
||||
* <p>
|
||||
* This is only used for servers
|
||||
* running 1.13 and above.
|
||||
* </p>
|
||||
*/
|
||||
private JavaMinecraftServer.ForgeData forgeData;
|
||||
|
||||
/**
|
||||
* The motd of the server.
|
||||
*/
|
||||
private final Object description;
|
||||
|
||||
/**
|
||||
* The favicon of the server.
|
||||
*/
|
||||
private final String favicon;
|
||||
|
||||
/**
|
||||
* Whether the server prevents chat reports.
|
||||
*/
|
||||
private boolean preventsChatReports;
|
||||
|
||||
/**
|
||||
* Whether the server enforces secure chat.
|
||||
*/
|
||||
private boolean enforcesSecureChat;
|
||||
|
||||
/**
|
||||
* Whether the server has previews chat enabled.
|
||||
* <p>
|
||||
* Chat Preview sends chat messages to the server as they are typed, even before they're sent.
|
||||
* <a href="https://www.minecraft.net/es-mx/article/minecraft-snapshot-22w19a">More information</a>
|
||||
* </p>
|
||||
*/
|
||||
private boolean previewsChat;
|
||||
}
|
Reference in New Issue
Block a user