diff --git a/pom.xml b/pom.xml
index ed402a91..62f6ecfb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,12 +46,12 @@
io.javalin
javalin
- 3.13.12
+ 4.1.1
io.javalin
javalin-openapi
- 3.13.12
+ 4.1.1
diff --git a/src/main/java/io/servertap/PluginEntrypoint.java b/src/main/java/io/servertap/PluginEntrypoint.java
index 421fb6c0..dc9b1448 100644
--- a/src/main/java/io/servertap/PluginEntrypoint.java
+++ b/src/main/java/io/servertap/PluginEntrypoint.java
@@ -11,6 +11,7 @@
import io.servertap.api.v1.models.ConsoleLine;
import io.servertap.api.v1.websockets.ConsoleListener;
import io.servertap.api.v1.websockets.WebsocketHandler;
+import io.servertap.utils.GsonJsonMapper;
import io.swagger.v3.oas.models.info.Info;
import net.milkbowl.vault.economy.Economy;
import org.apache.logging.log4j.LogManager;
@@ -92,6 +93,8 @@ public void onEnable() {
if (app == null) {
app = Javalin.create(config -> {
+ config.jsonMapper(new GsonJsonMapper());
+
config.defaultContentType = "application/json";
config.showJavalinBanner = false;
@@ -191,11 +194,11 @@ public void onEnable() {
get("worlds", ServerApi::worldsGet);
post("worlds/save", ServerApi::saveAllWorlds);
get("worlds/download", ServerApi::downloadWorlds);
- get("worlds/:uuid", ServerApi::worldGet);
- get("worlds/:uuid/download", ServerApi::downloadWorld);
- post("worlds/:uuid/save", ServerApi::saveWorld);
+ get("worlds/{uuid}", ServerApi::worldGet);
+ post("worlds/{uuid}/save", ServerApi::saveWorld);
+ get("worlds/{uuid}/download", ServerApi::downloadWorld);
get("scoreboard", ServerApi::scoreboardGet);
- get("scoreboard/:name", ServerApi::objectiveGet);
+ get("scoreboard/{name}", ServerApi::objectiveGet);
// Chat
post("chat/broadcast", ServerApi::broadcastPost);
@@ -204,8 +207,8 @@ public void onEnable() {
// Player routes
get("players", PlayerApi::playersGet);
get("players/all", PlayerApi::offlinePlayersGet);
- get("players/:uuid", PlayerApi::playerGet);
- get("players/:playerUuid/:worldUuid/inventory", PlayerApi::getPlayerInv);
+ get("players/{uuid}", PlayerApi::playerGet);
+ get("players/{playerUuid}/{worldUuid}/inventory", PlayerApi::getPlayerInv);
// Economy routes
post("economy/pay", EconomyApi::playerPay);
@@ -268,6 +271,7 @@ private OpenApiOptions getOpenApiOptions() {
.description(this.getDescription().getDescription());
return new OpenApiOptions(applicationInfo)
.path("/swagger-docs")
+ .includePath(Constants.API_V1 + "/*")
.activateAnnotationScanningFor("io.servertap.api.v1")
.swagger(new SwaggerOptions("/swagger"));
}
diff --git a/src/main/java/io/servertap/WebhookEventListener.java b/src/main/java/io/servertap/WebhookEventListener.java
index 3d6e6fa2..fcf5e129 100644
--- a/src/main/java/io/servertap/WebhookEventListener.java
+++ b/src/main/java/io/servertap/WebhookEventListener.java
@@ -4,8 +4,8 @@
import io.servertap.api.v1.models.ItemStack;
import io.servertap.api.v1.models.Player;
import io.servertap.api.v1.models.events.*;
+import io.servertap.utils.GsonSingleton;
import net.md_5.bungee.api.ChatColor;
-
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.EventHandler;
@@ -27,7 +27,6 @@
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
-import java.util.regex.*;
public class WebhookEventListener implements Listener {
private List registeredWebhooks;
@@ -260,7 +259,7 @@ public PostRequestTask(WebhookEvent webhookEvent, String listener) {
@Override
public void run() {
try {
- Gson gson = new Gson();
+ Gson gson = GsonSingleton.getInstance();
String jsonContent = gson.toJson(webhookEvent);
byte[] output = jsonContent.getBytes(StandardCharsets.UTF_8);
diff --git a/src/main/java/io/servertap/api/v1/PlayerApi.java b/src/main/java/io/servertap/api/v1/PlayerApi.java
index ae03e8f6..b6f8d28e 100644
--- a/src/main/java/io/servertap/api/v1/PlayerApi.java
+++ b/src/main/java/io/servertap/api/v1/PlayerApi.java
@@ -42,7 +42,7 @@ public static void playersGet(Context ctx) {
}
@OpenApi(
- path = "/v1/players/:uuid",
+ path = "/v1/players/{uuid}",
method = HttpMethod.GET,
summary = "Gets a specific online player by their UUID",
tags = {"Player"},
@@ -158,7 +158,7 @@ public static void offlinePlayersGet(Context ctx) {
}
@OpenApi(
- path = "/v1/players/:playerUuid/:worldUuid/inventory",
+ path = "/v1/players/{playerUuid}/{worldUuid}/inventory",
method = HttpMethod.GET,
summary = "Gets a specific online player's Inventory in the specified world",
tags = {"Player"},
diff --git a/src/main/java/io/servertap/api/v1/ServerApi.java b/src/main/java/io/servertap/api/v1/ServerApi.java
index 94e5b2e0..339487c2 100644
--- a/src/main/java/io/servertap/api/v1/ServerApi.java
+++ b/src/main/java/io/servertap/api/v1/ServerApi.java
@@ -2,7 +2,6 @@
import com.google.gson.Gson;
import io.javalin.http.*;
-import io.javalin.plugin.json.JavalinJson;
import io.javalin.plugin.openapi.annotations.*;
import io.servertap.Constants;
import io.servertap.Lag;
@@ -11,6 +10,7 @@
import io.servertap.api.v1.models.*;
import io.servertap.mojang.api.MojangApiService;
import io.servertap.mojang.api.models.NameChange;
+import io.servertap.utils.GsonSingleton;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.bukkit.BanList;
@@ -19,7 +19,10 @@
import org.bukkit.plugin.Plugin;
import org.bukkit.scoreboard.ScoreboardManager;
-import java.io.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.math.BigDecimal;
import java.nio.file.Paths;
@@ -160,7 +163,7 @@ public static void saveAllWorlds(Context ctx) {
}
@OpenApi(
- path = "/v1/worlds/:uuid/save",
+ path = "/v1/worlds/{uuid}/save",
summary = "Triggers a world save",
method = HttpMethod.POST,
tags = {"Server"},
@@ -233,7 +236,7 @@ private static void addFolderToZip(File folder, ZipOutputStream zip, String base
}
@OpenApi(
- path = "/v1/worlds/:uuid/download",
+ path = "/v1/worlds/{uuid}/download",
summary = "Downloads a ZIP compressed archive of the world's folder",
method = HttpMethod.GET,
tags = {"Server"},
@@ -401,7 +404,7 @@ public static void scoreboardGet(Context ctx) {
}
@OpenApi(
- path = "/v1/scoreboard/:name",
+ path = "/v1/scoreboard/{name}",
summary = "Get information about a specific objective",
tags = {"Server"},
headers = {
@@ -415,7 +418,7 @@ public static void scoreboardGet(Context ctx) {
}
)
public static void objectiveGet(Context ctx) {
- String objectiveName = ctx.pathParam(":name");
+ String objectiveName = ctx.pathParam("name");
ScoreboardManager manager = Bukkit.getScoreboardManager();
org.bukkit.scoreboard.Scoreboard gameScoreboard = manager.getMainScoreboard();
org.bukkit.scoreboard.Objective objective = gameScoreboard.getObjective(objectiveName);
@@ -477,7 +480,7 @@ public static void worldsGet(Context ctx) {
}
@OpenApi(
- path = "/v1/worlds/:uuid",
+ path = "/v1/worlds/{uuid}",
summary = "Get information about a specific world",
tags = {"Server"},
headers = {
@@ -650,7 +653,7 @@ public static void whitelistPost(Context ctx) {
}
}
whitelist.add(newEntry);
- final String json = new Gson().toJson(whitelist);
+ final String json = GsonSingleton.getInstance().toJson(whitelist);
try {
final String path = Paths.get(directory.getAbsolutePath(), "whitelist.json").toString();
final File myObj = new File(path);
@@ -827,11 +830,13 @@ public static void postCommand(Context ctx) {
AtomicLong time = new AtomicLong(timeRaw != null ? Long.parseLong(timeRaw) : 0);
if (time.get() < 0) time.set(0);
- ctx.result(CompletableFuture.supplyAsync(() -> {
+ ctx.future(CompletableFuture.supplyAsync(() -> {
CompletableFuture future = new ServerExecCommandSender().executeCommand(command, time.get(), TimeUnit.MILLISECONDS);
try {
String output = future.get();
- return "application/json".equalsIgnoreCase(ctx.contentType()) ? JavalinJson.toJson(output) : output;
+ Gson g = GsonSingleton.getInstance();
+
+ return "application/json".equalsIgnoreCase(ctx.contentType()) ? g.toJson(output) : output;
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
diff --git a/src/main/java/io/servertap/api/v1/websockets/WebsocketHandler.java b/src/main/java/io/servertap/api/v1/websockets/WebsocketHandler.java
index 834b2175..d96cea44 100644
--- a/src/main/java/io/servertap/api/v1/websockets/WebsocketHandler.java
+++ b/src/main/java/io/servertap/api/v1/websockets/WebsocketHandler.java
@@ -1,7 +1,7 @@
package io.servertap.api.v1.websockets;
+import io.javalin.websocket.WsConfig;
import io.javalin.websocket.WsContext;
-import io.javalin.websocket.WsHandler;
import io.servertap.PluginEntrypoint;
import io.servertap.api.v1.models.ConsoleLine;
import org.bukkit.Bukkit;
@@ -15,7 +15,7 @@ public class WebsocketHandler {
private final static Map subscribers = new ConcurrentHashMap<>();
- public static void events(WsHandler ws) {
+ public static void events(WsConfig ws) {
ws.onConnect(ctx -> {
subscribers.put(clientHash(ctx), ctx);
diff --git a/src/main/java/io/servertap/mojang/api/MojangApiService.java b/src/main/java/io/servertap/mojang/api/MojangApiService.java
index 74d9eb9f..38f2546d 100644
--- a/src/main/java/io/servertap/mojang/api/MojangApiService.java
+++ b/src/main/java/io/servertap/mojang/api/MojangApiService.java
@@ -4,6 +4,7 @@
import com.google.gson.reflect.TypeToken;
import io.servertap.mojang.api.models.NameChange;
import io.servertap.mojang.api.models.PlayerInfo;
+import io.servertap.utils.GsonSingleton;
import java.io.BufferedReader;
import java.io.IOException;
@@ -21,7 +22,7 @@ public class MojangApiService {
private static final String getNameHistoryResource = "https://api.mojang.com/user/profiles/%s/names";
public static String getUuid(String username) throws IOException {
- Gson gson = new Gson();
+ Gson gson = GsonSingleton.getInstance();
ApiResponse apiResponse = getApiResponse(String.format(getUuidResource, username));
if (apiResponse.getHttpStatus() == HttpURLConnection.HTTP_NO_CONTENT) {
@@ -34,7 +35,7 @@ public static String getUuid(String username) throws IOException {
public static List getNameHistory(String uuid) throws IOException {
Type listType = new TypeToken>() {
}.getType();
- Gson gson = new Gson();
+ Gson gson = GsonSingleton.getInstance();
//This API call doesn't accept UUIDS with dashes
ApiResponse apiResponse = getApiResponse(String.format(getNameHistoryResource, uuid).replace("-", ""));
diff --git a/src/main/java/io/servertap/utils/GsonJsonMapper.java b/src/main/java/io/servertap/utils/GsonJsonMapper.java
new file mode 100644
index 00000000..51ac42ce
--- /dev/null
+++ b/src/main/java/io/servertap/utils/GsonJsonMapper.java
@@ -0,0 +1,11 @@
+package io.servertap.utils;
+
+import org.jetbrains.annotations.NotNull;
+
+public class GsonJsonMapper implements io.javalin.plugin.json.JsonMapper {
+ @NotNull
+ @Override
+ public String toJsonString(@NotNull Object obj) {
+ return GsonSingleton.getInstance().toJson(obj);
+ }
+}
diff --git a/src/main/java/io/servertap/utils/GsonSingleton.java b/src/main/java/io/servertap/utils/GsonSingleton.java
new file mode 100644
index 00000000..936a7c4c
--- /dev/null
+++ b/src/main/java/io/servertap/utils/GsonSingleton.java
@@ -0,0 +1,19 @@
+package io.servertap.utils;
+
+import com.google.gson.Gson;
+
+public class GsonSingleton {
+
+ private static Gson instance;
+
+ private GsonSingleton() {
+ }
+
+ public static Gson getInstance() {
+ if (instance == null) {
+ instance = new Gson();
+ }
+
+ return instance;
+ }
+}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 3ebf4a31..32365131 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -2,6 +2,9 @@ name: ServerTap
main: io.servertap.PluginEntrypoint
version: 0.1.4-SNAPSHOT
description: ServerTap is a REST API for Bukkit/Spigot/PaperMC Minecraft servers.
+authors: [phybros, ATechAdventurer, c1oneman, Earlh21, Scarsz, au5ton, Aberdeener, Hedlund01, matteoturini, Diddyy]
+load: STARTUP
+website: https://servertap.io
api-version: 1.14
softdepend:
- Vault