minor fixes #4
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import io.swagger.v3.oas.annotations.info.Contact;
|
||||
import io.swagger.v3.oas.annotations.info.Info;
|
||||
import io.swagger.v3.oas.annotations.info.License;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@SpringBootApplication(exclude = { JacksonAutoConfiguration.class })
|
||||
@Slf4j(topic = "RESTfulMC")
|
||||
@OpenAPIDefinition(info = @Info(
|
||||
title = "RESTfulMC",
|
||||
description = "A simple, yet useful RESTful API for Minecraft utilizing Springboot.",
|
||||
version = "1.0.0",
|
||||
contact = @Contact(name = "Braydon (Rainnny)", url = "https://rainnny.club", email = "braydonrainnny@gmail.com"),
|
||||
license = @License(name = "MIT License", url = "https://opensource.org/license/MIT")
|
||||
))
|
||||
public class RESTfulMC {
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
.setDateFormat("MM-dd-yyyy HH:mm:ss")
|
||||
.create();
|
||||
|
||||
@SneakyThrows
|
||||
public static void main(@NonNull String[] args) {
|
||||
// Handle loading of our configuration file
|
||||
File config = new File("application.yml");
|
||||
if (!config.exists()) { // Saving the default config if it doesn't exist locally
|
||||
Files.copy(Objects.requireNonNull(RESTfulMC.class.getResourceAsStream("/application.yml")), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
log.info("Saved the default configuration to '{}', please re-launch the application", // Log the default config being saved
|
||||
config.getAbsolutePath()
|
||||
);
|
||||
return;
|
||||
}
|
||||
log.info("Found configuration at '{}'", config.getAbsolutePath()); // Log the found config
|
||||
|
||||
// Start the app
|
||||
SpringApplication.run(RESTfulMC.class, args);
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.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,76 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.*;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class DNSUtils {
|
||||
private static final String SRV_QUERY_PREFIX = "_minecraft._tcp.%s";
|
||||
|
||||
/**
|
||||
* Resolve the hostname to an {@link InetSocketAddress}.
|
||||
*
|
||||
* @param hostname the hostname to resolve
|
||||
* @return the resolved {@link InetSocketAddress}
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static InetSocketAddress resolveSRV(@NonNull String hostname) {
|
||||
Record[] records = new Lookup(SRV_QUERY_PREFIX.formatted(hostname), Type.SRV).run(); // Resolve SRV records
|
||||
if (records == null) { // No records exist
|
||||
return null;
|
||||
}
|
||||
String host = null;
|
||||
int port = -1;
|
||||
for (Record record : records) {
|
||||
SRVRecord srv = (SRVRecord) record;
|
||||
host = srv.getTarget().toString().replaceFirst("\\.$", "");
|
||||
port = srv.getPort();
|
||||
}
|
||||
return host == null ? null : new InetSocketAddress(host, port);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static InetAddress resolveA(@NonNull String hostname) {
|
||||
Record[] records = new Lookup(hostname, Type.A).run(); // Resolve A records
|
||||
if (records == null) { // No records exist
|
||||
return null;
|
||||
}
|
||||
InetAddress address = null;
|
||||
for (Record record : records) {
|
||||
address = ((ARecord) record).getAddress();
|
||||
}
|
||||
return address;
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class EnumUtils {
|
||||
/**
|
||||
* Get the enum constant of the specified enum type with the specified name.
|
||||
*
|
||||
* @param enumType the enum type
|
||||
* @param name the name of the constant to return
|
||||
* @param <T> the type of the enum
|
||||
* @return the enum constant of the specified enum type with the specified name
|
||||
*/
|
||||
public <T extends Enum<T>> T getEnumConstant(@NonNull Class<T> enumType, @NonNull String name) {
|
||||
try {
|
||||
return Enum.valueOf(enumType, name);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import net.jodah.expiringmap.ExpirationPolicy;
|
||||
import net.jodah.expiringmap.ExpiringMap;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A simple set that expires elements after a certain
|
||||
* amount of time, utilizing the {@link ExpiringMap} library.
|
||||
*
|
||||
* @param <T> The type of element to store within this set
|
||||
* @author Braydon
|
||||
*/
|
||||
@ThreadSafe
|
||||
public final class ExpiringSet<T> implements Iterable<T> {
|
||||
/**
|
||||
* The internal cache for this set.
|
||||
*/
|
||||
@NonNull private final ExpiringMap<T, Long> cache;
|
||||
|
||||
/**
|
||||
* The lifetime (in millis) of the elements in this set.
|
||||
*/
|
||||
private final long lifetime;
|
||||
|
||||
public ExpiringSet(@NonNull ExpirationPolicy expirationPolicy, long duration, @NonNull TimeUnit timeUnit) {
|
||||
this(expirationPolicy, duration, timeUnit, ignored -> {});
|
||||
}
|
||||
|
||||
public ExpiringSet(@NonNull ExpirationPolicy expirationPolicy, long duration, @NonNull TimeUnit timeUnit, @NonNull Consumer<T> onExpire) {
|
||||
//noinspection unchecked
|
||||
this.cache = ExpiringMap.builder()
|
||||
.expirationPolicy(expirationPolicy)
|
||||
.expiration(duration, timeUnit)
|
||||
.expirationListener((key, ignored) -> onExpire.accept((T) key))
|
||||
.build();
|
||||
this.lifetime = timeUnit.toMillis(duration); // Get the lifetime in millis
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element to this set.
|
||||
*
|
||||
* @param element the element
|
||||
* @return whether the element was added
|
||||
*/
|
||||
public boolean add(@NonNull T element) {
|
||||
boolean contains = contains(element); // Does this set already contain the element?
|
||||
this.cache.put(element, System.currentTimeMillis() + this.lifetime);
|
||||
return !contains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry time of an element in this set.
|
||||
*
|
||||
* @param element the element
|
||||
* @return the entry time, -1 if not contained
|
||||
*/
|
||||
public long getEntryTime(@NonNull T element) {
|
||||
return contains(element) ? this.cache.get(element) - this.lifetime : -1L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an element is
|
||||
* contained within this set.
|
||||
*
|
||||
* @param element the element
|
||||
* @return whether the element is contained
|
||||
*/
|
||||
public boolean contains(@NonNull T element) {
|
||||
Long timeout = this.cache.get(element); // Get the timeout for the element
|
||||
return timeout != null && (timeout > System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this set is empty.
|
||||
*
|
||||
* @return whether this set is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.cache.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of this set.
|
||||
*
|
||||
* @return the size
|
||||
*/
|
||||
public int size() {
|
||||
return this.cache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an element from this set.
|
||||
*
|
||||
* @param element the element
|
||||
* @return whether the element was removed
|
||||
*/
|
||||
public boolean remove(@NonNull T element) {
|
||||
return this.cache.remove(element) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear this set.
|
||||
*/
|
||||
public void clear() {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the elements in this set.
|
||||
*
|
||||
* @return the elements
|
||||
*/
|
||||
@NonNull
|
||||
public Set<T> getElements() {
|
||||
return this.cache.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over elements of type {@code T}.
|
||||
*
|
||||
* @return an Iterator.
|
||||
*/
|
||||
@Override @NonNull
|
||||
public Iterator<T> iterator() {
|
||||
return this.cache.keySet().iterator();
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class IPUtils {
|
||||
private static final String[] IP_HEADERS = new String[] {
|
||||
"CF-Connecting-IP",
|
||||
"X-Forwarded-For"
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the real IP from the given request.
|
||||
*
|
||||
* @param request the request
|
||||
* @return the real IP
|
||||
*/
|
||||
@NonNull
|
||||
public static String getRealIp(@NonNull HttpServletRequest request) {
|
||||
String ip = request.getRemoteAddr();
|
||||
for (String headerName : IP_HEADERS) {
|
||||
String header = request.getHeader(headerName);
|
||||
if (header == null) {
|
||||
continue;
|
||||
}
|
||||
if (!header.contains(",")) { // Handle single IP
|
||||
ip = header;
|
||||
break;
|
||||
}
|
||||
// Handle multiple IPs
|
||||
String[] ips = header.split(",");
|
||||
for (String ipHeader : ips) {
|
||||
ip = ipHeader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import me.braydon.mc.model.Skin;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class ImageUtils {
|
||||
public static final int SKIN_TEXTURE_SIZE = 64; // The skin of a skin texture
|
||||
|
||||
/**
|
||||
* Get the texture of a part of a skin.
|
||||
*
|
||||
* @param skin the skin to get the part texture from
|
||||
* @param part the part of the skin to get
|
||||
* @param size the size to scale the texture to
|
||||
* @return the texture of the skin part
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static byte[] getSkinPart(@NonNull Skin skin, @NonNull Skin.Part part, int size) {
|
||||
return getSkinPartTexture(skin, part.getX(), part.getY(), part.getWidth(), part.getHeight(), size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture of a specific part of a skin.
|
||||
*
|
||||
* @param skin the skin to get the part from
|
||||
* @param x the x position of the part
|
||||
* @param y the y position of the part
|
||||
* @param width the width of the part
|
||||
* @param height the height of the part
|
||||
* @param size the size to scale the part to
|
||||
* @return the texture of the skin part
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static byte[] getSkinPartTexture(@NonNull Skin skin, int x, int y, int width, int height, int size) {
|
||||
BufferedImage skinImage = ImageIO.read(new URL(skin.getUrl())); // The skin texture
|
||||
|
||||
// Create a new BufferedImage for the part of the skin texture
|
||||
BufferedImage headTexture = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
// Crop just the part we want based on our x, y, width, and height
|
||||
headTexture.getGraphics().drawImage(skinImage, 0, 0, width, height, x, y, x + width, y + height, null);
|
||||
|
||||
// Scale the skin part texture
|
||||
double scale = (double) size / width;
|
||||
AffineTransform transform = AffineTransform.getScaleInstance(scale, scale);
|
||||
headTexture = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR).filter(headTexture, null);
|
||||
|
||||
// Convert BufferedImage to byte array
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
ImageIO.write(headTexture, "png", outputStream);
|
||||
outputStream.flush();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* A list of versions for the
|
||||
* Java edition of Minecraft.
|
||||
*
|
||||
* @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
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class MiscUtils {
|
||||
private static final Pattern USERNAME_REGEX = Pattern.compile("^[a-zA-Z0-9_]{2,16}$");
|
||||
private static final List<String> WHITELISTED_NAMES = Arrays.asList("8", "g");
|
||||
|
||||
/**
|
||||
* Check if the given username is a valid.
|
||||
*
|
||||
* @param username the username to check
|
||||
* @return whether the username is valid
|
||||
*/
|
||||
public static boolean isUsernameValid(@NonNull String username) {
|
||||
if (WHITELISTED_NAMES.contains(username.toLowerCase())) { // Name is whitelisted
|
||||
return true;
|
||||
}
|
||||
return USERNAME_REGEX.matcher(username).matches();
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* A simple tuple utility.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@NoArgsConstructor @AllArgsConstructor @Setter @Getter
|
||||
public class Tuple<L, R> {
|
||||
/**
|
||||
* The left value.
|
||||
*/
|
||||
private L left;
|
||||
|
||||
/**
|
||||
* The right value.
|
||||
*/
|
||||
private R right;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class UUIDUtils {
|
||||
/**
|
||||
* Add dashes to an untrimmed uuid.
|
||||
*
|
||||
* @param trimmed the untrimmed uuid
|
||||
* @return the uuid with dashes
|
||||
*/
|
||||
@NonNull
|
||||
public static UUID addDashes(@NonNull String trimmed) {
|
||||
StringBuilder builder = new StringBuilder(trimmed);
|
||||
for (int i = 0, pos = 20; i < 4; i++, pos -= 4) {
|
||||
builder.insert(pos, "-");
|
||||
}
|
||||
return UUID.fromString(builder.toString());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.packet;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramSocket;
|
||||
|
||||
/**
|
||||
* Represents a packet in the
|
||||
* Minecraft Bedrock protocol.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Raknet_Protocol">Protocol Docs</a>
|
||||
*/
|
||||
public interface MinecraftBedrockPacket {
|
||||
/**
|
||||
* Process this packet.
|
||||
*
|
||||
* @param socket the socket to process the packet for
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
void process(@NonNull DatagramSocket socket) throws IOException;
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.packet;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Represents a packet in the
|
||||
* Minecraft Java protocol.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Protocol">Protocol Docs</a>
|
||||
*/
|
||||
public abstract class MinecraftJavaPacket {
|
||||
/**
|
||||
* Process this packet.
|
||||
*
|
||||
* @param inputStream the input stream to read from
|
||||
* @param outputStream the output stream to write to
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public abstract void process(@NonNull DataInputStream inputStream, @NonNull DataOutputStream outputStream) throws IOException;
|
||||
|
||||
/**
|
||||
* Write a variable integer to the output stream.
|
||||
*
|
||||
* @param outputStream the output stream to write to
|
||||
* @param paramInt the integer to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected final void writeVarInt(DataOutputStream outputStream, int paramInt) throws IOException {
|
||||
while (true) {
|
||||
if ((paramInt & 0xFFFFFF80) == 0) {
|
||||
outputStream.writeByte(paramInt);
|
||||
return;
|
||||
}
|
||||
outputStream.writeByte(paramInt & 0x7F | 0x80);
|
||||
paramInt >>>= 7;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a variable integer from the input stream.
|
||||
*
|
||||
* @param inputStream the input stream to read from
|
||||
* @return the integer that was read
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected final int readVarInt(@NonNull DataInputStream inputStream) throws IOException {
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (true) {
|
||||
int k = inputStream.readByte();
|
||||
i |= (k & 0x7F) << j++ * 7;
|
||||
if (j > 5) {
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
if ((k & 0x80) != 128) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.packet.impl.bedrock;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.mc.common.packet.MinecraftBedrockPacket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* This packet is sent by the client to the server to
|
||||
* request a pong response from the server. The server
|
||||
* will respond with a string containing the server's status.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Raknet_Protocol#Unconnected_Ping">Protocol Docs</a>
|
||||
*/
|
||||
public final class BedrockPacketUnconnectedPing implements MinecraftBedrockPacket {
|
||||
private static final byte ID = 0x01; // The ID of the packet
|
||||
private static final byte[] MAGIC = { 0, -1, -1, 0, -2, -2, -2, -2, -3, -3, -3, -3, 18, 52, 86, 120 };
|
||||
|
||||
/**
|
||||
* Process this packet.
|
||||
*
|
||||
* @param socket the socket to process the packet for
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public void process(@NonNull DatagramSocket socket) throws IOException {
|
||||
// Construct the packet buffer
|
||||
ByteBuffer buffer = ByteBuffer.allocate(33).order(ByteOrder.LITTLE_ENDIAN);;
|
||||
buffer.put(ID); // Packet ID
|
||||
buffer.putLong(System.currentTimeMillis()); // Timestamp
|
||||
buffer.put(MAGIC); // Magic
|
||||
buffer.putLong(0L); // Client GUID
|
||||
|
||||
// Send the packet
|
||||
socket.send(new DatagramPacket(buffer.array(), 0, buffer.limit()));
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.packet.impl.bedrock;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.braydon.mc.common.packet.MinecraftBedrockPacket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* This packet is sent by the server to the client in
|
||||
* response to the {@link BedrockPacketUnconnectedPing}.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Raknet_Protocol#Unconnected_Pong">Protocol Docs</a>
|
||||
*/
|
||||
@Getter
|
||||
public final class BedrockPacketUnconnectedPong implements MinecraftBedrockPacket {
|
||||
private static final byte ID = 0x1C; // The ID of the packet
|
||||
|
||||
/**
|
||||
* The response from the server, null if none.
|
||||
*/
|
||||
private String response;
|
||||
|
||||
/**
|
||||
* Process this packet.
|
||||
*
|
||||
* @param socket the socket to process the packet for
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public void process(@NonNull DatagramSocket socket) throws IOException {
|
||||
// Handle receiving of the packet
|
||||
byte[] receiveData = new byte[2048];
|
||||
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
||||
socket.receive(receivePacket);
|
||||
|
||||
// Construct a buffer from the received packet
|
||||
ByteBuffer buffer = ByteBuffer.wrap(receivePacket.getData()).order(ByteOrder.LITTLE_ENDIAN);
|
||||
byte id = buffer.get(); // The received packet id
|
||||
if (id == ID) {
|
||||
response = new String(buffer.array(), StandardCharsets.UTF_8).substring(34).trim();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.packet.impl.java;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import me.braydon.mc.common.packet.MinecraftJavaPacket;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This packet is sent by the client to the server to set
|
||||
* the hostname, port, and protocol version of the client.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Protocol#Handshake">Protocol Docs</a>
|
||||
*/
|
||||
@AllArgsConstructor @ToString
|
||||
public final class JavaPacketHandshakingInSetProtocol extends MinecraftJavaPacket {
|
||||
private static final byte ID = 0x00; // The ID of the packet
|
||||
private static final int STATUS_HANDSHAKE = 1; // The status handshake ID
|
||||
|
||||
/**
|
||||
* The hostname of the server.
|
||||
*/
|
||||
@NonNull private final String hostname;
|
||||
|
||||
/**
|
||||
* The port of the server.
|
||||
*/
|
||||
private final int port;
|
||||
|
||||
/**
|
||||
* The protocol version of the server.
|
||||
*/
|
||||
private final int protocolVersion;
|
||||
|
||||
/**
|
||||
* Process this packet.
|
||||
*
|
||||
* @param inputStream the input stream to read from
|
||||
* @param outputStream the output stream to write to
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public void process(@NonNull DataInputStream inputStream, @NonNull DataOutputStream outputStream) throws IOException {
|
||||
try (ByteArrayOutputStream handshakeBytes = new ByteArrayOutputStream();
|
||||
DataOutputStream handshake = new DataOutputStream(handshakeBytes)
|
||||
) {
|
||||
handshake.writeByte(ID); // Write the ID of the packet
|
||||
writeVarInt(handshake, protocolVersion); // Write the protocol version
|
||||
writeVarInt(handshake, hostname.length()); // Write the length of the hostname
|
||||
handshake.writeBytes(hostname); // Write the hostname
|
||||
handshake.writeShort(port); // Write the port
|
||||
writeVarInt(handshake, STATUS_HANDSHAKE); // Write the status handshake ID
|
||||
|
||||
// Write the handshake bytes to the output stream
|
||||
writeVarInt(outputStream, handshakeBytes.size());
|
||||
outputStream.write(handshakeBytes.toByteArray());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.packet.impl.java;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.braydon.mc.common.packet.MinecraftJavaPacket;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This packet is sent by the client to the server to request the
|
||||
* status of the server. The server will respond with a json object
|
||||
* containing the server's status.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Protocol#Status_Request">Protocol Docs</a>
|
||||
*/
|
||||
@Getter
|
||||
public final class JavaPacketStatusInStart extends MinecraftJavaPacket {
|
||||
private static final byte ID = 0x00; // The ID of the packet
|
||||
|
||||
/**
|
||||
* The response json from the server, null if none.
|
||||
*/
|
||||
private String response;
|
||||
|
||||
/**
|
||||
* Process this packet.
|
||||
*
|
||||
* @param inputStream the input stream to read from
|
||||
* @param outputStream the output stream to write to
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public void process(@NonNull DataInputStream inputStream, @NonNull DataOutputStream outputStream) throws IOException {
|
||||
// Send the status request
|
||||
outputStream.writeByte(0x01); // Size of packet
|
||||
outputStream.writeByte(ID);
|
||||
|
||||
// Read the status response
|
||||
readVarInt(inputStream); // Size of the response
|
||||
int id = readVarInt(inputStream);
|
||||
if (id == -1) { // The stream was prematurely ended
|
||||
throw new IOException("Server prematurely ended stream.");
|
||||
} else if (id != ID) { // Invalid packet ID
|
||||
throw new IOException("Server returned invalid packet ID.");
|
||||
}
|
||||
|
||||
int length = readVarInt(inputStream); // Length of the response
|
||||
if (length == -1) { // The stream was prematurely ended
|
||||
throw new IOException("Server prematurely ended stream.");
|
||||
} else if (length == 0) {
|
||||
throw new IOException("Server returned unexpected value.");
|
||||
}
|
||||
|
||||
// Get the json response
|
||||
byte[] data = new byte[length];
|
||||
inputStream.readFully(data);
|
||||
response = new String(data);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.web;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* This exception is raised when a
|
||||
* {@link JsonWebRequest} encounters an error.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Getter
|
||||
public class JsonWebException extends RuntimeException {
|
||||
/**
|
||||
* The status code of the response.
|
||||
*/
|
||||
private final int statusCode;
|
||||
|
||||
protected JsonWebException(int statusCode, @NonNull String message) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
protected JsonWebException(int statusCode, @NonNull Throwable cause) {
|
||||
super(cause);
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.common.web;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.braydon.mc.RESTfulMC;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
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.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A Json web request.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @Getter
|
||||
public final class JsonWebRequest {
|
||||
private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
|
||||
|
||||
/**
|
||||
* The endpoint to make the request to.
|
||||
*/
|
||||
@NonNull private final String endpoint;
|
||||
|
||||
/**
|
||||
* The method to use for the request.
|
||||
*/
|
||||
@NonNull private final HttpMethod method;
|
||||
|
||||
/**
|
||||
* The headers to send with the request.
|
||||
*/
|
||||
private final Map<String, String> headers = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
/**
|
||||
* Make a new web request.
|
||||
*
|
||||
* @param endpoint the endpoint to make the request to
|
||||
* @param method the method of the request to make
|
||||
* @return the web request
|
||||
*/
|
||||
@NonNull
|
||||
public static JsonWebRequest makeRequest(@NonNull String endpoint, @NonNull HttpMethod method) {
|
||||
return new JsonWebRequest(endpoint, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header for this request.
|
||||
*
|
||||
* @param name the header name
|
||||
* @param value the header value
|
||||
* @return the request
|
||||
*/
|
||||
@NonNull
|
||||
public JsonWebRequest header(@NonNull String name, @NonNull String value) {
|
||||
headers.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this request.
|
||||
*
|
||||
* @param responseType the response type
|
||||
* @return the response
|
||||
* @param <T> the type of the response
|
||||
* @throws JsonWebException if an error is encountered while making the request
|
||||
*/
|
||||
public <T> T execute(@NonNull Class<T> responseType) throws JsonWebException {
|
||||
// Build the request
|
||||
HttpRequest.Builder request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(endpoint))
|
||||
.method(method.name(), HttpRequest.BodyPublishers.noBody());
|
||||
// Append headers
|
||||
headers.put("User-Agent", "RESTfulMC");
|
||||
headers.put("Content-Type", "application/json");
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
request.header(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
// Send the request and get the response
|
||||
int status = -1; // The response status code
|
||||
try {
|
||||
HttpResponse<String> response = HTTP_CLIENT.send(request.build(), HttpResponse.BodyHandlers.ofString());
|
||||
status = response.statusCode(); // Set the response status
|
||||
if (status != HttpStatus.OK.value()) { // Status code is not OK, raise an exception
|
||||
throw new IOException("Failed to make a %s request to %s: %s".formatted(method.name(), endpoint, status));
|
||||
}
|
||||
// Return with the response as the type
|
||||
return RESTfulMC.GSON.fromJson(response.body(), responseType);
|
||||
} catch (Exception ex) {
|
||||
if (!(ex instanceof JsonWebException)) {
|
||||
throw new JsonWebException(status, ex);
|
||||
}
|
||||
throw (JsonWebException) ex;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Getter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* The configuration for the app.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Configuration @Getter
|
||||
public class AppConfig {
|
||||
public static AppConfig INSTANCE;
|
||||
|
||||
@Value("${server.publicUrl}")
|
||||
private String serverPublicUrl;
|
||||
|
||||
@PostConstruct
|
||||
public void onInitialize() {
|
||||
INSTANCE = this;
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.config;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Configuration
|
||||
@Log4j2(topic = "Redis")
|
||||
public class RedisConfig {
|
||||
/**
|
||||
* The Redis server host.
|
||||
*/
|
||||
@Value("${spring.data.redis.host}")
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* The Redis server port.
|
||||
*/
|
||||
@Value("${spring.data.redis.port}")
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* The Redis database index.
|
||||
*/
|
||||
@Value("${spring.data.redis.database}")
|
||||
private int database;
|
||||
|
||||
/**
|
||||
* The optional Redis password.
|
||||
*/
|
||||
@Value("${spring.data.redis.auth}")
|
||||
private String auth;
|
||||
|
||||
/**
|
||||
* Build the config to use for Redis.
|
||||
*
|
||||
* @return the config
|
||||
* @see RedisTemplate for config
|
||||
*/
|
||||
@Bean @NonNull
|
||||
public RedisTemplate<String, Object> redisTemplate() {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(jedisConnectionFactory());
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the connection factory to use
|
||||
* when making connections to Redis.
|
||||
*
|
||||
* @return the built factory
|
||||
* @see JedisConnectionFactory for factory
|
||||
*/
|
||||
@Bean @NonNull
|
||||
public JedisConnectionFactory jedisConnectionFactory() {
|
||||
log.info("Connecting to Redis at {}:{}/{}", host, port, database);
|
||||
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port);
|
||||
config.setDatabase(database);
|
||||
if (!auth.trim().isEmpty()) { // Auth with our provided password
|
||||
log.info("Using auth...");
|
||||
config.setPassword(auth);
|
||||
}
|
||||
return new JedisConnectionFactory(config);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import me.braydon.mc.RESTfulMC;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
|
||||
gsonHttpMessageConverter.setGson(gson());
|
||||
converters.add(gsonHttpMessageConverter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Gson gson() {
|
||||
return RESTfulMC.GSON;
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import me.braydon.mc.exception.impl.BadRequestException;
|
||||
import me.braydon.mc.exception.impl.MojangRateLimitException;
|
||||
import me.braydon.mc.exception.impl.ResourceNotFoundException;
|
||||
import me.braydon.mc.model.Player;
|
||||
import me.braydon.mc.model.cache.CachedPlayer;
|
||||
import me.braydon.mc.service.MojangService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* The controller for handling
|
||||
* {@link Player} related requests.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/player", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Log4j2(topic = "Player Controller")
|
||||
@Tag(name = "Player Controller", description = "The controller for handling player related requests.")
|
||||
public final class PlayerController {
|
||||
/**
|
||||
* The Mojang service to use for player information.
|
||||
*/
|
||||
@NonNull private final MojangService mojangService;
|
||||
|
||||
@Autowired
|
||||
public PlayerController(@NonNull MojangService mojangService) {
|
||||
this.mojangService = mojangService;
|
||||
}
|
||||
|
||||
/**
|
||||
* A GET route to get a player by their username or UUID.
|
||||
*
|
||||
* @param query the query to search for the player by
|
||||
* @return the player response
|
||||
* @throws BadRequestException if the UUID or username is invalid
|
||||
* @throws ResourceNotFoundException if the player is not found
|
||||
* @throws MojangRateLimitException if the Mojang API rate limit is reached
|
||||
*/
|
||||
@GetMapping("/{query}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<CachedPlayer> getPlayer(@PathVariable @NonNull String query)
|
||||
throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
|
||||
return ResponseEntity.ofNullable(mojangService.getPlayer(query, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the part of a skin texture for
|
||||
* a player by their username or UUID.
|
||||
* <p>
|
||||
* If the player being searched is
|
||||
* invalid, the default Steve skin
|
||||
* will be used.
|
||||
* </p>
|
||||
*
|
||||
* @param partName the part of the player's skin texture to get
|
||||
* @param query the query to search for the player by
|
||||
* @param extension the skin part image extension
|
||||
* @param size the size of the skin part image
|
||||
* @return the skin part texture
|
||||
* @throws BadRequestException if the extension is invalid
|
||||
*/
|
||||
@GetMapping(value = "/{partName}/{query}.{extension}", produces = { MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE })
|
||||
@ResponseBody
|
||||
public ResponseEntity<byte[]> getPartTexture(@PathVariable @NonNull String partName, @PathVariable @NonNull String query,
|
||||
@PathVariable @NonNull String extension, @RequestParam(required = false) String size
|
||||
) throws BadRequestException {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(extension.equalsIgnoreCase("png") ? MediaType.IMAGE_PNG : MediaType.IMAGE_JPEG)
|
||||
.body(mojangService.getSkinPartTexture(partName, query, extension, size));
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import me.braydon.mc.exception.impl.BadRequestException;
|
||||
import me.braydon.mc.exception.impl.ResourceNotFoundException;
|
||||
import me.braydon.mc.model.MinecraftServer;
|
||||
import me.braydon.mc.model.cache.CachedMinecraftServer;
|
||||
import me.braydon.mc.service.MojangService;
|
||||
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.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The controller for handling
|
||||
* {@link MinecraftServer} related requests.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/server", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Log4j2(topic = "Server Controller")
|
||||
@Tag(name = "Server Controller", description = "The controller for handling server related requests.")
|
||||
public final class ServerController {
|
||||
/**
|
||||
* The Mojang service to use for server information.
|
||||
*/
|
||||
@NonNull private final MojangService mojangService;
|
||||
|
||||
@Autowired
|
||||
public ServerController(@NonNull MojangService mojangService) {
|
||||
this.mojangService = mojangService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Minecraft server by its platform and hostname.
|
||||
*
|
||||
* @param platform the platform of the server
|
||||
* @param hostname the hostname of the server
|
||||
* @param port the port of the server, null for default
|
||||
* @return the server
|
||||
* @throws BadRequestException if the hostname, platform, or port is invalid
|
||||
* @throws ResourceNotFoundException if the server isn't found
|
||||
*/
|
||||
@GetMapping("/{platform}/{hostname}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<CachedMinecraftServer> getServer(@PathVariable @NonNull String platform, @PathVariable @NonNull String hostname,
|
||||
@RequestParam(required = false) String port
|
||||
) throws BadRequestException, ResourceNotFoundException {
|
||||
return ResponseEntity.ofNullable(mojangService.getMinecraftServer(platform, hostname, port));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the server with the
|
||||
* given hostname is blocked by Mojang.
|
||||
*
|
||||
* @param hostname the server hostname to check
|
||||
* @return whether the hostname is blocked
|
||||
*/
|
||||
@GetMapping("/blocked/{hostname}")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Map<String, Object>> isServerBlocked(@PathVariable @NonNull String hostname) {
|
||||
return ResponseEntity.ok(Map.of(
|
||||
"blocked", mojangService.isServerBlocked(hostname)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server icon of a Minecraft
|
||||
* server by its platform and hostname.
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param port the port of the server, null for default
|
||||
* @return the server icon
|
||||
*/
|
||||
@GetMapping(value = "/icon/{hostname}", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<byte[]> getServerFavicon(@PathVariable @NonNull String hostname, @RequestParam(required = false) String port) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=%s.png".formatted(hostname))
|
||||
.body(mojangService.getServerFavicon(hostname, port));
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.exception;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.mc.model.response.ErrorResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
|
||||
/**
|
||||
* Advice for handling raised exceptions.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public final class ExceptionControllerAdvice {
|
||||
/**
|
||||
* Handle a raised exception.
|
||||
*
|
||||
* @param ex the raised exception
|
||||
* @return the error response
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<?> handleException(@NonNull Exception ex) {
|
||||
HttpStatus status = null; // Get the HTTP status
|
||||
if (ex instanceof NoResourceFoundException) { // Not found
|
||||
status = HttpStatus.NOT_FOUND;
|
||||
} else if (ex instanceof UnsupportedOperationException) { // Not implemented
|
||||
status = HttpStatus.NOT_IMPLEMENTED;
|
||||
}
|
||||
if (ex.getClass().isAnnotationPresent(ResponseStatus.class)) { // Get from the @ResponseStatus annotation
|
||||
status = ex.getClass().getAnnotation(ResponseStatus.class).value();
|
||||
}
|
||||
String message = ex.getLocalizedMessage(); // Get the error message
|
||||
if (message == null) { // Fallback
|
||||
message = "An internal error has occurred.";
|
||||
}
|
||||
// Print the stack trace if no response status is present
|
||||
if (status == null) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
if (status == null) { // Fallback to 500
|
||||
status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
return new ResponseEntity<>(new ErrorResponse(status, message), status);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.exception.impl;
|
||||
|
||||
import lombok.experimental.StandardException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* This exception is raised
|
||||
* when a bad request is made.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@StandardException
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public final class BadRequestException extends RuntimeException { }
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.exception.impl;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* This exception is raised when the
|
||||
* Mojang API rate limit is reached.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
|
||||
public final class MojangRateLimitException extends RuntimeException {
|
||||
public MojangRateLimitException() {
|
||||
super("Mojang requests exhausted, please try again later.");
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.exception.impl;
|
||||
|
||||
import lombok.experimental.StandardException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* This exception is raised
|
||||
* when a resource is not found.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@StandardException
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public final class ResourceNotFoundException extends RuntimeException { }
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.log;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.braydon.mc.common.IPUtils;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Responsible for logging request and
|
||||
* response transactions to the terminal.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@ControllerAdvice
|
||||
@Slf4j(topic = "Req/Res Transaction")
|
||||
public class TransactionLogger implements ResponseBodyAdvice<Object> {
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType, @NonNull MediaType selectedContentType,
|
||||
@NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType, @NonNull ServerHttpRequest rawRequest,
|
||||
@NonNull ServerHttpResponse rawResponse) {
|
||||
HttpServletRequest request = ((ServletServerHttpRequest) rawRequest).getServletRequest();
|
||||
HttpServletResponse response = ((ServletServerHttpResponse) rawResponse).getServletResponse();
|
||||
|
||||
// Get the request ip ip
|
||||
String ip = IPUtils.getRealIp(request);
|
||||
|
||||
// Getting params
|
||||
Map<String, String> params = new HashMap<>();
|
||||
for (Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
|
||||
params.put(entry.getKey(), Arrays.toString(entry.getValue()));
|
||||
}
|
||||
|
||||
// Getting headers
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String headerName = headerNames.nextElement();
|
||||
headers.put(headerName, request.getHeader(headerName));
|
||||
}
|
||||
|
||||
// Log the request
|
||||
log.info(String.format("[Req] %s | %s | '%s', params=%s, headers=%s",
|
||||
request.getMethod(),
|
||||
ip,
|
||||
request.getRequestURI(),
|
||||
params,
|
||||
headers
|
||||
));
|
||||
|
||||
// Getting response headers
|
||||
headers = new HashMap<>();
|
||||
for (String headerName : response.getHeaderNames()) {
|
||||
headers.put(headerName, response.getHeader(headerName));
|
||||
}
|
||||
|
||||
// Log the response
|
||||
log.info(String.format("[Res] %s, headers=%s",
|
||||
response.getStatus(),
|
||||
headers
|
||||
));
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(@NonNull MethodParameter returnType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* A cape for a {@link Player}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter @ToString
|
||||
public final class Cape {
|
||||
/**
|
||||
* The texture URL of this cape.
|
||||
*/
|
||||
@NonNull private final String url;
|
||||
|
||||
/**
|
||||
* Build a cape from the given Json object.
|
||||
*
|
||||
* @param jsonObject the json object to build from
|
||||
* @return the built cape
|
||||
*/
|
||||
public static Cape fromJsonObject(JsonObject jsonObject) {
|
||||
if (jsonObject == null) { // No object to parse
|
||||
return null;
|
||||
}
|
||||
return new Cape(jsonObject.get("url").getAsString());
|
||||
}
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model;
|
||||
|
||||
import lombok.*;
|
||||
import me.braydon.mc.common.ColorUtils;
|
||||
import me.braydon.mc.service.pinger.MinecraftServerPinger;
|
||||
import me.braydon.mc.service.pinger.impl.BedrockMinecraftServerPinger;
|
||||
import me.braydon.mc.service.pinger.impl.JavaMinecraftServerPinger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A model representing a Minecraft server.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
|
||||
public class MinecraftServer {
|
||||
/**
|
||||
* The hostname of this server.
|
||||
*/
|
||||
@EqualsAndHashCode.Include @NonNull private final String hostname;
|
||||
|
||||
/**
|
||||
* The IP address of this server, if resolved.
|
||||
*/
|
||||
private final String ip;
|
||||
|
||||
/**
|
||||
* The port of this server.
|
||||
*/
|
||||
@EqualsAndHashCode.Include private final int port;
|
||||
|
||||
/**
|
||||
* The player counts of this server.
|
||||
*/
|
||||
@NonNull private final Players players;
|
||||
|
||||
/**
|
||||
* The MOTD of this server.
|
||||
*/
|
||||
@NonNull private final MOTD motd;
|
||||
|
||||
/**
|
||||
* Player count data for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The MOTD for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class MOTD {
|
||||
/**
|
||||
* The raw MOTD lines.
|
||||
*/
|
||||
@NonNull private final String[] raw;
|
||||
|
||||
/**
|
||||
* The clean MOTD lines (no color codes).
|
||||
*/
|
||||
@NonNull private final String[] clean;
|
||||
|
||||
/**
|
||||
* The HTML MOTD lines.
|
||||
*/
|
||||
@NonNull 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model;
|
||||
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A model representing a player.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@ToString
|
||||
public class Player {
|
||||
/**
|
||||
* The unique id of this player.
|
||||
*/
|
||||
@Id @EqualsAndHashCode.Include @NonNull private final UUID uniqueId;
|
||||
|
||||
/**
|
||||
* The username of this player.
|
||||
*/
|
||||
@NonNull private final String username;
|
||||
|
||||
/**
|
||||
* The skin of this player.
|
||||
*/
|
||||
@NonNull private final Skin skin;
|
||||
|
||||
/**
|
||||
* The cape of this player, null if none.
|
||||
*/
|
||||
private final Cape cape;
|
||||
|
||||
/**
|
||||
* The profile actions this player has, null if none.
|
||||
*/
|
||||
private final ProfileAction[] profileActions;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model;
|
||||
|
||||
/**
|
||||
* Profile actions that can
|
||||
* be taken on a {@link Player}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
public enum ProfileAction {
|
||||
/**
|
||||
* The player is required to change their
|
||||
* username before accessing Multiplayer.
|
||||
*/
|
||||
FORCED_NAME_CHANGE,
|
||||
|
||||
/**
|
||||
* The player is using a banned skin.
|
||||
*/
|
||||
USING_BANNED_SKIN
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.*;
|
||||
import me.braydon.mc.common.ImageUtils;
|
||||
import me.braydon.mc.config.AppConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A skin for a {@link Player}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @Setter @Getter @ToString
|
||||
public final class Skin {
|
||||
public static final Skin DEFAULT_STEVE = new Skin("http://textures.minecraft.net/texture/60a5bd016b3c9a1b9272e4929e30827a67be4ebb219017adbbc4a4d22ebd5b1", Model.DEFAULT);
|
||||
|
||||
/**
|
||||
* The texture URL of this skin.
|
||||
*/
|
||||
@NonNull private final String url;
|
||||
|
||||
/**
|
||||
* The model of this skin.
|
||||
*/
|
||||
@NonNull private final Model model;
|
||||
|
||||
/**
|
||||
* URLs to the parts of this skin.
|
||||
*/
|
||||
@NonNull @SerializedName("parts") private Map<String, String> partUrls = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Populate the part URLs for this skin.
|
||||
*
|
||||
* @param playerUuid the UUID of the player
|
||||
* @return the skin
|
||||
*/
|
||||
@NonNull
|
||||
public Skin populatePartUrls(@NonNull String playerUuid) {
|
||||
for (Part part : Part.values()) {
|
||||
partUrls.put(part.name(), AppConfig.INSTANCE.getServerPublicUrl() + "/player/" + part.name().toLowerCase() + "/" + playerUuid + ".png");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a skin from the given Json object.
|
||||
*
|
||||
* @param jsonObject the json object to build from
|
||||
* @return the built skin
|
||||
*/
|
||||
public static Skin fromJsonObject(JsonObject jsonObject) {
|
||||
if (jsonObject == null) { // No object to parse
|
||||
return null;
|
||||
}
|
||||
Model model = Model.DEFAULT; // The skin model
|
||||
|
||||
JsonObject metadataJsonObject = jsonObject.getAsJsonObject("metadata");
|
||||
if (metadataJsonObject != null) { // Parse the skin model
|
||||
model = Model.valueOf(metadataJsonObject.get("model").getAsString().toUpperCase());
|
||||
}
|
||||
return new Skin(jsonObject.get("url").getAsString(), model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible models for a skin.
|
||||
*/
|
||||
public enum Model {
|
||||
SLIM, DEFAULT
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of a skin.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public enum Part {
|
||||
HEAD(8, 8, ImageUtils.SKIN_TEXTURE_SIZE / 8, ImageUtils.SKIN_TEXTURE_SIZE / 8);
|
||||
|
||||
/**
|
||||
* The coordinates of this part.
|
||||
*/
|
||||
private final int x, y;
|
||||
|
||||
/**
|
||||
* The size of this part.
|
||||
*/
|
||||
private final int width, height;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.cache;
|
||||
|
||||
import lombok.*;
|
||||
import me.braydon.mc.model.MinecraftServer;
|
||||
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 private transient final String id;
|
||||
|
||||
/**
|
||||
* The cached server.
|
||||
*/
|
||||
@NonNull private final MinecraftServer value;
|
||||
|
||||
/**
|
||||
* The unix timestamp of when this
|
||||
* server was cached, -1 if not cached.
|
||||
*/
|
||||
private long cached;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.cache;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import me.braydon.mc.model.Cape;
|
||||
import me.braydon.mc.model.Player;
|
||||
import me.braydon.mc.model.ProfileAction;
|
||||
import me.braydon.mc.model.Skin;
|
||||
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(@NonNull UUID uniqueId, @NonNull String username,
|
||||
@NonNull Skin skin, Cape cape, ProfileAction[] profileActions, long cached) {
|
||||
super(uniqueId, username, skin, cape, profileActions);
|
||||
this.cached = cached;
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.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;
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.response;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* A response representing an error.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Getter @ToString
|
||||
public final 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();
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.server;
|
||||
|
||||
import lombok.*;
|
||||
import me.braydon.mc.model.MinecraftServer;
|
||||
|
||||
/**
|
||||
* 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 uniqueId;
|
||||
|
||||
/**
|
||||
* 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 uniqueId, @NonNull String hostname, String ip, int port,
|
||||
@NonNull Edition edition, @NonNull Version version, @NonNull Players players,
|
||||
@NonNull MOTD motd, @NonNull GameMode gamemode) {
|
||||
super(hostname, ip, port, players, motd);
|
||||
this.uniqueId = uniqueId;
|
||||
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, @NonNull String token) {
|
||||
String[] split = token.split(";"); // Split the token
|
||||
|
||||
Edition edition;
|
||||
try {
|
||||
edition = Edition.valueOf(split[0]);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Funky fix, but sometimes the edition can contain an extra
|
||||
// portion of the length of the edition, so we need to remove it
|
||||
edition = Edition.valueOf(split[0].substring(1));
|
||||
}
|
||||
|
||||
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, 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;
|
||||
}
|
||||
}
|
@ -1,224 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.server;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.*;
|
||||
import me.braydon.mc.RESTfulMC;
|
||||
import me.braydon.mc.common.JavaMinecraftVersion;
|
||||
import me.braydon.mc.config.AppConfig;
|
||||
import me.braydon.mc.model.MinecraftServer;
|
||||
import me.braydon.mc.model.token.JavaServerStatusToken;
|
||||
import me.braydon.mc.service.MojangService;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
/**
|
||||
* A Java edition {@link MinecraftServer}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Setter @Getter @ToString(callSuper = true)
|
||||
public final class JavaMinecraftServer extends MinecraftServer {
|
||||
/**
|
||||
* The version information of this server.
|
||||
*/
|
||||
@NonNull private final Version version;
|
||||
|
||||
/**
|
||||
* The favicon of this server, null if none.
|
||||
*/
|
||||
private final Favicon favicon;
|
||||
|
||||
/**
|
||||
* The Forge mod information for this server, null if none.
|
||||
*/
|
||||
private final ModInfo modInfo;
|
||||
|
||||
/**
|
||||
* Does this server enforce secure chat?
|
||||
*/
|
||||
private final boolean enforcesSecureChat;
|
||||
|
||||
/**
|
||||
* Is this server preventing chat reports?
|
||||
*/
|
||||
private final boolean preventsChatReports;
|
||||
|
||||
/**
|
||||
* Is this server on the list
|
||||
* of blocked servers by Mojang?
|
||||
* <p>
|
||||
* This value is later set by the
|
||||
* {@link MojangService} when a server
|
||||
* is requested.
|
||||
* </p>
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Mojang_API#Blocked_Servers">Mojang API</a>
|
||||
*/
|
||||
private boolean mojangBanned;
|
||||
|
||||
private JavaMinecraftServer(@NonNull String hostname, String ip, int port, @NonNull Version version,
|
||||
@NonNull Players players, @NonNull MOTD motd, Favicon favicon, ModInfo modInfo,
|
||||
boolean enforcesSecureChat, boolean preventsChatReports, boolean mojangBanned) {
|
||||
super(hostname, ip, port, players, motd);
|
||||
this.version = version;
|
||||
this.favicon = favicon;
|
||||
this.modInfo = modInfo;
|
||||
this.enforcesSecureChat = enforcesSecureChat;
|
||||
this.preventsChatReports = preventsChatReports;
|
||||
this.mojangBanned = mojangBanned;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, @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(RESTfulMC.GSON.toJson(token.getDescription()))).toLegacyText();
|
||||
}
|
||||
return new JavaMinecraftServer(hostname, ip, port, token.getVersion().detailedCopy(), token.getPlayers(),
|
||||
MOTD.create(motdString), Favicon.create(token.getFavicon(), hostname), token.getModInfo(),
|
||||
token.isEnforcesSecureChat(), token.isPreventsChatReports(), false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class Version {
|
||||
/**
|
||||
* The version name of the server.
|
||||
*/
|
||||
@NonNull private final String name;
|
||||
|
||||
/**
|
||||
* The identified platform of the server, null if unknown.
|
||||
*/
|
||||
private String platform;
|
||||
|
||||
/**
|
||||
* The protocol version of the server.
|
||||
*/
|
||||
private final int protocol;
|
||||
|
||||
/**
|
||||
* The name of the version for the protocol, null if unknown.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The favicon for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class Favicon {
|
||||
/**
|
||||
* The raw Base64 encoded favicon.
|
||||
*/
|
||||
@NonNull private final String base64;
|
||||
|
||||
/**
|
||||
* The URL to the favicon.
|
||||
*/
|
||||
@NonNull private final String url;
|
||||
|
||||
/**
|
||||
* Create a new favicon for a server.
|
||||
*
|
||||
* @param base64 the Base64 encoded favicon
|
||||
* @param hostname the server hostname
|
||||
* @return the favicon, null if none
|
||||
*/
|
||||
public static Favicon create(String base64, @NonNull String hostname) {
|
||||
if (base64 == null) { // No favicon to create
|
||||
return null;
|
||||
}
|
||||
return new Favicon(
|
||||
base64,
|
||||
AppConfig.INSTANCE.getServerPublicUrl() + "/server/icon/" + hostname
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forge mod information for a server.
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public static class ModInfo {
|
||||
/**
|
||||
* 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 id;
|
||||
|
||||
/**
|
||||
* The version of this mod.
|
||||
*/
|
||||
private final String version;
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.token;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import me.braydon.mc.model.MinecraftServer;
|
||||
import me.braydon.mc.model.server.JavaMinecraftServer;
|
||||
|
||||
/**
|
||||
* A token representing the response from
|
||||
* pinging a {@link JavaMinecraftServer}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public final class JavaServerStatusToken {
|
||||
/**
|
||||
* The description (MOTD) of this server.
|
||||
* <p>
|
||||
* Legacy: String, New: JSON Object
|
||||
* </p>
|
||||
*/
|
||||
@NonNull private final Object description;
|
||||
|
||||
/**
|
||||
* The base64 encoded favicon of this server, null if no favicon.
|
||||
*/
|
||||
private final String favicon;
|
||||
|
||||
/**
|
||||
* The version information of this server.
|
||||
*/
|
||||
@NonNull private final JavaMinecraftServer.Version version;
|
||||
|
||||
/**
|
||||
* The player counts of this server.
|
||||
*/
|
||||
@NonNull private final MinecraftServer.Players players;
|
||||
|
||||
/**
|
||||
* The Forge mod information for this server, null if none.
|
||||
*/
|
||||
@SerializedName("modinfo") private final JavaMinecraftServer.ModInfo modInfo;
|
||||
|
||||
/**
|
||||
* Does this server enforce secure chat?
|
||||
*/
|
||||
private final boolean enforcesSecureChat;
|
||||
|
||||
/**
|
||||
* Is this server preventing chat reports?
|
||||
*/
|
||||
private final boolean preventsChatReports;
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.token;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.*;
|
||||
import me.braydon.mc.RESTfulMC;
|
||||
import me.braydon.mc.common.Tuple;
|
||||
import me.braydon.mc.model.Cape;
|
||||
import me.braydon.mc.model.ProfileAction;
|
||||
import me.braydon.mc.model.Skin;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* A token representing a Mojang user profile.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Mojang_API#UUID_to_Profile_and_Skin.2FCape">Mojang API</a>
|
||||
*/
|
||||
@AllArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
|
||||
public final class MojangProfileToken {
|
||||
/**
|
||||
* The id of the profile.
|
||||
*/
|
||||
@EqualsAndHashCode.Include @NonNull private final String id;
|
||||
|
||||
/**
|
||||
* The name of the profile.
|
||||
*/
|
||||
@NonNull private final String name;
|
||||
|
||||
/**
|
||||
* The properties of the profile.
|
||||
*/
|
||||
@NonNull private final ProfileProperty[] properties;
|
||||
|
||||
/**
|
||||
* The actions this profile has.
|
||||
*/
|
||||
@NonNull private final ProfileAction[] profileActions;
|
||||
|
||||
/**
|
||||
* Get the skin and cape of this profile.
|
||||
*
|
||||
* @return the skin and cape of this profile
|
||||
*/
|
||||
public Tuple<Skin, Cape> getSkinAndCape() {
|
||||
ProfileProperty textures = getPropertyByName("textures"); // Get the profile textures
|
||||
if (textures == null) { // No profile textures
|
||||
return new Tuple<>();
|
||||
}
|
||||
JsonObject jsonObject = RESTfulMC.GSON.fromJson(textures.getDecodedValue(), JsonObject.class); // Get the Json object
|
||||
JsonObject texturesJsonObject = jsonObject.getAsJsonObject("textures"); // Get the textures object
|
||||
|
||||
// Return the tuple containing the skin and cape
|
||||
return new Tuple<>(
|
||||
Skin.fromJsonObject(texturesJsonObject.getAsJsonObject("SKIN")).populatePartUrls(id),
|
||||
Cape.fromJsonObject(texturesJsonObject.getAsJsonObject("CAPE"))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the profile property
|
||||
* with the given name.
|
||||
*
|
||||
* @param name the property name
|
||||
* @return the profile property, null if none
|
||||
*/
|
||||
public ProfileProperty getPropertyByName(@NonNull String name) {
|
||||
for (ProfileProperty property : properties) {
|
||||
if (property.getName().equalsIgnoreCase(name)) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A property of a Mojang profile.
|
||||
*/
|
||||
@NoArgsConstructor @Setter @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
|
||||
public static class ProfileProperty {
|
||||
/**
|
||||
* The name of this property.
|
||||
*/
|
||||
@EqualsAndHashCode.Include @NonNull private String name;
|
||||
|
||||
/**
|
||||
* The base64 value of this property.
|
||||
*/
|
||||
@NonNull private String value;
|
||||
|
||||
/**
|
||||
* The base64 signature of this property.
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* Get the decoded Base64
|
||||
* value of this property.
|
||||
*
|
||||
* @return the decoded value
|
||||
*/
|
||||
@NonNull
|
||||
public String getDecodedValue() {
|
||||
return new String(Base64.getDecoder().decode(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this property signed?
|
||||
*
|
||||
* @return whether this property has a signature
|
||||
*/
|
||||
public boolean isSigned() {
|
||||
return signature != null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.model.token;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* A token representing a Mojang username to UUID response.
|
||||
*
|
||||
* @author Braydon
|
||||
* @see <a href="https://wiki.vg/Mojang_API#Username_to_UUID">Mojang API</a>
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public final class MojangUsernameToUUIDToken {
|
||||
/**
|
||||
* The id of the username.
|
||||
*/
|
||||
@NonNull private final String id;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.repository;
|
||||
|
||||
import me.braydon.mc.model.cache.CachedMinecraftServer;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* A cache repository for {@link CachedMinecraftServer}'s.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
public interface MinecraftServerCacheRepository extends CrudRepository<CachedMinecraftServer, String> { }
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.repository;
|
||||
|
||||
import me.braydon.mc.model.cache.CachedPlayer;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A cache repository for {@link CachedPlayer}'s.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
public interface PlayerCacheRepository extends CrudRepository<CachedPlayer, UUID> { }
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.repository;
|
||||
|
||||
import me.braydon.mc.model.cache.CachedPlayerName;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* A cache repository for player usernames.
|
||||
* <p>
|
||||
* This will allow us to easily lookup a
|
||||
* player's username and get their uuid.
|
||||
* </p>
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
public interface PlayerNameCacheRepository extends CrudRepository<CachedPlayerName, String> { }
|
@ -1,479 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.service;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.hash.Hashing;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import me.braydon.mc.common.*;
|
||||
import me.braydon.mc.common.web.JsonWebException;
|
||||
import me.braydon.mc.common.web.JsonWebRequest;
|
||||
import me.braydon.mc.exception.impl.BadRequestException;
|
||||
import me.braydon.mc.exception.impl.MojangRateLimitException;
|
||||
import me.braydon.mc.exception.impl.ResourceNotFoundException;
|
||||
import me.braydon.mc.model.*;
|
||||
import me.braydon.mc.model.cache.CachedMinecraftServer;
|
||||
import me.braydon.mc.model.cache.CachedPlayer;
|
||||
import me.braydon.mc.model.cache.CachedPlayerName;
|
||||
import me.braydon.mc.model.server.JavaMinecraftServer;
|
||||
import me.braydon.mc.model.token.MojangProfileToken;
|
||||
import me.braydon.mc.model.token.MojangUsernameToUUIDToken;
|
||||
import me.braydon.mc.repository.MinecraftServerCacheRepository;
|
||||
import me.braydon.mc.repository.PlayerCacheRepository;
|
||||
import me.braydon.mc.repository.PlayerNameCacheRepository;
|
||||
import net.jodah.expiringmap.ExpirationPolicy;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A service for interacting with the Mojang API.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Service
|
||||
@Log4j2(topic = "Mojang Service")
|
||||
public final class MojangService {
|
||||
private static final String SESSION_SERVER_ENDPOINT = "https://sessionserver.mojang.com";
|
||||
private static final String API_ENDPOINT = "https://api.mojang.com";
|
||||
private static final String UUID_TO_PROFILE = SESSION_SERVER_ENDPOINT + "/session/minecraft/profile/%s";
|
||||
private static final String USERNAME_TO_UUID = API_ENDPOINT + "/users/profiles/minecraft/%s";
|
||||
private static final String FETCH_BLOCKED_SERVERS = SESSION_SERVER_ENDPOINT + "/blockedservers";
|
||||
|
||||
private static final int DEFAULT_PART_TEXTURE_SIZE = 128;
|
||||
private static final int MAX_PART_TEXTURE_SIZE = 512;
|
||||
|
||||
private static final String DEFAULT_SERVER_ICON = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAASFBMVEWwsLBBQUE9PT1JSUlFRUUuLi5MTEyzs7M0NDQ5OTlVVVVQUFAmJia5ubl+fn5zc3PFxcVdXV3AwMCJiYmUlJRmZmbQ0NCjo6OL5p+6AAAFVklEQVRYw+1W67K0KAzkJnIZdRAZ3/9NtzvgXM45dX7st1VbW7XBUVDSdEISRqn/5R+T82/+nsr/XZn/SHm/3x9/ArA/IP8qwPK433d44VubZ/XT6/cJy0L792VZfnDrcRznr86d748u92X5vtaxOe228zcCy+MSMpg/5SwRopsYMv8oigCwngbQhE/rzhwAYMpxnvMvHhgy/8AgByJolzb5pPqEbvtgMBBmtvkbgxKmaaIZ5TyPum6Viue6te241N+s+W6nOlucgjEx6Nay9zZta1XVxejW+Q5ZhhkDS31lgOTegjUBor33CQilbC2GYGy9y9bN8ytevjE4a2stajHDAgAcUkoYwzO6zQi8ZflC+XO0+exiuNa3OQtIJOCk13neUjv7VO7Asu/3LwDFeg37sQtQhy4lAQH6IR9ztca0E3oI5PtDAlJ1tHGplrJ12jjrrXPWYvXsU042Bl/qUr3B9qzPSKaovpvjgglYL2F1x+Zs7gIvpLYuq46wr3H5/RJxyvM6sXOY762oU4YZ3mAz1lpc9O3Y30VJUM/iWhBIib63II/LA4COEMxcSmrH4ddl/wTYe3RIO0vK2VI9wQy6AxRsJpb3AAALvXb6TxvUCYSdOQo5Mh0GySkJc7rB405GUEfzbbl/iFpPoNQVNUQAZG06nkI6RCABRqRA9IimH6Up5Mhybtu2IlewB2Sf6AmQ4ZU9rfBELvyA23Yub6LWWtUBgK3OB79L7FILLDKWd4wpxmMRAMoLQR1ItLoiWUmhFtjptab7LQDgRARliLITLrcBkHNp9VACUH1UDRQEYGuYxzyM9H0mBccQNnCkQ3Q1UHBaO6sNyw0CelEtBGXKSoE+fJWZh5GupyneMIkCOMESAniMAzMreLvuO+pnmBQSp4C+ELCiMSGVLPh7M023SSBAiAA5yPh2m0wigEbWKnw3qDrrscF00cciCATGwNQRAv2YGvyD4Y36QGhqOS4AcABAA88oGvBCRho5H2+UiW6EfyM1L5l8a56rqdvE6lFakc3ScVDOBNBUoFM8c1vgnhAG5VsAqMD6Q9IwwtAkR39iGEQF1ZBxgU+v9UGL6MBQYiTdJllIBtx5y0rixGdAZ1YysbS53TAVy3vf4aabEpt1T0HoB2Eg4Yv5OKNwyHgmNvPKaQAYLG3EIyIqcL6Fj5C2jhXL9EpCdRMROE5nCW3qm1vfR6wYh0HKGG3wY+JgLkUWQ/WMfI8oMvIWMY7aCncNxxpSmHRUCEzDdSR0+dRwIQaMWW1FE0AOGeKkx0OLwYanBK3qfC0BSmIlozkuFcvSkulckoIB2FbHWu0y9gMHsEapMMEoySNUA2RDrduxIqr5POQV2zZ++IBOwVrFO9THrtjU2uWsCMZjxXl88Hmeaz1rPdAqXyJl68F5RTtdvN1aIyYEAMAWJaCMHvon7s23jljlxoKBEgNv6LQ25/rZIQyOdwDO3jLsqE2nbVAil21LxqFpZ2xJ3CFuE33QCo7kfkfO8kpW6gdioxdzZDLOaMMwidzeKD0RxaD7cnHHsu0jVkW5oTwwMGI0lwwA36u2nMY8AKzErLW9JxFiteyzZsAAxY1vPe5Uf68lIDVjV8JZpPfjxbc/QuyRKdAQJaAdIA4tCTht+kQJ1I4nbdjfHxgpTSLyI19pb/iuK7+9YJaZCxEIKj79YZ6uDU8f97878teRN1FzA7OvquSrVKUgk+S6ROpJfA7GpN6RPkx4voshXgu91p7CGHeA+IY8dUUVXwT7PYw12Xsj0Lfh9X4ac9XgKW86cj8bPh8XmyDOD88FLoB+YPXp4YtyB3gBPXu98xeRI2zploVCBQAAAABJRU5ErkJggg==";
|
||||
|
||||
private static final Splitter DOT_SPLITTER = Splitter.on('.');
|
||||
private static final Joiner DOT_JOINER = Joiner.on('.');
|
||||
|
||||
/**
|
||||
* The cache repository for {@link Player}'s by their username.
|
||||
*/
|
||||
@NonNull private final PlayerNameCacheRepository playerNameCache;
|
||||
|
||||
/**
|
||||
* The cache repository for {@link Player}'s.
|
||||
*/
|
||||
@NonNull private final PlayerCacheRepository playerCache;
|
||||
|
||||
/**
|
||||
* The cache repository for {@link MinecraftServer}'s.
|
||||
*/
|
||||
@NonNull private final MinecraftServerCacheRepository minecraftServerCache;
|
||||
|
||||
/**
|
||||
* A list of banned server hashes provided by Mojang.
|
||||
* <p>
|
||||
* This is periodically fetched from Mojang, see
|
||||
* {@link #fetchBlockedServers()} for more info.
|
||||
* </p>
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Mojang_API#Blocked_Servers">Mojang API</a>
|
||||
*/
|
||||
private List<String> bannedServerHashes;
|
||||
|
||||
/**
|
||||
* A cache of blocked server hostnames.
|
||||
*
|
||||
* @see #isServerHostnameBlocked(String) for more
|
||||
*/
|
||||
private final ExpiringSet<String> blockedServersCache = new ExpiringSet<>(ExpirationPolicy.CREATED, 10L, TimeUnit.MINUTES);
|
||||
|
||||
@Autowired
|
||||
public MojangService(@NonNull PlayerNameCacheRepository playerNameCache, @NonNull PlayerCacheRepository playerCache,
|
||||
@NonNull MinecraftServerCacheRepository minecraftServerCache) {
|
||||
this.playerNameCache = playerNameCache;
|
||||
this.playerCache = playerCache;
|
||||
this.minecraftServerCache = minecraftServerCache;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void onInitialize() {
|
||||
// Schedule a task to fetch blocked
|
||||
// servers from Mojang every 15 minutes.
|
||||
new Timer().scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
fetchBlockedServers();
|
||||
}
|
||||
}, 0L, 60L * 15L * 1000L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the part of a skin texture for
|
||||
* a player by their username or UUID.
|
||||
*
|
||||
* @param partName the part of the player's skin texture to get
|
||||
* @param query the query to search for the player by
|
||||
* @param extension the skin part image extension
|
||||
* @param sizeString the size of the skin part image
|
||||
* @return the skin part texture
|
||||
* @throws BadRequestException if the extension is invalid
|
||||
* @throws MojangRateLimitException if the Mojang API rate limit is reached
|
||||
*/
|
||||
public byte[] getSkinPartTexture(@NonNull String partName, @NonNull String query, @NonNull String extension, String sizeString)
|
||||
throws BadRequestException, MojangRateLimitException {
|
||||
Integer size = null;
|
||||
if (sizeString != null) { // Attempt to parse the size
|
||||
try {
|
||||
size = Integer.parseInt(sizeString);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Safely ignore, invalid number provided
|
||||
}
|
||||
}
|
||||
Skin.Part part = EnumUtils.getEnumConstant(Skin.Part.class, partName.toUpperCase()); // The skin part to get
|
||||
if (part == null) { // Default to the head part
|
||||
part = Skin.Part.HEAD;
|
||||
}
|
||||
if (extension.isBlank()) { // Invalid extension
|
||||
throw new BadRequestException("Invalid extension");
|
||||
}
|
||||
if (size == null || size <= 0) { // Invalid size
|
||||
size = DEFAULT_PART_TEXTURE_SIZE;
|
||||
}
|
||||
size = Math.min(size, MAX_PART_TEXTURE_SIZE); // Limit the size to 512
|
||||
|
||||
Skin target = null; // The target skin to get the skin part of
|
||||
try {
|
||||
CachedPlayer player = getPlayer(query, false); // Retrieve the player
|
||||
target = player.getSkin(); // Use the player's skin
|
||||
} catch (Exception ignored) {
|
||||
// Simply ignore, and fallback to the default skin
|
||||
}
|
||||
if (target == null) { // Fallback to the default skin
|
||||
target = Skin.DEFAULT_STEVE;
|
||||
}
|
||||
return ImageUtils.getSkinPart(target, part, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a player by their username or UUID.
|
||||
* <p>
|
||||
* If the player is present within the cache, that will
|
||||
* be returned. If the player is not cached, a request
|
||||
* will be made to retrieve the player from Mojang, cache it
|
||||
* and then return the response.
|
||||
* </p>
|
||||
*
|
||||
* @param query the query to search for the player by
|
||||
* @param bypassCache should the cache be bypassed?
|
||||
* @return the player
|
||||
* @throws BadRequestException if the UUID or username is invalid
|
||||
* @throws ResourceNotFoundException if the player is not found
|
||||
* @throws MojangRateLimitException if the Mojang API rate limit is reached
|
||||
*/
|
||||
@NonNull
|
||||
public CachedPlayer getPlayer(@NonNull String query, boolean bypassCache)
|
||||
throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
|
||||
log.info("Requesting player with query: {}", query);
|
||||
|
||||
UUID uuid; // The player UUID to lookup
|
||||
boolean isFullUuid = query.length() == 36; // Was a UUID provided?
|
||||
if (query.length() == 32 || isFullUuid) { // Parse the query as a UUID
|
||||
try {
|
||||
uuid = isFullUuid ? UUID.fromString(query) : UUIDUtils.addDashes(query);
|
||||
log.info("Parsed {}UUID: {} -> {}", isFullUuid ? "" : "trimmed ", query, uuid);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new BadRequestException("Malformed UUID provided: %s".formatted(query));
|
||||
}
|
||||
} else { // The query is a username, request from Mojang
|
||||
if (!MiscUtils.isUsernameValid(query)) { // Ensure the username is valid
|
||||
throw new BadRequestException("Invalid username provided: %s".formatted(query));
|
||||
}
|
||||
uuid = usernameToUUID(query);
|
||||
log.info("Found UUID for username {}: {}", query, uuid);
|
||||
}
|
||||
|
||||
// Check the cache for the player
|
||||
if (!bypassCache) {
|
||||
Optional<CachedPlayer> cached = playerCache.findById(uuid);
|
||||
if (cached.isPresent()) { // Respond with the cache if present
|
||||
log.info("Found player in cache: {}", uuid);
|
||||
return cached.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Send a request to Mojang requesting
|
||||
// the player profile by their UUID
|
||||
try {
|
||||
log.info("Retrieving player profile for UUID: {}", uuid);
|
||||
MojangProfileToken token = JsonWebRequest.makeRequest(
|
||||
UUID_TO_PROFILE.formatted(uuid), HttpMethod.GET
|
||||
).execute(MojangProfileToken.class);
|
||||
Tuple<Skin, Cape> skinAndCape = token.getSkinAndCape(); // Get the skin and cape
|
||||
ProfileAction[] profileActions = token.getProfileActions();
|
||||
|
||||
// Build our player model, cache it, and then return it
|
||||
CachedPlayer player = new CachedPlayer(
|
||||
uuid, token.getName(),
|
||||
skinAndCape.getLeft() == null ? Skin.DEFAULT_STEVE : skinAndCape.getLeft(),
|
||||
skinAndCape.getRight(),
|
||||
profileActions.length == 0 ? null : profileActions,
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
if (!bypassCache) { // Store in the cache
|
||||
playerCache.save(player);
|
||||
log.info("Cached player: {}", uuid);
|
||||
}
|
||||
|
||||
player.setCached(-1L); // Set to -1 to indicate it's not cached in the response
|
||||
return player;
|
||||
} catch (JsonWebException ex) {
|
||||
// No profile found, return null
|
||||
if (ex.getStatusCode() == 400) {
|
||||
throw new ResourceNotFoundException("Player not found with query: %s".formatted(query));
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the favicon of a Java
|
||||
* server with the given hostname.
|
||||
* <p>
|
||||
* If the favicon of the server cannot be
|
||||
* retrieved, the default icon will be used.
|
||||
* </p>
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param port the port of the server, null for default
|
||||
* @return the server favicon
|
||||
* @see #DEFAULT_SERVER_ICON for the default server icon
|
||||
*/
|
||||
public byte[] getServerFavicon(@NonNull String hostname, String port) {
|
||||
String icon = null; // The server base64 icon
|
||||
try {
|
||||
JavaMinecraftServer.Favicon favicon = ((JavaMinecraftServer) getMinecraftServer(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
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the server with the
|
||||
* given hostname is blocked by Mojang.
|
||||
*
|
||||
* @param hostname the server hostname to check
|
||||
* @return whether the hostname is blocked
|
||||
*/
|
||||
public boolean isServerBlocked(@NonNull String hostname) {
|
||||
// Remove trailing dots
|
||||
while (hostname.charAt(hostname.length() - 1) == '.') {
|
||||
hostname = hostname.substring(0, hostname.length() - 1);
|
||||
}
|
||||
// Is the hostname banned?
|
||||
if (isServerHostnameBlocked(hostname)) {
|
||||
return true;
|
||||
}
|
||||
List<String> splitDots = Lists.newArrayList(DOT_SPLITTER.split(hostname)); // Split the hostname by dots
|
||||
boolean isIp = splitDots.size() == 4; // Is it an IP address?
|
||||
if (isIp) {
|
||||
for (String element : splitDots) {
|
||||
try {
|
||||
int part = Integer.parseInt(element);
|
||||
if (part >= 0 && part <= 255) { // Ensure the part is within the valid range
|
||||
continue;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Safely ignore, not a number
|
||||
}
|
||||
isIp = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Check if the hostname is blocked
|
||||
if (!isIp && isServerHostnameBlocked("*." + hostname)) {
|
||||
return true;
|
||||
}
|
||||
// Additional checks for the hostname
|
||||
while (splitDots.size() > 1) {
|
||||
splitDots.remove(isIp ? splitDots.size() - 1 : 0);
|
||||
String starredPart = isIp ? DOT_JOINER.join(splitDots) + ".*" : "*." + DOT_JOINER.join(splitDots);
|
||||
if (isServerHostnameBlocked(starredPart)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a Minecraft server on the given
|
||||
* platform with the given hostname.
|
||||
*
|
||||
* @param platformName the name of the platform
|
||||
* @param hostname the hostname of the server
|
||||
* @param portString the port of the server, null for default
|
||||
* @return the resolved Minecraft server
|
||||
* @throws BadRequestException if the hostname, platform, or port is invalid
|
||||
* @throws ResourceNotFoundException if the server isn't found
|
||||
*/
|
||||
@NonNull
|
||||
public CachedMinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname, String portString)
|
||||
throws BadRequestException, ResourceNotFoundException {
|
||||
MinecraftServer.Platform platform = EnumUtils.getEnumConstant(MinecraftServer.Platform.class, platformName.toUpperCase());
|
||||
if (platform == null) { // Invalid platform
|
||||
throw new BadRequestException("Invalid platform: %s".formatted(platformName));
|
||||
}
|
||||
String lookupHostname = hostname; // The hostname used to lookup the server
|
||||
|
||||
int port = platform.getDefaultPort(); // Port to ping
|
||||
if (portString != null) {
|
||||
try { // Try and parse the port
|
||||
port = Integer.parseInt(portString);
|
||||
} catch (NumberFormatException ex) { // Invalid port
|
||||
throw new BadRequestException("Invalid port defined");
|
||||
}
|
||||
}
|
||||
|
||||
// Check the cache for the server
|
||||
Optional<CachedMinecraftServer> cached = minecraftServerCache.findById(platform.name() + "-" + hostname + "-" + port);
|
||||
if (cached.isPresent()) { // Respond with the cache if present
|
||||
log.info("Found server in cache: {}", hostname);
|
||||
return cached.get();
|
||||
}
|
||||
InetSocketAddress address = platform == MinecraftServer.Platform.JAVA ? DNSUtils.resolveSRV(hostname) : null; // Resolve the SRV record
|
||||
if (address != null) { // SRV was resolved, use the hostname and port
|
||||
hostname = address.getHostName();
|
||||
port = address.getPort();
|
||||
}
|
||||
|
||||
// Build our server model, cache it, and then return it
|
||||
CachedMinecraftServer minecraftServer = new CachedMinecraftServer(
|
||||
platform.name() + "-" + lookupHostname + "-" + (portString == null ? port : portString),
|
||||
platform.getPinger().ping(hostname, port),
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
|
||||
// Get the blocked status of the Java server
|
||||
if (platform == MinecraftServer.Platform.JAVA) {
|
||||
((JavaMinecraftServer) minecraftServer.getValue()).setMojangBanned(isServerBlocked(hostname));
|
||||
}
|
||||
|
||||
minecraftServerCache.save(minecraftServer);
|
||||
log.info("Cached server: {}", hostname);
|
||||
minecraftServer.setCached(-1L); // Set to -1 to indicate it's not cached in the response
|
||||
return minecraftServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UUID of a player by their username.
|
||||
*
|
||||
* @param username the player's username
|
||||
* @return the player's UUID
|
||||
* @throws ResourceNotFoundException if the player isn't found
|
||||
* @throws MojangRateLimitException if the Mojang rate limit is reached
|
||||
*/
|
||||
@NonNull
|
||||
private UUID usernameToUUID(@NonNull String username) throws ResourceNotFoundException, MojangRateLimitException {
|
||||
String originalUsername = username;
|
||||
username = username.toLowerCase(); // Lowercase the username
|
||||
|
||||
// Check the cache for the player's UUID
|
||||
Optional<CachedPlayerName> cached = playerNameCache.findById(username);
|
||||
if (cached.isPresent()) { // Respond with the cache if present
|
||||
log.info("Found UUID in cache for username {}: {}", originalUsername, cached.get().getUniqueId());
|
||||
return cached.get().getUniqueId();
|
||||
}
|
||||
|
||||
// Make a request to Mojang requesting the UUID
|
||||
try {
|
||||
MojangUsernameToUUIDToken token = JsonWebRequest.makeRequest(
|
||||
USERNAME_TO_UUID.formatted(username), HttpMethod.GET
|
||||
).execute(MojangUsernameToUUIDToken.class);
|
||||
|
||||
// Cache the UUID and return it
|
||||
UUID uuid = UUIDUtils.addDashes(token.getId());
|
||||
playerNameCache.save(new CachedPlayerName(username, uuid));
|
||||
log.info("Cached UUID for username {}: {}", username, uuid);
|
||||
return uuid;
|
||||
} catch (JsonWebException ex) {
|
||||
if (ex.getStatusCode() == 429) { // Mojang rate limit reached
|
||||
throw new MojangRateLimitException();
|
||||
} else if (ex.getStatusCode() == 404) { // Player not found
|
||||
throw new ResourceNotFoundException("Player not found with username: %s".formatted(originalUsername));
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of blocked servers from Mojang.
|
||||
*/
|
||||
@SneakyThrows
|
||||
private void fetchBlockedServers() {
|
||||
try (
|
||||
InputStream inputStream = new URL(FETCH_BLOCKED_SERVERS).openStream();
|
||||
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8).useDelimiter("\n");
|
||||
) {
|
||||
List<String> hashes = new ArrayList<>();
|
||||
while (scanner.hasNext()) {
|
||||
hashes.add(scanner.next());
|
||||
}
|
||||
bannedServerHashes = Collections.synchronizedList(hashes);
|
||||
log.info("Fetched {} banned server hashes", bannedServerHashes.size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the hash for the given
|
||||
* hostname is in the blocked server list.
|
||||
*
|
||||
* @param hostname the hostname to check
|
||||
* @return whether the hostname is blocked
|
||||
*/
|
||||
private boolean isServerHostnameBlocked(@NonNull String hostname) {
|
||||
// Check the cache first for the hostname
|
||||
if (blockedServersCache.contains(hostname)) {
|
||||
return true;
|
||||
}
|
||||
String hashed = Hashing.sha1().hashBytes(hostname.toLowerCase().getBytes(StandardCharsets.ISO_8859_1)).toString();
|
||||
boolean blocked = bannedServerHashes.contains(hashed); // Is the hostname blocked?
|
||||
if (blocked) { // Cache the blocked hostname
|
||||
blockedServersCache.add(hostname);
|
||||
}
|
||||
return blocked;
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.service.pinger;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.mc.model.MinecraftServer;
|
||||
|
||||
/**
|
||||
* A {@link MinecraftServerPinger} is
|
||||
* used to ping a {@link MinecraftServer}.
|
||||
*
|
||||
* @author Braydon
|
||||
* @param <T> the type of server to ping
|
||||
*/
|
||||
public interface MinecraftServerPinger<T extends MinecraftServer> {
|
||||
/**
|
||||
* Ping the server with the given hostname and port.
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param port the port of the server
|
||||
* @return the server that was pinged
|
||||
*/
|
||||
T ping(@NonNull String hostname, int port);
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.service.pinger.impl;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import me.braydon.mc.common.DNSUtils;
|
||||
import me.braydon.mc.common.packet.impl.bedrock.BedrockPacketUnconnectedPing;
|
||||
import me.braydon.mc.common.packet.impl.bedrock.BedrockPacketUnconnectedPong;
|
||||
import me.braydon.mc.exception.impl.BadRequestException;
|
||||
import me.braydon.mc.exception.impl.ResourceNotFoundException;
|
||||
import me.braydon.mc.model.server.BedrockMinecraftServer;
|
||||
import me.braydon.mc.service.pinger.MinecraftServerPinger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
* The {@link MinecraftServerPinger} for pinging
|
||||
* {@link BedrockMinecraftServer} over UDP.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Log4j2(topic = "Bedrock MC Server Pinger")
|
||||
public final class BedrockMinecraftServerPinger implements MinecraftServerPinger<BedrockMinecraftServer> {
|
||||
private static final int TIMEOUT = 3000; // The timeout for the socket
|
||||
|
||||
/**
|
||||
* Ping the server with the given hostname and port.
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param port the port of the server
|
||||
* @return the server that was pinged
|
||||
*/
|
||||
@Override
|
||||
public BedrockMinecraftServer ping(@NonNull String hostname, int port) {
|
||||
InetAddress inetAddress = DNSUtils.resolveA(hostname); // Resolve the hostname to an IP address
|
||||
String ip = inetAddress == null ? null : inetAddress.getHostAddress(); // Get the IP address
|
||||
if (ip != null) { // Was the IP resolved?
|
||||
log.info("Resolved hostname: {} -> {}", hostname, ip);
|
||||
}
|
||||
log.info("Pinging {}:{}...", hostname, port);
|
||||
long before = System.currentTimeMillis(); // Timestamp before pinging
|
||||
|
||||
// Open a socket connection to the server
|
||||
try (DatagramSocket socket = new DatagramSocket()) {
|
||||
socket.setSoTimeout(TIMEOUT);
|
||||
socket.connect(new InetSocketAddress(hostname, port));
|
||||
|
||||
long ping = System.currentTimeMillis() - before; // Calculate the ping
|
||||
log.info("Pinged {}:{} in {}ms", hostname, port, ping);
|
||||
|
||||
// Send the unconnected ping packet
|
||||
new BedrockPacketUnconnectedPing().process(socket);
|
||||
|
||||
// Handle the received unconnected pong packet
|
||||
BedrockPacketUnconnectedPong unconnectedPong = new BedrockPacketUnconnectedPong();
|
||||
unconnectedPong.process(socket);
|
||||
String response = unconnectedPong.getResponse();
|
||||
if (response == null) { // No pong response
|
||||
throw new ResourceNotFoundException("Server didn't respond to ping");
|
||||
}
|
||||
return BedrockMinecraftServer.create(hostname, ip, port, response); // Return the server
|
||||
} catch (IOException ex) {
|
||||
if (ex instanceof UnknownHostException) {
|
||||
throw new BadRequestException("Unknown hostname: %s".formatted(hostname));
|
||||
} else if (ex instanceof SocketTimeoutException) {
|
||||
throw new ResourceNotFoundException(ex);
|
||||
}
|
||||
log.error("An error occurred pinging %s:%s:".formatted(hostname, port), ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.service.pinger.impl;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import me.braydon.mc.RESTfulMC;
|
||||
import me.braydon.mc.common.DNSUtils;
|
||||
import me.braydon.mc.common.packet.impl.java.JavaPacketHandshakingInSetProtocol;
|
||||
import me.braydon.mc.common.packet.impl.java.JavaPacketStatusInStart;
|
||||
import me.braydon.mc.exception.impl.BadRequestException;
|
||||
import me.braydon.mc.exception.impl.ResourceNotFoundException;
|
||||
import me.braydon.mc.model.server.JavaMinecraftServer;
|
||||
import me.braydon.mc.model.token.JavaServerStatusToken;
|
||||
import me.braydon.mc.service.pinger.MinecraftServerPinger;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
* The {@link MinecraftServerPinger} for pinging
|
||||
* {@link JavaMinecraftServer}'s over TCP.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Log4j2(topic = "Java MC Server Pinger")
|
||||
public final class JavaMinecraftServerPinger implements MinecraftServerPinger<JavaMinecraftServer> {
|
||||
private static final int TIMEOUT = 3000; // The timeout for the socket
|
||||
|
||||
/**
|
||||
* Ping the server with the given hostname and port.
|
||||
*
|
||||
* @param hostname the hostname of the server
|
||||
* @param port the port of the server
|
||||
* @return the server that was pinged
|
||||
* @throws BadRequestException if the hostname is unknown
|
||||
* @throws ResourceNotFoundException if the server could not be found
|
||||
*/
|
||||
@Override
|
||||
public JavaMinecraftServer ping(@NonNull String hostname, int port) throws BadRequestException, ResourceNotFoundException {
|
||||
InetAddress inetAddress = DNSUtils.resolveA(hostname); // Resolve the hostname to an IP address
|
||||
String ip = inetAddress == null ? null : inetAddress.getHostAddress(); // Get the IP address
|
||||
if (ip != null) { // Was the IP resolved?
|
||||
log.info("Resolved hostname: {} -> {}", hostname, ip);
|
||||
}
|
||||
log.info("Pinging {}:{}...", hostname, port);
|
||||
long before = System.currentTimeMillis(); // Timestamp before pinging
|
||||
|
||||
// Open a socket connection to the server
|
||||
try (Socket socket = new Socket()) {
|
||||
socket.setTcpNoDelay(true);
|
||||
socket.connect(new InetSocketAddress(hostname, port), TIMEOUT);
|
||||
|
||||
long ping = System.currentTimeMillis() - before; // Calculate the ping
|
||||
log.info("Pinged {}:{} in {}ms", hostname, port, ping);
|
||||
|
||||
// Open data streams to begin packet transaction
|
||||
try (DataInputStream inputStream = new DataInputStream(socket.getInputStream());
|
||||
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream())) {
|
||||
// Begin handshaking with the server
|
||||
new JavaPacketHandshakingInSetProtocol(hostname, port, 47).process(inputStream, outputStream);
|
||||
|
||||
// Send the status request to the server, and await back the response
|
||||
JavaPacketStatusInStart packetStatusInStart = new JavaPacketStatusInStart();
|
||||
packetStatusInStart.process(inputStream, outputStream);
|
||||
JavaServerStatusToken token = RESTfulMC.GSON.fromJson(packetStatusInStart.getResponse(), JavaServerStatusToken.class);
|
||||
return JavaMinecraftServer.create(hostname, ip, port, token); // Return the server
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (ex instanceof UnknownHostException) {
|
||||
throw new BadRequestException("Unknown hostname: %s".formatted(hostname));
|
||||
} else if (ex instanceof ConnectException || ex instanceof SocketTimeoutException) {
|
||||
throw new ResourceNotFoundException(ex);
|
||||
}
|
||||
log.error("An error occurred pinging %s:%s:".formatted(hostname, port), ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
# Server Configuration
|
||||
server:
|
||||
address: 0.0.0.0
|
||||
port: 7500
|
||||
publicUrl: "http://localhost:7500" # The publicly accessible URL for this app
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
# Log Configuration
|
||||
logging:
|
||||
file:
|
||||
path: "./logs"
|
||||
|
||||
# Spring Configuration
|
||||
spring:
|
||||
data:
|
||||
# Redis - This is used for caching
|
||||
redis:
|
||||
host: "localhost"
|
||||
port: 6379
|
||||
database: 0
|
||||
auth: "" # Leave blank for no auth
|
||||
|
||||
# Ignore
|
||||
application:
|
||||
name: "RESTfulMC"
|
||||
banner:
|
||||
location: "classpath:banner.txt"
|
@ -1,10 +0,0 @@
|
||||
_____ ______ _____ _______ __ _ __ __ _____
|
||||
| __ \| ____|/ ____|__ __/ _| | | \/ |/ ____|
|
||||
| |__) | |__ | (___ | | | |_ _ _| | \ / | |
|
||||
| _ /| __| \___ \ | | | _| | | | | |\/| | |
|
||||
| | \ \| |____ ____) | | | | | | |_| | | | | | |____
|
||||
|_| \_\______|_____/ |_| |_| \__,_|_|_| |_|\_____|
|
||||
|
||||
| API Version - v${application.version}
|
||||
| Spring Version - ${spring-boot.formatted-version}
|
||||
_______________________________________
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
@ -1,15 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="A simple, yet useful RESTful API for Minecraft utilizing Springboot.">
|
||||
<meta name="theme-color" content="#3A34EB">
|
||||
<title>RESTfulMC</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>RESTfulMC</h1>
|
||||
<p>A simple, yet useful RESTful API for Minecraft utilizing Springboot.</p>
|
||||
<p>View the source <a href="https://git.rainnny.club/Rainnny/RESTfulMC">here</a>!</p>
|
||||
</body>
|
||||
</html>
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.test.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.NonNull;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import redis.embedded.RedisServer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Test configuration for
|
||||
* a mock Redis server.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@TestConfiguration
|
||||
public class TestRedisConfig {
|
||||
@NonNull private final RedisServer server;
|
||||
|
||||
public TestRedisConfig() throws IOException {
|
||||
server = new RedisServer(); // Construct the mock server
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up the mock Redis server.
|
||||
*
|
||||
* @throws IOException if there was an issue starting the server
|
||||
*/
|
||||
@PostConstruct
|
||||
public void onInitialize() throws IOException {
|
||||
server.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the running mock Redis server.
|
||||
*
|
||||
* @throws IOException if there was an issue stopping the server
|
||||
*/
|
||||
@PreDestroy
|
||||
public void housekeeping() throws IOException {
|
||||
server.stop();
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.test.controller;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.mc.controller.PlayerController;
|
||||
import me.braydon.mc.test.config.TestRedisConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for the {@link PlayerController}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@SpringBootTest(classes = TestRedisConfig.class)
|
||||
@AutoConfigureMockMvc
|
||||
public final class PlayerControllerTests {
|
||||
/**
|
||||
* The {@link MockMvc} instance to use for testing.
|
||||
*/
|
||||
@NonNull private final MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
public PlayerControllerTests(@NonNull MockMvc mockMvc) {
|
||||
this.mockMvc = mockMvc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure retrieving
|
||||
* a player's data is successful.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensurePlayerLookupSuccess() throws Exception {
|
||||
mockMvc.perform(get("/player/g")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isOk()) // Expect 200 (OK)
|
||||
.andExpect(jsonPath("$.username").value("g")) // Expect the player's username
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure retrieving
|
||||
* invalid player's results in a 404.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensurePlayerNotFound() throws Exception {
|
||||
mockMvc.perform(get("/player/SDFSDFSDFSDFDDDG")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isNotFound()) // Expect 404 (Not Found)
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure retrieving
|
||||
* player's with invalid usernames
|
||||
* results in a 400.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureUsernameIsInvalid() throws Exception {
|
||||
mockMvc.perform(get("/player/A")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isBadRequest()) // Expect 400 (Bad Request)
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure retrieving
|
||||
* a player's head texture is successful.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureSkinPartTextureSuccess() throws Exception {
|
||||
mockMvc.perform(get("/player/head/Rainnny.png")
|
||||
.contentType(MediaType.IMAGE_PNG) // Content type is PNG
|
||||
).andExpect(status().isOk()) // Expect 200 (OK)
|
||||
.andReturn();
|
||||
}
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Braydon (Rainnny).
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package me.braydon.mc.test.controller;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.mc.controller.ServerController;
|
||||
import me.braydon.mc.test.config.TestRedisConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for the {@link ServerController}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@SpringBootTest(classes = TestRedisConfig.class)
|
||||
@AutoConfigureMockMvc
|
||||
public final class ServerControllerTests {
|
||||
/**
|
||||
* The {@link MockMvc} instance to use for testing.
|
||||
*/
|
||||
@NonNull private final MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
public ServerControllerTests(@NonNull MockMvc mockMvc) {
|
||||
this.mockMvc = mockMvc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure retrieving
|
||||
* a Java server's status is successful.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureJavaServerLookupSuccess() throws Exception {
|
||||
mockMvc.perform(get("/server/java/hypixel.net")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isOk()) // Expect 200 (OK)
|
||||
.andExpect(jsonPath("$.value.hostname") // Expect the server's resolved hostname
|
||||
.value("mc.hypixel.net")
|
||||
).andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure retrieving
|
||||
* a Bedrock server's status is successful.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureBedrockServerLookupSuccess() throws Exception {
|
||||
mockMvc.perform(get("/server/bedrock/gateway.wildnetwork.net")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isOk()) // Expect 200 (OK)
|
||||
.andExpect(jsonPath("$.value.hostname") // Expect the server's resolved hostname
|
||||
.value("gateway.wildnetwork.net")
|
||||
).andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure that requesting
|
||||
* information about a server on an invalid
|
||||
* platform results in a 400.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureUnknownPlatform() throws Exception {
|
||||
mockMvc.perform(get("/server/invalid/hypixel.net")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isBadRequest()) // Expect 400 (Bad Request)
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure looking up
|
||||
* an invalid hostname results in a 400.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureUnknownHostname() throws Exception {
|
||||
mockMvc.perform(get("/server/java/invalid")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isBadRequest()) // Expect 400 (Bad Request)
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure looking up
|
||||
* an invalid port results in a 400.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureUnknownPort() throws Exception {
|
||||
mockMvc.perform(get("/server/java/hypixel.net?port=A")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isBadRequest()) // Expect 400 (Bad Request)
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure checking if
|
||||
* a server is banned is successful.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureServerBanCheckSuccess() throws Exception{
|
||||
mockMvc.perform(get("/server/blocked/arkhamnetwork.org")
|
||||
.accept(MediaType.APPLICATION_JSON) // Accept JSON
|
||||
.contentType(MediaType.APPLICATION_JSON) // Content type is JSON
|
||||
).andExpect(status().isOk()) // Expect 200 (OK)
|
||||
.andExpect(jsonPath("$.blocked").value(true)) // Expect block
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test to ensure retrieving
|
||||
* a server's favicon is successful.
|
||||
*
|
||||
* @throws Exception if the test fails
|
||||
*/
|
||||
@Test
|
||||
void ensureServerFaviconSuccess() throws Exception {
|
||||
mockMvc.perform(get("/server/icon/hypixel.net")
|
||||
.contentType(MediaType.IMAGE_PNG) // Content type is PNG
|
||||
).andExpect(status().isOk()) // Expect 200 (OK)
|
||||
.andReturn();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user