Refactor code structure

This commit is contained in:
Alessandro Proto 2024-10-25 12:56:22 +02:00
parent 325e52bc19
commit 6ca46fc562
15 changed files with 306 additions and 271 deletions

View file

@ -1,39 +1,26 @@
package cc.reconnected.server;
import cc.reconnected.server.commands.*;
import cc.reconnected.server.core.*;
import cc.reconnected.server.database.PlayerData;
import cc.reconnected.server.events.PlayerActivityEvents;
import cc.reconnected.server.events.PlayerWelcome;
import cc.reconnected.server.events.Ready;
import cc.reconnected.server.http.ServiceServer;
import cc.reconnected.server.struct.ServerPosition;
import cc.reconnected.server.tablist.TabList;
import cc.reconnected.server.trackers.AfkTracker;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.kyori.adventure.platform.fabric.FabricServerAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
public class RccServer implements ModInitializer {
@ -42,20 +29,14 @@ public class RccServer implements ModInitializer {
public static final cc.reconnected.server.RccServerConfig CONFIG = cc.reconnected.server.RccServerConfig.createAndLoad();
private static float currentTps = 0;
private static float currentMspt = 0;
private static int currentPlayerCount = 0;
private static RccServer INSTANCE;
public static RccServer getInstance() {
return INSTANCE;
}
private ServiceServer serviceServer;
public ServiceServer serviceServer() {
return serviceServer;
public RccServer() {
INSTANCE = this;
}
private LuckPerms luckPerms;
@ -64,28 +45,6 @@ public class RccServer implements ModInitializer {
return luckPerms;
}
private AfkTracker afkTracker;
public AfkTracker afkTracker() {
return afkTracker;
}
public static float getTPS() {
return currentTps;
}
public static float getMSPT() {
return currentMspt;
}
public static int getPlayerCount() {
return currentPlayerCount;
}
public RccServer() {
INSTANCE = this;
}
private volatile FabricServerAudiences adventure;
public FabricServerAudiences adventure() {
@ -96,9 +55,6 @@ public class RccServer implements ModInitializer {
return ret;
}
public static final ConcurrentHashMap<UUID, ConcurrentLinkedDeque<TeleportAskCommand.TeleportRequest>> teleportRequests = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<UUID, ServerPosition> lastPlayerPositions = new ConcurrentHashMap<>();
@Override
public void onInitialize() {
LOGGER.info("Starting rcc-server");
@ -120,46 +76,18 @@ public class RccServer implements ModInitializer {
GodCommand.register(dispatcher, registryAccess, environment);
});
AfkTracker.register();
TeleportTracker.register();
BackTracker.register();
TabList.register();
HttpApiServer.register();
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
luckPerms = LuckPermsProvider.get();
afkTracker = new AfkTracker();
Ready.READY.invoker().ready(server, luckPerms);
if (CONFIG.enableHttpApi()) {
try {
serviceServer = new ServiceServer();
} catch (IOException e) {
LOGGER.error("Unable to start HTTP server", e);
}
}
});
ServerTickEvents.END_SERVER_TICK.register(server -> {
currentMspt = server.getTickTime();
if (currentMspt != 0) {
currentTps = Math.min(20, 1000 / currentMspt);
}
teleportRequests.forEach((recipient, requestList) -> {
requestList.forEach(request -> {
if (request.remainingTicks-- == 0) {
requestList.remove(request);
}
});
});
});
ServerLifecycleEvents.SERVER_STOPPING.register(server -> {
if (CONFIG.enableHttpApi()) {
LOGGER.info("Stopping HTTP services");
serviceServer.httpServer().stop(0);
}
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
currentPlayerCount = server.getCurrentPlayerCount() + 1;
var player = handler.getPlayer();
var playerData = PlayerData.getPlayer(player.getUuid());
playerData.set(PlayerData.KEYS.username, player.getName().getString());
@ -173,45 +101,6 @@ public class RccServer implements ModInitializer {
PlayerWelcome.PLAYER_WELCOME.invoker().playerWelcome(player, playerData, server);
LOGGER.info("Player {} joined for the first time!", player.getName().getString());
}
teleportRequests.put(player.getUuid(), new ConcurrentLinkedDeque<>());
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
currentPlayerCount = server.getCurrentPlayerCount() - 1;
teleportRequests.remove(handler.getPlayer().getUuid());
lastPlayerPositions.remove(handler.getPlayer().getUuid());
});
PlayerActivityEvents.AFK.register((player, server) -> {
LOGGER.info("{} is AFK. Active time: {} seconds.", player.getGameProfile().getName(), afkTracker.getActiveTime(player));
var displayNameJson = Text.Serializer.toJson(player.getDisplayName());
var displayName = JSONComponentSerializer.json().deserialize(displayNameJson);
var message = MiniMessage.miniMessage().deserialize(CONFIG.afkMessage(),
Placeholder.component("displayname", displayName),
Placeholder.unparsed("username", player.getGameProfile().getName()),
Placeholder.unparsed("uuid", player.getUuid().toString())
);
broadcastMessage(server, message);
});
PlayerActivityEvents.AFK_RETURN.register((player, server) -> {
LOGGER.info("{} is no longer AFK. Active time: {} seconds.", player.getGameProfile().getName(), afkTracker.getActiveTime(player));
var displayNameJson = Text.Serializer.toJson(player.getDisplayName());
var displayName = JSONComponentSerializer.json().deserialize(displayNameJson);
var message = MiniMessage.miniMessage().deserialize(CONFIG.afkReturnMessage(),
Placeholder.component("displayname", displayName),
Placeholder.unparsed("username", player.getGameProfile().getName()),
Placeholder.unparsed("uuid", player.getUuid().toString())
);
broadcastMessage(server, message);
});
}
@ -226,16 +115,4 @@ public class RccServer implements ModInitializer {
player.sendMessage(message);
}
}
public boolean isPlayerAfk(PlayerEntity player) {
return afkTracker.isPlayerAfk(player.getUuid());
}
public void setPlayerAfk(ServerPlayerEntity player, boolean afk) {
afkTracker.setPlayerAfk(player, afk);
}
public int getActiveTime(ServerPlayerEntity player) {
return afkTracker().getActiveTime(player);
}
}

View file

@ -1,6 +1,6 @@
package cc.reconnected.server.commands;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.core.AfkTracker;
import com.mojang.brigadier.CommandDispatcher;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.command.CommandRegistryAccess;
@ -22,7 +22,7 @@ public class AfkCommand {
}
var player = context.getSource().getPlayer();
RccServer.getInstance().setPlayerAfk(player, true);
AfkTracker.getInstance().setPlayerAfk(player, true);
return 1;
});

View file

@ -1,6 +1,6 @@
package cc.reconnected.server.commands;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.core.BackTracker;
import com.mojang.brigadier.CommandDispatcher;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.command.CommandRegistryAccess;
@ -23,7 +23,7 @@ public class BackCommand {
var player = context.getSource().getPlayer();
var lastPosition = RccServer.lastPlayerPositions.get(player.getUuid());
var lastPosition = BackTracker.lastPlayerPositions.get(player.getUuid());
if (lastPosition == null) {
context.getSource().sendFeedback(() -> Text.literal("There is no position to return back to.").formatted(Formatting.RED), false);
return 1;

View file

@ -1,6 +1,6 @@
package cc.reconnected.server.commands;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.core.TeleportTracker;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.command.CommandRegistryAccess;
@ -23,7 +23,7 @@ public class TeleportAcceptCommand {
}
var playerUuid = context.getSource().getPlayer().getUuid();
var playerRequests = RccServer.teleportRequests.get(playerUuid);
var playerRequests = TeleportTracker.teleportRequests.get(playerUuid);
var request = playerRequests.pollLast();
@ -46,7 +46,7 @@ public class TeleportAcceptCommand {
var uuid = UuidArgumentType.getUuid(context, "uuid");
var playerUuid = context.getSource().getPlayer().getUuid();
var playerRequests = RccServer.teleportRequests.get(playerUuid);
var playerRequests = TeleportTracker.teleportRequests.get(playerUuid);
var request = playerRequests.stream().filter(req -> req.requestId.equals(uuid)).findFirst().orElse(null);
if (request == null) {
@ -62,7 +62,7 @@ public class TeleportAcceptCommand {
dispatcher.register(literal("tpyes").redirect(node));
}
private static void execute(CommandContext<ServerCommandSource> context, TeleportAskCommand.TeleportRequest request) {
private static void execute(CommandContext<ServerCommandSource> context, TeleportTracker.TeleportRequest request) {
var source = context.getSource();
request.expire();
@ -88,6 +88,6 @@ public class TeleportAcceptCommand {
targetPlayer.sendMessage(Text.empty().append(player.getDisplayName()).append(Text.literal(" accepted your teleport request.").formatted(Formatting.GREEN)), false);
}
TeleportAskCommand.teleport(sourcePlayer, targetPlayer);
TeleportTracker.teleport(sourcePlayer, targetPlayer);
}
}

View file

@ -1,28 +1,19 @@
package cc.reconnected.server.commands;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.struct.ServerPosition;
import cc.reconnected.server.core.TeleportTracker;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.command.CommandSource;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.time.Duration;
import java.util.UUID;
import static net.minecraft.server.command.CommandManager.*;
public class TeleportAskCommand {
@ -60,8 +51,8 @@ public class TeleportAskCommand {
return;
}
var request = new TeleportRequest(player.getUuid(), target.getUuid());
var targetRequests = RccServer.teleportRequests.get(target.getUuid());
var request = new TeleportTracker.TeleportRequest(player.getUuid(), target.getUuid());
var targetRequests = TeleportTracker.teleportRequests.get(target.getUuid());
targetRequests.addLast(request);
var requestMessage = Component.empty()
@ -69,53 +60,13 @@ public class TeleportAskCommand {
.appendSpace()
.append(Component.text("requested to teleport to you.", NamedTextColor.GOLD))
.appendNewline().appendSpace()
.append(makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
.append(TeleportTracker.makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
.appendSpace()
.append(makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
.append(TeleportTracker.makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
target.sendMessage(requestMessage);
source.sendFeedback(() -> Text.literal("Teleport request sent.").setStyle(Style.EMPTY.withColor(Formatting.GREEN)), false);
}
public static Component makeButton(ComponentLike text, ComponentLike hoverText, String command) {
return Component.empty()
.append(Component.text("["))
.append(text)
.append(Component.text("]"))
.color(NamedTextColor.AQUA)
.hoverEvent(HoverEvent.showText(hoverText))
.clickEvent(ClickEvent.runCommand(command));
}
public static void teleport(ServerPlayerEntity sourcePlayer, ServerPlayerEntity targetPlayer) {
RccServer.lastPlayerPositions.put(sourcePlayer.getUuid(), new ServerPosition(sourcePlayer));
sourcePlayer.teleport(
targetPlayer.getServerWorld(),
targetPlayer.getX(),
targetPlayer.getY(),
targetPlayer.getZ(),
targetPlayer.getYaw(),
targetPlayer.getPitch()
);
}
public static class TeleportRequest {
public UUID requestId = UUID.randomUUID();
public UUID player;
public UUID target;
public int remainingTicks;
public TeleportRequest(UUID player, UUID target) {
this.player = player;
this.target = target;
// Seconds in config per 20 ticks
this.remainingTicks = RccServer.CONFIG.teleportRequestTimeout() * 20;
}
public void expire() {
remainingTicks = 0;
}
}
}

View file

@ -1,6 +1,6 @@
package cc.reconnected.server.commands;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.core.TeleportTracker;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
@ -52,8 +52,8 @@ public class TeleportAskHereCommand {
return;
}
var request = new TeleportAskCommand.TeleportRequest(target.getUuid(), player.getUuid());
var targetRequests = RccServer.teleportRequests.get(target.getUuid());
var request = new TeleportTracker.TeleportRequest(target.getUuid(), player.getUuid());
var targetRequests = TeleportTracker.teleportRequests.get(target.getUuid());
targetRequests.addLast(request);
var requestMessage = Component.empty()
@ -61,9 +61,9 @@ public class TeleportAskHereCommand {
.appendSpace()
.append(Component.text("requested you to teleport to them.", NamedTextColor.GOLD))
.appendNewline().appendSpace()
.append(TeleportAskCommand.makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
.append(TeleportTracker.makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
.appendSpace()
.append(TeleportAskCommand.makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
.append(TeleportTracker.makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
target.sendMessage(requestMessage);

View file

@ -1,6 +1,6 @@
package cc.reconnected.server.commands;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.core.TeleportTracker;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.command.CommandRegistryAccess;
@ -25,7 +25,7 @@ public class TeleportDenyCommand {
}
var playerUuid = context.getSource().getPlayer().getUuid();
var playerRequests = RccServer.teleportRequests.get(playerUuid);
var playerRequests = TeleportTracker.teleportRequests.get(playerUuid);
var request = playerRequests.pollLast();
@ -47,7 +47,7 @@ public class TeleportDenyCommand {
var uuid = UuidArgumentType.getUuid(context, "uuid");
var playerUuid = context.getSource().getPlayer().getUuid();
var playerRequests = RccServer.teleportRequests.get(playerUuid);
var playerRequests = TeleportTracker.teleportRequests.get(playerUuid);
var request = playerRequests.stream().filter(req -> req.requestId.equals(uuid)).findFirst().orElse(null);
if (request == null) {
@ -64,7 +64,7 @@ public class TeleportDenyCommand {
dispatcher.register(literal("tprefuse").redirect(node));
}
private static void execute(CommandContext<ServerCommandSource> context, TeleportAskCommand.TeleportRequest request) {
private static void execute(CommandContext<ServerCommandSource> context, TeleportTracker.TeleportRequest request) {
var source = context.getSource();
request.expire();

View file

@ -1,15 +1,18 @@
package cc.reconnected.server.trackers;
package cc.reconnected.server.core;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.data.StateSaverAndLoader;
import cc.reconnected.server.database.PlayerData;
import cc.reconnected.server.events.PlayerActivityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.player.*;
import net.fabricmc.fabric.api.message.v1.ServerMessageEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.TypedActionResult;
@ -22,7 +25,44 @@ public class AfkTracker {
private final HashMap<UUID, PlayerState> playerStates = new HashMap<>();
public AfkTracker() {
private static final AfkTracker instance = new AfkTracker();
public static AfkTracker getInstance() {
return instance;
}
public static void register() {
PlayerActivityEvents.AFK.register((player, server) -> {
RccServer.LOGGER.info("{} is AFK. Active time: {} seconds.", player.getGameProfile().getName(), getInstance().getActiveTime(player));
var displayNameJson = Text.Serializer.toJson(player.getDisplayName());
var displayName = JSONComponentSerializer.json().deserialize(displayNameJson);
var message = MiniMessage.miniMessage().deserialize(RccServer.CONFIG.afkMessage(),
Placeholder.component("displayname", displayName),
Placeholder.unparsed("username", player.getGameProfile().getName()),
Placeholder.unparsed("uuid", player.getUuid().toString())
);
RccServer.getInstance().broadcastMessage(server, message);
});
PlayerActivityEvents.AFK_RETURN.register((player, server) -> {
RccServer.LOGGER.info("{} is no longer AFK. Active time: {} seconds.", player.getGameProfile().getName(), getInstance().getActiveTime(player));
var displayNameJson = Text.Serializer.toJson(player.getDisplayName());
var displayName = JSONComponentSerializer.json().deserialize(displayNameJson);
var message = MiniMessage.miniMessage().deserialize(RccServer.CONFIG.afkReturnMessage(),
Placeholder.component("displayname", displayName),
Placeholder.unparsed("username", player.getGameProfile().getName()),
Placeholder.unparsed("uuid", player.getUuid().toString())
);
RccServer.getInstance().broadcastMessage(server, message);
});
}
private AfkTracker() {
ServerTickEvents.END_SERVER_TICK.register(server -> {
if (server.getTicks() % cycleDelay == 0) {
updatePlayers(server);
@ -83,7 +123,6 @@ public class AfkTracker {
});
}
private void updatePlayer(ServerPlayerEntity player, MinecraftServer server) {
var currentTick = server.getTicks();
var playerState = playerStates.computeIfAbsent(player.getUuid(), uuid -> new PlayerState(player, currentTick));
@ -202,5 +241,4 @@ public class AfkTracker {
var worldPlayerData = StateSaverAndLoader.getPlayerState(player);
return worldPlayerData.activeTime;
}
}

View file

@ -0,0 +1,27 @@
package cc.reconnected.server.core;
import cc.reconnected.server.events.PlayerTeleport;
import cc.reconnected.server.struct.ServerPosition;
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class BackTracker {
public static final ConcurrentHashMap<UUID, ServerPosition> lastPlayerPositions = new ConcurrentHashMap<>();
public static void register() {
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
BackTracker.lastPlayerPositions.remove(handler.getPlayer().getUuid());
});
PlayerTeleport.EVENT.register((player, origin, destination) -> {
lastPlayerPositions.put(player.getUuid(), origin);
});
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> {
lastPlayerPositions.put(oldPlayer.getUuid(), new ServerPosition(oldPlayer));
});
}
}

View file

@ -0,0 +1,104 @@
package cc.reconnected.server.core;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import cc.reconnected.server.RccServer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import java.io.IOException;
import java.net.InetSocketAddress;
public class HttpApiServer {
private static HttpApiServer instance;
public static HttpApiServer getInstance() {
return instance;
}
private static float currentTps = 0;
private static float currentMspt = 0;
private static int currentPlayerCount = 0;
public static void register() {
if (!RccServer.CONFIG.enableHttpApi())
return;
try {
instance = new HttpApiServer();
} catch (IOException e) {
RccServer.LOGGER.error("Could not start HTTP API server", e);
return;
}
new Thread(() -> instance.httpServer().start());
ServerLifecycleEvents.SERVER_STOPPING.register(server -> {
RccServer.LOGGER.info("Stopping HTTP services");
instance.httpServer().stop(0);
});
ServerTickEvents.END_SERVER_TICK.register(server -> {
currentMspt = server.getTickTime();
if (currentMspt != 0) {
currentTps = Math.min(20, 1000 / currentMspt);
}
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
currentPlayerCount = server.getCurrentPlayerCount() + 1;
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
currentPlayerCount = server.getCurrentPlayerCount() - 1;
});
}
private final HttpServer server;
public HttpServer httpServer() {
return this.server;
}
private HttpApiServer() throws IOException {
server = HttpServer.create(new InetSocketAddress(RccServer.CONFIG.httpPort()), 0);
server.createContext("/tps", new TPSHandler());
server.createContext("/mspt", new MSPTHandler());
server.createContext("/player", new PlayerCountHandler());
server.setExecutor(null);
}
static class TPSHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
var tps = String.valueOf(currentTps);
t.sendResponseHeaders(200, tps.length());
var body = t.getResponseBody();
body.write(tps.getBytes());
body.close();
}
}
static class MSPTHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
var tps = String.valueOf(currentTps);
t.sendResponseHeaders(200, tps.length());
var body = t.getResponseBody();
body.write(tps.getBytes());
body.close();
}
}
static class PlayerCountHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
var tps = String.valueOf(currentPlayerCount);
t.sendResponseHeaders(200, tps.length());
var body = t.getResponseBody();
body.write(tps.getBytes());
body.close();
}
}
}

View file

@ -1,4 +1,4 @@
package cc.reconnected.server.tablist;
package cc.reconnected.server.core;
import cc.reconnected.server.RccServer;
import eu.pb4.placeholders.api.PlaceholderContext;

View file

@ -0,0 +1,82 @@
package cc.reconnected.server.core;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.events.PlayerTeleport;
import cc.reconnected.server.struct.ServerPosition;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
public class TeleportTracker {
public static final ConcurrentHashMap<UUID, ConcurrentLinkedDeque<TeleportRequest>> teleportRequests = new ConcurrentHashMap<>();
public static void register() {
ServerTickEvents.END_SERVER_TICK.register(server -> {
teleportRequests.forEach((recipient, requestList) -> {
requestList.forEach(request -> {
if (request.remainingTicks-- == 0) {
requestList.remove(request);
}
});
});
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
teleportRequests.put(handler.getPlayer().getUuid(), new ConcurrentLinkedDeque<>());
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
TeleportTracker.teleportRequests.remove(handler.getPlayer().getUuid());
});
}
public static Component makeButton(ComponentLike text, ComponentLike hoverText, String command) {
return Component.empty()
.append(Component.text("["))
.append(text)
.append(Component.text("]"))
.color(NamedTextColor.AQUA)
.hoverEvent(HoverEvent.showText(hoverText))
.clickEvent(ClickEvent.runCommand(command));
}
public static void teleport(ServerPlayerEntity sourcePlayer, ServerPlayerEntity targetPlayer) {
PlayerTeleport.EVENT.invoker().teleport(sourcePlayer, new ServerPosition(sourcePlayer), new ServerPosition(targetPlayer));
sourcePlayer.teleport(
targetPlayer.getServerWorld(),
targetPlayer.getX(),
targetPlayer.getY(),
targetPlayer.getZ(),
targetPlayer.getYaw(),
targetPlayer.getPitch()
);
}
public static class TeleportRequest {
public UUID requestId = UUID.randomUUID();
public UUID player;
public UUID target;
public int remainingTicks;
public TeleportRequest(UUID player, UUID target) {
this.player = player;
this.target = target;
// Seconds in config per 20 ticks
this.remainingTicks = RccServer.CONFIG.teleportRequestTimeout() * 20;
}
public void expire() {
remainingTicks = 0;
}
}
}

View file

@ -0,0 +1,17 @@
package cc.reconnected.server.events;
import cc.reconnected.server.struct.ServerPosition;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.network.ServerPlayerEntity;
public interface PlayerTeleport {
Event<PlayerTeleport> EVENT = EventFactory.createArrayBacked(PlayerTeleport.class,
(listeners) -> (player, origin, destination) -> {
for (PlayerTeleport listener : listeners) {
listener.teleport(player, origin, destination);
}
});
void teleport(ServerPlayerEntity player, ServerPosition origin, ServerPosition destination);
}

View file

@ -1,61 +0,0 @@
package cc.reconnected.server.http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import cc.reconnected.server.RccServer;
import java.io.IOException;
import java.net.InetSocketAddress;
public class ServiceServer {
private final HttpServer server;
public HttpServer httpServer() {
return this.server;
}
public ServiceServer() throws IOException {
server = HttpServer.create(new InetSocketAddress(RccServer.CONFIG.httpPort()), 0);
server.createContext("/tps", new TPSHandler());
server.createContext("/mspt", new MSPTHandler());
server.createContext("/player", new PlayerCountHandler());
server.setExecutor(null);
var httpThread = new Thread(server::start);
httpThread.start();
}
static class TPSHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
var tps = String.valueOf(RccServer.getTPS());
t.sendResponseHeaders(200, tps.length());
var body = t.getResponseBody();
body.write(tps.getBytes());
body.close();
}
}
static class MSPTHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
var tps = String.valueOf(RccServer.getMSPT());
t.sendResponseHeaders(200, tps.length());
var body = t.getResponseBody();
body.write(tps.getBytes());
body.close();
}
}
static class PlayerCountHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
var tps = String.valueOf(RccServer.getPlayerCount());
t.sendResponseHeaders(200, tps.length());
var body = t.getResponseBody();
body.write(tps.getBytes());
body.close();
}
}
}

View file

@ -1,6 +1,6 @@
package cc.reconnected.server.struct;
import cc.reconnected.server.RccServer;
import cc.reconnected.server.core.BackTracker;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
@ -32,7 +32,7 @@ public class ServerPosition {
public void teleport(ServerPlayerEntity player) {
var currentPosition = new ServerPosition(player);
RccServer.lastPlayerPositions.put(player.getUuid(), currentPosition);
BackTracker.lastPlayerPositions.put(player.getUuid(), currentPosition);
player.teleport(
this.world,