More refactoring, added custom tab
This commit is contained in:
parent
019da6ebfb
commit
5a879b0800
38 changed files with 938 additions and 192 deletions
|
@ -9,7 +9,7 @@ yarn_mappings=1.20.1+build.10
|
|||
loader_version=0.16.7
|
||||
|
||||
# Mod Properties
|
||||
mod_version=1.13.0
|
||||
mod_version=1.14.0
|
||||
maven_group=cc.reconnected
|
||||
archives_base_name=rcc-server
|
||||
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
package cc.reconnected.server;
|
||||
|
||||
import cc.reconnected.server.commands.*;
|
||||
import cc.reconnected.server.commands.home.*;
|
||||
import cc.reconnected.server.commands.misc.*;
|
||||
import cc.reconnected.server.commands.spawn.*;
|
||||
import cc.reconnected.server.commands.teleport.*;
|
||||
import cc.reconnected.server.commands.tell.*;
|
||||
import cc.reconnected.server.commands.warp.*;
|
||||
import cc.reconnected.server.core.*;
|
||||
import cc.reconnected.server.data.StateManager;
|
||||
import cc.reconnected.server.database.PlayerData;
|
||||
import cc.reconnected.server.events.PlayerUsernameChange;
|
||||
import cc.reconnected.server.events.PlayerWelcome;
|
||||
import cc.reconnected.server.events.Ready;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
@ -17,6 +24,7 @@ import net.luckperms.api.LuckPermsProvider;
|
|||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -29,6 +37,8 @@ public class RccServer implements ModInitializer {
|
|||
|
||||
public static final cc.reconnected.server.RccServerConfig CONFIG = cc.reconnected.server.RccServerConfig.createAndLoad();
|
||||
|
||||
public static final StateManager state = new StateManager();
|
||||
|
||||
private static RccServer INSTANCE;
|
||||
|
||||
public static RccServer getInstance() {
|
||||
|
@ -59,21 +69,40 @@ public class RccServer implements ModInitializer {
|
|||
public void onInitialize() {
|
||||
LOGGER.info("Starting rcc-server");
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(server -> this.adventure = FabricServerAudiences.of(server));
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
|
||||
state.register(server.getSavePath(WorldSavePath.ROOT).resolve("data").resolve(RccServer.MOD_ID));
|
||||
this.adventure = FabricServerAudiences.of(server);
|
||||
});
|
||||
ServerLifecycleEvents.SERVER_STOPPED.register(server -> this.adventure = null);
|
||||
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
|
||||
RccCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
AfkCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
TellCommand.register(dispatcher, registryAccess, environment);
|
||||
ReplyCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
TeleportAskCommand.register(dispatcher, registryAccess, environment);
|
||||
TeleportAskHereCommand.register(dispatcher, registryAccess, environment);
|
||||
TeleportAcceptCommand.register(dispatcher, registryAccess, environment);
|
||||
TeleportDenyCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
BackCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
FlyCommand.register(dispatcher, registryAccess, environment);
|
||||
GodCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
SetSpawnCommand.register(dispatcher, registryAccess, environment);
|
||||
SpawnCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
HomeCommand.register(dispatcher, registryAccess, environment);
|
||||
SetHomeCommand.register(dispatcher, registryAccess, environment);
|
||||
DeleteHomeCommand.register(dispatcher, registryAccess, environment);
|
||||
|
||||
WarpCommand.register(dispatcher, registryAccess, environment);
|
||||
SetWarpCommand.register(dispatcher, registryAccess, environment);
|
||||
DeleteWarpCommand.register(dispatcher, registryAccess, environment);
|
||||
});
|
||||
|
||||
AfkTracker.register();
|
||||
|
@ -89,25 +118,32 @@ public class RccServer implements ModInitializer {
|
|||
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
var player = handler.getPlayer();
|
||||
|
||||
var playerState = state.getPlayerState(player.getUuid());
|
||||
var playerData = PlayerData.getPlayer(player.getUuid());
|
||||
playerData.set(PlayerData.KEYS.username, player.getName().getString());
|
||||
var firstJoinedDate = playerData.getDate(PlayerData.KEYS.firstJoinedDate);
|
||||
boolean isNewPlayer = false;
|
||||
if (firstJoinedDate == null) {
|
||||
playerData.setDate(PlayerData.KEYS.firstJoinedDate, new Date());
|
||||
isNewPlayer = true;
|
||||
}
|
||||
if (isNewPlayer) {
|
||||
PlayerWelcome.PLAYER_WELCOME.invoker().playerWelcome(player, playerData, server);
|
||||
LOGGER.info("Player {} joined for the first time!", player.getName().getString());
|
||||
}
|
||||
});
|
||||
|
||||
if (playerState.firstJoinedDate == null) {
|
||||
LOGGER.info("Player {} joined for the first time!", player.getGameProfile().getName());
|
||||
playerState.firstJoinedDate = new Date();
|
||||
PlayerWelcome.PLAYER_WELCOME.invoker().playerWelcome(player, server);
|
||||
var serverState = state.getServerState();
|
||||
var spawnPosition = serverState.spawn;
|
||||
|
||||
if (spawnPosition != null) {
|
||||
spawnPosition.teleport(player, false);
|
||||
}
|
||||
|
||||
public void broadcastMessage(MinecraftServer server, Text message) {
|
||||
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
|
||||
player.sendMessage(message, false);
|
||||
playerData.setDate(PlayerData.KEYS.firstJoinedDate, new Date());
|
||||
}
|
||||
|
||||
if (playerState.username != null && !playerState.username.equals(player.getGameProfile().getName())) {
|
||||
LOGGER.info("Player {} has changed their username from {}", player.getGameProfile().getName(), playerState.username);
|
||||
PlayerUsernameChange.PLAYER_USERNAME_CHANGE.invoker().changeUsername(player, playerState.username);
|
||||
playerData.set(PlayerData.KEYS.username, player.getName().getString());
|
||||
}
|
||||
playerState.username = player.getGameProfile().getName();
|
||||
state.savePlayerState(player.getUuid(), playerState);
|
||||
});
|
||||
}
|
||||
|
||||
public void broadcastMessage(MinecraftServer server, Component message) {
|
||||
|
|
|
@ -28,4 +28,6 @@ public class RccServerConfigModel {
|
|||
public ArrayList<String> tabFooter = new ArrayList<>(List.of(
|
||||
"<gradient:#DEDE6C:#CC4C4C:{phase}><st> </st></gradient>"
|
||||
));
|
||||
|
||||
public String playerTabName = "%player:displayname_visual% %player:playtime%";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package cc.reconnected.server.commands.home;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
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.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class DeleteHomeCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("delhome")
|
||||
.requires(Permissions.require("rcc.command.delhome", true))
|
||||
.executes(context -> execute(context, "home"))
|
||||
.then(argument("name", StringArgumentType.word())
|
||||
.suggests((context, builder) -> {
|
||||
if (!context.getSource().isExecutedByPlayer())
|
||||
return CommandSource.suggestMatching(new String[]{}, builder);
|
||||
|
||||
var playerState = RccServer.state.getPlayerState(context.getSource().getPlayer().getUuid());
|
||||
return CommandSource.suggestMatching(playerState.homes.keySet().stream(), builder);
|
||||
})
|
||||
.executes(context -> execute(context, StringArgumentType.getString(context, "name"))));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context, String name) {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
var player = context.getSource().getPlayer();
|
||||
var playerState = RccServer.state.getPlayerState(player.getUuid());
|
||||
var homes = playerState.homes;
|
||||
|
||||
if (!homes.containsKey(name)) {
|
||||
context.getSource().sendFeedback(() -> Text.literal("The home ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" does not exist!")
|
||||
.formatted(Formatting.RED), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
homes.remove(name);
|
||||
RccServer.state.savePlayerState(player.getUuid(), playerState);
|
||||
|
||||
context.getSource().sendFeedback(() -> Text
|
||||
.literal("Home ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" deleted!")
|
||||
.formatted(Formatting.GREEN), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package cc.reconnected.server.commands.home;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
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.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.*;
|
||||
|
||||
public class HomeCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("home")
|
||||
.requires(Permissions.require("rcc.command.home", true))
|
||||
.executes(context -> execute(context, "home"))
|
||||
.then(argument("name", StringArgumentType.word())
|
||||
.suggests((context, builder) -> {
|
||||
if (!context.getSource().isExecutedByPlayer())
|
||||
return CommandSource.suggestMatching(new String[]{}, builder);
|
||||
|
||||
var playerState = RccServer.state.getPlayerState(context.getSource().getPlayer().getUuid());
|
||||
return CommandSource.suggestMatching(playerState.homes.keySet().stream(), builder);
|
||||
})
|
||||
.executes(context -> execute(context, StringArgumentType.getString(context, "name"))));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context, String name) {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
var player = context.getSource().getPlayer();
|
||||
var playerState = RccServer.state.getPlayerState(player.getUuid());
|
||||
var homes = playerState.homes;
|
||||
|
||||
if (!homes.containsKey(name)) {
|
||||
context.getSource().sendFeedback(() -> Text.literal("The home ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" does not exist!")
|
||||
.formatted(Formatting.RED), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
context.getSource().sendFeedback(() -> Text
|
||||
.literal("Teleporting to ")
|
||||
.append(Text.literal(name).formatted(Formatting.GREEN))
|
||||
.append("...")
|
||||
.formatted(Formatting.GOLD), false);
|
||||
|
||||
var homePosition = homes.get(name);
|
||||
homePosition.teleport(player);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package cc.reconnected.server.commands.home;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import cc.reconnected.server.util.Components;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.*;
|
||||
|
||||
public class SetHomeCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("sethome")
|
||||
.requires(Permissions.require("rcc.command.sethome", true))
|
||||
.executes(context -> execute(context,
|
||||
"home",
|
||||
false))
|
||||
.then(argument("name", StringArgumentType.word())
|
||||
.executes(context -> execute(context,
|
||||
StringArgumentType.getString(context, "name"),
|
||||
false))
|
||||
.then(argument("force", BoolArgumentType.bool())
|
||||
.executes(context -> execute(context,
|
||||
StringArgumentType.getString(context, "name"),
|
||||
BoolArgumentType.getBool(context, "force")))));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context, String name, boolean forced) {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
var player = context.getSource().getPlayer();
|
||||
var playerState = RccServer.state.getPlayerState(player.getUuid());
|
||||
|
||||
var homes = playerState.homes;
|
||||
|
||||
if (homes.containsKey(name) && !forced) {
|
||||
var text = Component.text("You already have set this home.")
|
||||
.appendNewline().appendSpace()
|
||||
.append(Components.makeButton(
|
||||
Component.text("Force set home", NamedTextColor.GOLD),
|
||||
Component.text("Click to force set the home"),
|
||||
"/sethome " + name + " true"
|
||||
));
|
||||
|
||||
context.getSource().sendFailure(text);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
var homePosition = new ServerPosition(player);
|
||||
homes.put(name, homePosition);
|
||||
|
||||
RccServer.state.savePlayerState(player.getUuid(), playerState);
|
||||
|
||||
context.getSource().sendFeedback(() -> Text.literal("New home ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" set!")
|
||||
.formatted(Formatting.GREEN), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.misc;
|
||||
|
||||
import cc.reconnected.server.core.AfkTracker;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
|
@ -1,4 +1,4 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.misc;
|
||||
|
||||
import cc.reconnected.server.core.BackTracker;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
|
@ -1,8 +1,6 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.misc;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.FloatArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
|
@ -1,4 +1,4 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.misc;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
|
@ -1,4 +1,4 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.misc;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
|
@ -0,0 +1,42 @@
|
|||
package cc.reconnected.server.commands.spawn;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class SetSpawnCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("setspawn")
|
||||
.requires(Permissions.require("rcc.command.setspawn", 3))
|
||||
.executes(context -> {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var player = context.getSource().getPlayer();
|
||||
var spawnPosition = new ServerPosition(player);
|
||||
|
||||
var serverState = RccServer.state.getServerState();
|
||||
serverState.spawn = spawnPosition;
|
||||
RccServer.state.saveServerState();
|
||||
|
||||
context.getSource().sendFeedback(() -> Text.literal("Server spawn set to ")
|
||||
.append(Text.literal(String.format("%.1f %.1f %.1f", spawnPosition.x, spawnPosition.y, spawnPosition.z))
|
||||
.formatted(Formatting.GOLD))
|
||||
.formatted(Formatting.GREEN), true);
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package cc.reconnected.server.commands.spawn;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class SpawnCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("spawn")
|
||||
.requires(Permissions.require("rcc.command.spawn", true))
|
||||
.executes(context -> {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var player = context.getSource().getPlayer();
|
||||
var serverState = RccServer.state.getServerState();
|
||||
var spawnPosition = serverState.spawn;
|
||||
if(spawnPosition == null) {
|
||||
var server = context.getSource().getServer();
|
||||
var spawnPos = server.getOverworld().getSpawnPos();
|
||||
spawnPosition = new ServerPosition(spawnPos.getX(), spawnPos.getY(), spawnPos.getZ(), 0, 0, server.getOverworld());
|
||||
}
|
||||
|
||||
context.getSource().sendFeedback(() -> Text.literal("Teleporting to spawn...").formatted(Formatting.GOLD), false);
|
||||
spawnPosition.teleport(player);
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.teleport;
|
||||
|
||||
import cc.reconnected.server.core.TeleportTracker;
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
|
@ -88,6 +89,7 @@ public class TeleportAcceptCommand {
|
|||
targetPlayer.sendMessage(Text.empty().append(player.getDisplayName()).append(Text.literal(" accepted your teleport request.").formatted(Formatting.GREEN)), false);
|
||||
}
|
||||
|
||||
TeleportTracker.teleport(sourcePlayer, targetPlayer);
|
||||
var targetPosition = new ServerPosition(targetPlayer);
|
||||
targetPosition.teleport(sourcePlayer);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.teleport;
|
||||
|
||||
import cc.reconnected.server.core.TeleportTracker;
|
||||
import cc.reconnected.server.util.Components;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
@ -60,9 +61,9 @@ public class TeleportAskCommand {
|
|||
.appendSpace()
|
||||
.append(Component.text("requested to teleport to you.", NamedTextColor.GOLD))
|
||||
.appendNewline().appendSpace()
|
||||
.append(TeleportTracker.makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
|
||||
.append(Components.makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
|
||||
.appendSpace()
|
||||
.append(TeleportTracker.makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
|
||||
.append(Components.makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
|
||||
|
||||
target.sendMessage(requestMessage);
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.teleport;
|
||||
|
||||
import cc.reconnected.server.core.TeleportTracker;
|
||||
import cc.reconnected.server.util.Components;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
@ -61,9 +62,9 @@ public class TeleportAskHereCommand {
|
|||
.appendSpace()
|
||||
.append(Component.text("requested you to teleport to them.", NamedTextColor.GOLD))
|
||||
.appendNewline().appendSpace()
|
||||
.append(TeleportTracker.makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
|
||||
.append(Components.makeButton(Component.text("Accept", NamedTextColor.GREEN), Component.text("Click to accept request"), "/tpaccept " + request.requestId))
|
||||
.appendSpace()
|
||||
.append(TeleportTracker.makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
|
||||
.append(Components.makeButton(Component.text("Refuse", NamedTextColor.RED), Component.text("Click to refuse request"), "/tpdeny " + request.requestId));
|
||||
|
||||
target.sendMessage(requestMessage);
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.teleport;
|
||||
|
||||
import cc.reconnected.server.core.TeleportTracker;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
|
@ -1,4 +1,4 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.tell;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
|
@ -1,4 +1,4 @@
|
|||
package cc.reconnected.server.commands;
|
||||
package cc.reconnected.server.commands.tell;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.parser.MarkdownParser;
|
||||
|
@ -6,6 +6,9 @@ import com.mojang.brigadier.CommandDispatcher;
|
|||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
|
@ -67,6 +70,17 @@ public class TellCommand {
|
|||
}
|
||||
|
||||
var parsedMessage = MarkdownParser.defaultParser.parseNode(message);
|
||||
var you = Component.text("You", NamedTextColor.GRAY, TextDecoration.ITALIC);
|
||||
var sourceText = MiniMessage.miniMessage().deserialize(RccServer.CONFIG.tellMessage(),
|
||||
Placeholder.component("source", you),
|
||||
Placeholder.component("target", targetDisplayName),
|
||||
Placeholder.component("message", parsedMessage.toText()));
|
||||
|
||||
var targetText = MiniMessage.miniMessage().deserialize(RccServer.CONFIG.tellMessage(),
|
||||
Placeholder.component("source", source.getDisplayName()),
|
||||
Placeholder.component("target", you),
|
||||
Placeholder.component("message", parsedMessage.toText()));
|
||||
|
||||
var text = MiniMessage.miniMessage().deserialize(RccServer.CONFIG.tellMessage(),
|
||||
Placeholder.component("source", source.getDisplayName()),
|
||||
Placeholder.component("target", targetDisplayName),
|
||||
|
@ -76,16 +90,16 @@ public class TellCommand {
|
|||
lastSender.put(source.getName(), targetName);
|
||||
|
||||
if (!source.getName().equals(targetName)) {
|
||||
source.sendMessage(text);
|
||||
source.sendMessage(sourceText);
|
||||
}
|
||||
if (targetPlayer != null) {
|
||||
targetPlayer.sendMessage(text);
|
||||
targetPlayer.sendMessage(targetText);
|
||||
if (source.isExecutedByPlayer()) {
|
||||
source.getServer().sendMessage(text);
|
||||
}
|
||||
} else {
|
||||
// avoid duped message
|
||||
source.getServer().sendMessage(text);
|
||||
source.getServer().sendMessage(targetText);
|
||||
}
|
||||
|
||||
var lp = RccServer.getInstance().luckPerms();
|
||||
|
@ -103,7 +117,6 @@ public class TellCommand {
|
|||
if (playerPerms.checkPermission("rcc.tell.spy").asBoolean()) {
|
||||
player.sendMessage(spyText);
|
||||
}
|
||||
;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package cc.reconnected.server.commands.warp;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
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.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class DeleteWarpCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("delwarp")
|
||||
.requires(Permissions.require("rcc.command.delwarp", 3))
|
||||
.then(argument("name", StringArgumentType.word())
|
||||
.suggests((context, builder) -> {
|
||||
if (!context.getSource().isExecutedByPlayer())
|
||||
return CommandSource.suggestMatching(new String[]{}, builder);
|
||||
|
||||
var serverState = RccServer.state.getServerState();
|
||||
return CommandSource.suggestMatching(serverState.warps.keySet().stream(), builder);
|
||||
})
|
||||
.executes(context -> execute(context, StringArgumentType.getString(context, "name"))));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context, String name) {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
var player = context.getSource().getPlayer();
|
||||
var serverState = RccServer.state.getServerState();
|
||||
var warps = serverState.warps;
|
||||
|
||||
if (!warps.containsKey(name)) {
|
||||
context.getSource().sendFeedback(() -> Text.literal("The warp ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" does not exist!")
|
||||
.formatted(Formatting.RED), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
warps.remove(name);
|
||||
RccServer.state.saveServerState();
|
||||
|
||||
context.getSource().sendFeedback(() -> Text
|
||||
.literal("Warp ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" deleted!")
|
||||
.formatted(Formatting.GREEN), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package cc.reconnected.server.commands.warp;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.*;
|
||||
|
||||
public class SetWarpCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("setwarp")
|
||||
.requires(Permissions.require("rcc.command.setwarp", 3))
|
||||
.then(argument("name", StringArgumentType.word())
|
||||
.executes(context -> execute(context,
|
||||
StringArgumentType.getString(context, "name"))));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context, String name) {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
var player = context.getSource().getPlayer();
|
||||
var serverState = RccServer.state.getServerState();
|
||||
|
||||
var warps = serverState.warps;
|
||||
|
||||
var warpPosition = new ServerPosition(player);
|
||||
warps.put(name, warpPosition);
|
||||
|
||||
RccServer.state.saveServerState();
|
||||
|
||||
context.getSource().sendFeedback(() -> Text.literal("New warp ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" set!")
|
||||
.formatted(Formatting.GREEN), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package cc.reconnected.server.commands.warp;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
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.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.*;
|
||||
|
||||
public class WarpCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||
var rootCommand = literal("warp")
|
||||
.requires(Permissions.require("rcc.command.warp", true))
|
||||
.then(argument("name", StringArgumentType.word())
|
||||
.suggests((context, builder) -> {
|
||||
if (!context.getSource().isExecutedByPlayer())
|
||||
return CommandSource.suggestMatching(new String[]{}, builder);
|
||||
|
||||
var serverState = RccServer.state.getServerState();
|
||||
return CommandSource.suggestMatching(serverState.warps.keySet().stream(), builder);
|
||||
})
|
||||
.executes(context -> execute(context, StringArgumentType.getString(context, "name"))));
|
||||
|
||||
dispatcher.register(rootCommand);
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> context, String name) {
|
||||
if (!context.getSource().isExecutedByPlayer()) {
|
||||
context.getSource().sendFeedback(() -> Text.of("This command can only be executed by players!"), false);
|
||||
return 1;
|
||||
}
|
||||
var player = context.getSource().getPlayer();
|
||||
var serverState = RccServer.state.getServerState();
|
||||
var warps = serverState.warps;
|
||||
|
||||
if (!warps.containsKey(name)) {
|
||||
context.getSource().sendFeedback(() -> Text.literal("The warp ")
|
||||
.append(Text.literal(name).formatted(Formatting.GOLD))
|
||||
.append(" does not exist!")
|
||||
.formatted(Formatting.RED), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
context.getSource().sendFeedback(() -> Text
|
||||
.literal("Teleporting to ")
|
||||
.append(Text.literal(name).formatted(Formatting.GREEN))
|
||||
.append("...")
|
||||
.formatted(Formatting.GOLD), false);
|
||||
|
||||
var warpPosition = warps.get(name);
|
||||
warpPosition.teleport(player);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package cc.reconnected.server.core;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.data.StateSaverAndLoader;
|
||||
import cc.reconnected.server.events.PlayerActivityEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.event.player.*;
|
||||
|
@ -16,16 +15,17 @@ import net.minecraft.text.Text;
|
|||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class AfkTracker {
|
||||
private static final int cycleDelay = 1;
|
||||
private static final int absentTimeTrigger = RccServer.CONFIG.afkTimeTrigger() * 20; // seconds * 20 ticks
|
||||
|
||||
private final HashMap<UUID, PlayerState> playerStates = new HashMap<>();
|
||||
private final ConcurrentHashMap<UUID, PlayerActivityState> playerActivityStates = new ConcurrentHashMap<>();
|
||||
|
||||
private static final AfkTracker instance = new AfkTracker();
|
||||
|
||||
public static AfkTracker getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
@ -63,20 +63,16 @@ public class AfkTracker {
|
|||
}
|
||||
|
||||
private AfkTracker() {
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
if (server.getTicks() % cycleDelay == 0) {
|
||||
updatePlayers(server);
|
||||
}
|
||||
});
|
||||
ServerTickEvents.END_SERVER_TICK.register(this::updatePlayers);
|
||||
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
final var player = handler.getPlayer();
|
||||
playerStates.put(player.getUuid(), new PlayerState(player, server.getTicks()));
|
||||
playerActivityStates.put(player.getUuid(), new PlayerActivityState(player, server.getTicks()));
|
||||
});
|
||||
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
||||
updatePlayerActiveTime(handler.getPlayer(), server.getTicks());
|
||||
playerStates.remove(handler.getPlayer().getUuid());
|
||||
playerActivityStates.remove(handler.getPlayer().getUuid());
|
||||
|
||||
// sync to LP
|
||||
//var activeTime = String.valueOf(getActiveTime(handler.getPlayer()));
|
||||
|
@ -125,7 +121,7 @@ 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));
|
||||
var playerState = playerActivityStates.computeIfAbsent(player.getUuid(), uuid -> new PlayerActivityState(player, currentTick));
|
||||
|
||||
var oldPosition = playerState.position;
|
||||
var newPosition = new PlayerPosition(player);
|
||||
|
@ -147,11 +143,11 @@ public class AfkTracker {
|
|||
}
|
||||
|
||||
private void updatePlayerActiveTime(ServerPlayerEntity player, int currentTick) {
|
||||
var playerState = playerStates.get(player.getUuid());
|
||||
if (!playerState.isAfk) {
|
||||
var worldPlayerData = StateSaverAndLoader.getPlayerState(player);
|
||||
var interval = currentTick - playerState.activeStart;
|
||||
worldPlayerData.activeTime += interval / 20;
|
||||
var playerActivityState = playerActivityStates.get(player.getUuid());
|
||||
if (!playerActivityState.isAfk) {
|
||||
var playerState = RccServer.state.getPlayerState(player.getUuid());
|
||||
var interval = currentTick - playerActivityState.activeStart;
|
||||
playerState.activeTime += interval / 20;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,10 +159,10 @@ public class AfkTracker {
|
|||
}
|
||||
|
||||
private void resetAfkState(ServerPlayerEntity player, MinecraftServer server) {
|
||||
if (!playerStates.containsKey(player.getUuid()))
|
||||
if (!playerActivityStates.containsKey(player.getUuid()))
|
||||
return;
|
||||
|
||||
var playerState = playerStates.get(player.getUuid());
|
||||
var playerState = playerActivityStates.get(player.getUuid());
|
||||
playerState.lastUpdate = server.getTicks();
|
||||
if (playerState.isAfk) {
|
||||
playerState.isAfk = false;
|
||||
|
@ -200,13 +196,13 @@ public class AfkTracker {
|
|||
}
|
||||
}
|
||||
|
||||
public static class PlayerState {
|
||||
public static class PlayerActivityState {
|
||||
public PlayerPosition position;
|
||||
public int lastUpdate;
|
||||
public boolean isAfk;
|
||||
public int activeStart;
|
||||
|
||||
public PlayerState(ServerPlayerEntity player, int lastUpdate) {
|
||||
public PlayerActivityState(ServerPlayerEntity player, int lastUpdate) {
|
||||
this.position = new PlayerPosition(player);
|
||||
this.lastUpdate = lastUpdate;
|
||||
this.isAfk = false;
|
||||
|
@ -215,21 +211,21 @@ public class AfkTracker {
|
|||
}
|
||||
|
||||
public boolean isPlayerAfk(UUID playerUuid) {
|
||||
if (!playerStates.containsKey(playerUuid)) {
|
||||
if (!playerActivityStates.containsKey(playerUuid)) {
|
||||
return false;
|
||||
}
|
||||
return playerStates.get(playerUuid).isAfk;
|
||||
return playerActivityStates.get(playerUuid).isAfk;
|
||||
}
|
||||
|
||||
public void setPlayerAfk(ServerPlayerEntity player, boolean afk) {
|
||||
if (!playerStates.containsKey(player.getUuid())) {
|
||||
if (!playerActivityStates.containsKey(player.getUuid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
var server = player.getWorld().getServer();
|
||||
|
||||
if (afk) {
|
||||
playerStates.get(player.getUuid()).lastUpdate = -absentTimeTrigger - 20; // just to be sure
|
||||
playerActivityStates.get(player.getUuid()).lastUpdate = -absentTimeTrigger - 20; // just to be sure
|
||||
} else {
|
||||
resetAfkState(player, server);
|
||||
}
|
||||
|
@ -238,7 +234,7 @@ public class AfkTracker {
|
|||
}
|
||||
|
||||
public int getActiveTime(ServerPlayerEntity player) {
|
||||
var worldPlayerData = StateSaverAndLoader.getPlayerState(player);
|
||||
return worldPlayerData.activeTime;
|
||||
var playerState = RccServer.state.getPlayerState(player.getUuid());
|
||||
return playerState.activeTime;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ public class BackTracker {
|
|||
|
||||
public static void register() {
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
||||
BackTracker.lastPlayerPositions.remove(handler.getPlayer().getUuid());
|
||||
lastPlayerPositions.remove(handler.getPlayer().getUuid());
|
||||
});
|
||||
|
||||
PlayerTeleport.EVENT.register((player, origin, destination) -> {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package cc.reconnected.server.core;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.util.Components;
|
||||
import eu.pb4.placeholders.api.PlaceholderContext;
|
||||
import eu.pb4.placeholders.api.Placeholders;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class TabList {
|
||||
public static void register() {
|
||||
|
@ -42,8 +41,8 @@ public class TabList {
|
|||
footerComponent = footerComponent.append(minimessage.deserialize(line));
|
||||
}
|
||||
|
||||
var parsedHeader = Placeholders.parseText(toText(headerComponent), playerContext);
|
||||
var parsedFooter = Placeholders.parseText(toText(footerComponent), playerContext);
|
||||
var parsedHeader = Placeholders.parseText(Components.toText(headerComponent), playerContext);
|
||||
var parsedFooter = Placeholders.parseText(Components.toText(footerComponent), playerContext);
|
||||
|
||||
var audience = RccServer.getInstance().adventure().player(player.getUuid());
|
||||
audience.sendPlayerListHeaderAndFooter(parsedHeader, parsedFooter);
|
||||
|
@ -52,8 +51,4 @@ public class TabList {
|
|||
|
||||
}
|
||||
|
||||
public static Text toText(Component component) {
|
||||
var json = JSONComponentSerializer.json().serialize(component);
|
||||
return Text.Serializer.fromJson(json);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
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;
|
||||
|
@ -30,36 +22,9 @@ public class TeleportTracker {
|
|||
});
|
||||
});
|
||||
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
teleportRequests.put(handler.getPlayer().getUuid(), new ConcurrentLinkedDeque<>());
|
||||
});
|
||||
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()
|
||||
);
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> TeleportTracker.teleportRequests.remove(handler.getPlayer().getUuid()));
|
||||
}
|
||||
|
||||
public static class TeleportRequest {
|
||||
|
|
29
src/main/java/cc/reconnected/server/data/PlayerState.java
Normal file
29
src/main/java/cc/reconnected/server/data/PlayerState.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package cc.reconnected.server.data;
|
||||
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PlayerState {
|
||||
public boolean dirty = false;
|
||||
public boolean saving = false;
|
||||
|
||||
@Expose
|
||||
public UUID uuid;
|
||||
@Expose
|
||||
public String username;
|
||||
@Expose
|
||||
public @Nullable Date firstJoinedDate;
|
||||
@Expose
|
||||
public @Nullable ServerPosition logoffPosition = null;
|
||||
@Expose
|
||||
public int activeTime = 0;
|
||||
@Expose
|
||||
public ConcurrentHashMap<String, ServerPosition> homes = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
}
|
18
src/main/java/cc/reconnected/server/data/ServerState.java
Normal file
18
src/main/java/cc/reconnected/server/data/ServerState.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
package cc.reconnected.server.data;
|
||||
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ServerState {
|
||||
public boolean dirty = false;
|
||||
public boolean saving = false;
|
||||
|
||||
@Expose
|
||||
public @Nullable ServerPosition spawn;
|
||||
@Expose
|
||||
public ConcurrentHashMap<String, ServerPosition> warps = new ConcurrentHashMap<>();
|
||||
|
||||
}
|
161
src/main/java/cc/reconnected/server/data/StateManager.java
Normal file
161
src/main/java/cc/reconnected/server/data/StateManager.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
package cc.reconnected.server.data;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import cc.reconnected.server.struct.ServerPosition;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class StateManager {
|
||||
private static final Charset charset = StandardCharsets.UTF_8;
|
||||
private ServerState serverState;
|
||||
private final ConcurrentHashMap<UUID, PlayerState> playerStates = new ConcurrentHashMap<>();
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX") // iso 8601 my beloved
|
||||
.create();
|
||||
|
||||
private Path basePath;
|
||||
private Path playersPath;
|
||||
private Path serverDataPath;
|
||||
|
||||
public StateManager() {
|
||||
}
|
||||
|
||||
public void register(Path path) {
|
||||
basePath = path;
|
||||
playersPath = basePath.resolve("players");
|
||||
serverDataPath = basePath.resolve("data.json");
|
||||
|
||||
if (!basePath.toFile().exists()) {
|
||||
if (!basePath.toFile().mkdirs()) {
|
||||
RccServer.LOGGER.error("Could not create directory: {}", basePath.toAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (!playersPath.toFile().exists()) {
|
||||
if (!playersPath.toFile().mkdirs()) {
|
||||
RccServer.LOGGER.error("Could not create directory: {}", playersPath.toAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (serverDataPath.toFile().exists()) {
|
||||
try (var br = new BufferedReader(new FileReader(serverDataPath.toFile(), charset))) {
|
||||
serverState = gson.fromJson(br, ServerState.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
serverState = new ServerState();
|
||||
serverState.dirty = true;
|
||||
}
|
||||
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register(serverState -> save());
|
||||
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> loadPlayerState(handler.player.getUuid()));
|
||||
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
|
||||
var playerState = getPlayerState(handler.getPlayer().getUuid());
|
||||
playerState.logoffPosition = new ServerPosition(handler.getPlayer());
|
||||
savePlayerState(handler.getPlayer().getUuid(), playerState);
|
||||
playerStates.remove(handler.player.getUuid());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void saveServerState() {
|
||||
if (!serverState.dirty)
|
||||
return;
|
||||
|
||||
if (serverState.saving)
|
||||
return;
|
||||
serverState.saving = true;
|
||||
|
||||
var json = gson.toJson(serverState, ServerState.class);
|
||||
try (var fw = new FileWriter(serverDataPath.toFile(), charset)) {
|
||||
fw.write(json);
|
||||
serverState.dirty = false;
|
||||
} catch (IOException e) {
|
||||
RccServer.LOGGER.error("Could not save server state", e);
|
||||
}
|
||||
serverState.saving = false;
|
||||
}
|
||||
|
||||
public void savePlayerState(UUID uuid, PlayerState playerState) {
|
||||
if (!playerState.dirty)
|
||||
return;
|
||||
|
||||
if (playerState.saving)
|
||||
return;
|
||||
playerState.saving = true;
|
||||
|
||||
var path = playersPath.resolve(uuid.toString() + ".json");
|
||||
var json = gson.toJson(playerState, PlayerState.class);
|
||||
try (var fw = new FileWriter(path.toFile(), charset)) {
|
||||
fw.write(json);
|
||||
playerState.dirty = false;
|
||||
} catch (IOException e) {
|
||||
RccServer.LOGGER.error("Could not save player state", e);
|
||||
}
|
||||
playerState.saving = false;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
saveServerState();
|
||||
playerStates.forEach(this::savePlayerState);
|
||||
}
|
||||
|
||||
private PlayerState loadPlayerState(UUID uuid) {
|
||||
var path = playersPath.resolve(uuid.toString() + ".json");
|
||||
PlayerState playerState;
|
||||
if (path.toFile().exists()) {
|
||||
try (var br = new BufferedReader(new FileReader(path.toFile(), charset))) {
|
||||
playerState = gson.fromJson(br, PlayerState.class);
|
||||
} catch (Exception e) {
|
||||
RccServer.LOGGER.error("Could not load player state: " + path.toAbsolutePath(), e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
playerState = new PlayerState();
|
||||
playerState.dirty = true;
|
||||
}
|
||||
|
||||
playerStates.put(uuid, playerState);
|
||||
playerState.uuid = uuid;
|
||||
|
||||
return playerState;
|
||||
}
|
||||
|
||||
public PlayerState getPlayerState(UUID uuid) {
|
||||
PlayerState playerState;
|
||||
if (playerStates.containsKey(uuid)) {
|
||||
playerState = playerStates.get(uuid);
|
||||
} else {
|
||||
playerState = loadPlayerState(uuid);
|
||||
}
|
||||
|
||||
if (playerState == null)
|
||||
return null;
|
||||
|
||||
playerState.dirty = true;
|
||||
return playerState;
|
||||
}
|
||||
|
||||
public ServerState getServerState() {
|
||||
serverState.dirty = true;
|
||||
return serverState;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package cc.reconnected.server.data;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.PersistentState;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class StateSaverAndLoader extends PersistentState {
|
||||
public final HashMap<UUID, WorldPlayerData> players = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public NbtCompound writeNbt(NbtCompound nbt) {
|
||||
var playersNbt = new NbtCompound();
|
||||
players.forEach((uuid, data) -> {
|
||||
var playerNbt = new NbtCompound();
|
||||
playerNbt.putInt("activeTime", data.activeTime);
|
||||
playersNbt.put(uuid.toString(), playerNbt);
|
||||
});
|
||||
nbt.put("players", playersNbt);
|
||||
|
||||
return nbt;
|
||||
}
|
||||
|
||||
public static StateSaverAndLoader createFromNbt(NbtCompound nbt) {
|
||||
var state = new StateSaverAndLoader();
|
||||
|
||||
var playersNbt = nbt.getCompound("players");
|
||||
playersNbt.getKeys().forEach(key -> {
|
||||
var playerData = new WorldPlayerData();
|
||||
|
||||
playerData.activeTime = playersNbt.getCompound(key).getInt("activeTime");
|
||||
UUID uuid = UUID.fromString(key);
|
||||
state.players.put(uuid, playerData);
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public static StateSaverAndLoader getServerState(MinecraftServer server) {
|
||||
var persistentStateManager = server.getWorld(World.OVERWORLD).getPersistentStateManager();
|
||||
var state = persistentStateManager.getOrCreate(
|
||||
StateSaverAndLoader::createFromNbt,
|
||||
StateSaverAndLoader::new,
|
||||
RccServer.MOD_ID
|
||||
);
|
||||
state.markDirty();
|
||||
return state;
|
||||
}
|
||||
|
||||
public static WorldPlayerData getPlayerState(LivingEntity player) {
|
||||
var serverState = getServerState(player.getWorld().getServer());
|
||||
return serverState.players.computeIfAbsent(player.getUuid(), uuid -> new WorldPlayerData());
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package cc.reconnected.server.data;
|
||||
|
||||
public class WorldPlayerData {
|
||||
public int activeTime = 0;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cc.reconnected.server.events;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
public interface PlayerUsernameChange {
|
||||
Event<PlayerUsernameChange> PLAYER_USERNAME_CHANGE = EventFactory.createArrayBacked(PlayerUsernameChange.class,
|
||||
(listeners) -> (player, previousUsername) -> {
|
||||
for (PlayerUsernameChange listener : listeners) {
|
||||
listener.changeUsername(player, previousUsername);
|
||||
}
|
||||
});
|
||||
|
||||
void changeUsername(ServerPlayerEntity player, String previousUsername);
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package cc.reconnected.server.events;
|
||||
|
||||
import cc.reconnected.server.database.PlayerData;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
@ -8,11 +7,11 @@ import net.minecraft.server.network.ServerPlayerEntity;
|
|||
|
||||
public interface PlayerWelcome {
|
||||
Event<PlayerWelcome> PLAYER_WELCOME = EventFactory.createArrayBacked(PlayerWelcome.class,
|
||||
(listeners) -> (player, playerData, server) -> {
|
||||
(listeners) -> (player, server) -> {
|
||||
for (PlayerWelcome listener : listeners) {
|
||||
listener.playerWelcome(player, playerData, server);
|
||||
listener.playerWelcome(player, server);
|
||||
}
|
||||
});
|
||||
|
||||
void playerWelcome(ServerPlayerEntity player, PlayerData playerData, MinecraftServer server);
|
||||
void playerWelcome(ServerPlayerEntity player, MinecraftServer server);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package cc.reconnected.server.mixin;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(ServerPlayNetworkHandler.class)
|
||||
public abstract class ServerPlayNetworkManagerMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftServer server;
|
||||
@Shadow
|
||||
public ServerPlayerEntity player;
|
||||
|
||||
@Shadow
|
||||
public abstract void sendPacket(Packet<?> packet);
|
||||
|
||||
@Inject(method = "tick", at = @At("TAIL"))
|
||||
private void rccServer$updatePlayerList(CallbackInfo ci) {
|
||||
if(RccServer.CONFIG.enableTabList()) {
|
||||
var packet = new PlayerListS2CPacket(EnumSet.of(PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME, PlayerListS2CPacket.Action.UPDATE_LISTED), List.of(this.player));
|
||||
this.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cc.reconnected.server.mixin;
|
||||
|
||||
import cc.reconnected.server.RccServer;
|
||||
import eu.pb4.placeholders.api.PlaceholderContext;
|
||||
import eu.pb4.placeholders.api.Placeholders;
|
||||
import eu.pb4.placeholders.api.parsers.NodeParser;
|
||||
import eu.pb4.placeholders.api.parsers.TextParserV1;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public class ServerPlayerEntityMixin {
|
||||
@Unique
|
||||
private static final NodeParser parser = NodeParser.merge(TextParserV1.DEFAULT, Placeholders.DEFAULT_PLACEHOLDER_PARSER);
|
||||
|
||||
@Inject(method = "getPlayerListName", at = @At("HEAD"), cancellable = true)
|
||||
private void rccServer$customizePlayerListName(CallbackInfoReturnable<Text> callback) {
|
||||
if (RccServer.CONFIG.enableTabList()) {
|
||||
var player = (ServerPlayerEntity) (Object) this;
|
||||
var playerContext = PlaceholderContext.of(player);
|
||||
var text = Placeholders.parseText(parser.parseNode(RccServer.CONFIG.playerTabName()), playerContext);
|
||||
callback.setReturnValue(text);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,26 @@
|
|||
package cc.reconnected.server.struct;
|
||||
|
||||
import cc.reconnected.server.core.BackTracker;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ServerPosition {
|
||||
@Expose
|
||||
public double x;
|
||||
@Expose
|
||||
public double y;
|
||||
@Expose
|
||||
public double z;
|
||||
@Expose
|
||||
public float yaw;
|
||||
@Expose
|
||||
public float pitch;
|
||||
public ServerWorld world;
|
||||
@Expose
|
||||
public String world;
|
||||
|
||||
public ServerPosition(double x, double y, double z, float yaw, float pitch, ServerWorld world) {
|
||||
this.x = x;
|
||||
|
@ -18,7 +28,7 @@ public class ServerPosition {
|
|||
this.z = z;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.world = world;
|
||||
this.world = world.getRegistryKey().getValue().toString();
|
||||
}
|
||||
|
||||
public ServerPosition(ServerPlayerEntity player) {
|
||||
|
@ -27,15 +37,19 @@ public class ServerPosition {
|
|||
this.z = player.getZ();
|
||||
this.yaw = player.getYaw();
|
||||
this.pitch = player.getPitch();
|
||||
this.world = player.getServerWorld();
|
||||
this.world = player.getServerWorld().getRegistryKey().getValue().toString();
|
||||
}
|
||||
|
||||
public void teleport(ServerPlayerEntity player) {
|
||||
public void teleport(ServerPlayerEntity player, boolean setBackPosition) {
|
||||
if (setBackPosition) {
|
||||
var currentPosition = new ServerPosition(player);
|
||||
BackTracker.lastPlayerPositions.put(player.getUuid(), currentPosition);
|
||||
}
|
||||
|
||||
var serverWorld = player.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, new Identifier(this.world)));
|
||||
|
||||
player.teleport(
|
||||
this.world,
|
||||
serverWorld,
|
||||
this.x,
|
||||
this.y,
|
||||
this.z,
|
||||
|
@ -43,4 +57,8 @@ public class ServerPosition {
|
|||
this.pitch
|
||||
);
|
||||
}
|
||||
|
||||
public void teleport(ServerPlayerEntity player) {
|
||||
teleport(player, true);
|
||||
}
|
||||
}
|
||||
|
|
26
src/main/java/cc/reconnected/server/util/Components.java
Normal file
26
src/main/java/cc/reconnected/server/util/Components.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package cc.reconnected.server.util;
|
||||
|
||||
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.kyori.adventure.text.serializer.json.JSONComponentSerializer;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class Components {
|
||||
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 Text toText(Component component) {
|
||||
var json = JSONComponentSerializer.json().serialize(component);
|
||||
return Text.Serializer.fromJson(json);
|
||||
}
|
||||
}
|
|
@ -3,10 +3,13 @@
|
|||
"minVersion": "0.8",
|
||||
"package": "cc.reconnected.server.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [],
|
||||
"mixins": [
|
||||
"ServerPlayNetworkManagerMixin"
|
||||
],
|
||||
"client": [],
|
||||
"server": [
|
||||
"MessageCommandMixin"
|
||||
"MessageCommandMixin",
|
||||
"ServerPlayerEntityMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
Loading…
Reference in a new issue