11package ict .minesunshineone .simpleTransfer .command ;
22
3+ import com .velocitypowered .api .command .CommandSource ;
34import com .velocitypowered .api .command .SimpleCommand ;
45import com .velocitypowered .api .proxy .Player ;
6+ import ict .minesunshineone .simpleTransfer .SimpleTransfer ;
57import ict .minesunshineone .simpleTransfer .config .ConfigManager ;
68import ict .minesunshineone .simpleTransfer .connection .PlayerConnectionManager ;
79import ict .minesunshineone .simpleTransfer .model .ServerInfo ;
1214import java .util .ArrayList ;
1315import java .util .List ;
1416import java .util .Map ;
17+ import java .util .UUID ;
1518
1619public class MainCommand implements SimpleCommand {
1720
@@ -20,14 +23,17 @@ public class MainCommand implements SimpleCommand {
2023 private final CommandRegistry commandRegistry ;
2124 private final PlayerConnectionManager connectionManager ;
2225 private final com .velocitypowered .api .proxy .ProxyServer server ;
26+ private final SimpleTransfer plugin ;
2327
2428 public MainCommand (ConfigManager configManager , PingMonitor pingMonitor , CommandRegistry commandRegistry ,
25- PlayerConnectionManager connectionManager , com .velocitypowered .api .proxy .ProxyServer server ) {
29+ PlayerConnectionManager connectionManager , com .velocitypowered .api .proxy .ProxyServer server ,
30+ SimpleTransfer plugin ) {
2631 this .configManager = configManager ;
2732 this .pingMonitor = pingMonitor ;
2833 this .commandRegistry = commandRegistry ;
2934 this .connectionManager = connectionManager ;
3035 this .server = server ;
36+ this .plugin = plugin ;
3137 }
3238
3339 @ Override
@@ -786,78 +792,165 @@ private void showPlayerStatus(Invocation invocation, String[] args) {
786792 * 管理员命令: 手动转移玩家线路
787793 */
788794 private void adminTransferPlayer (Invocation invocation , String [] args ) {
789- // 权限检查
790- if (!invocation .source ().hasPermission ("simpletransfer.admin" )) {
791- invocation .source ().sendMessage (Component .text ()
795+ CommandSource source = invocation .source ();
796+
797+ if (!source .hasPermission ("simpletransfer.admin" )) {
798+ source .sendMessage (Component .text ()
792799 .append (Component .text (" ✘ " , NamedTextColor .RED ))
793800 .append (Component .text ("您没有权限使用此命令!" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
794801 .build ());
795802 return ;
796803 }
797-
804+
798805 if (args .length < 3 ) {
799- invocation . source () .sendMessage (Component .text ()
806+ source .sendMessage (Component .text ()
800807 .append (Component .text (" ✘ " , NamedTextColor .RED ))
801- .append (Component .text ("用法: /st transfer <玩家名 > <线路>" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
808+ .append (Component .text ("用法: /st transfer <玩家名或UUID > <线路>" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
802809 .build ());
803- invocation . source () .sendMessage (Component .text ()
810+ source .sendMessage (Component .text ()
804811 .append (Component .text (" ➤ " , net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
805812 .append (Component .text ("可用线路: " , NamedTextColor .WHITE ))
806- .append (Component .text (String .join (", " , configManager .getTransferServers ().keySet ()),
813+ .append (Component .text (String .join (", " , configManager .getTransferServers ().keySet ()),
807814 net .kyori .adventure .text .format .TextColor .color (0xAAFFFF )))
808815 .build ());
809816 return ;
810817 }
811-
812- String playerName = args [1 ];
818+
819+ String targetInput = args [1 ];
813820 String routeName = args [2 ].toLowerCase ();
814-
815- Player targetPlayer = server .getPlayer (playerName ).orElse (null );
816-
817- if (targetPlayer == null ) {
818- invocation .source ().sendMessage (Component .text ()
819- .append (Component .text (" ✘ " , NamedTextColor .RED ))
820- .append (Component .text ("玩家 " + playerName + " 不在线或不存在!" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
821- .build ());
822- return ;
823- }
824-
821+
825822 ServerInfo serverInfo = configManager .getTransferServers ().get (routeName );
826-
823+
827824 if (serverInfo == null ) {
828- invocation . source () .sendMessage (Component .text ()
825+ source .sendMessage (Component .text ()
829826 .append (Component .text (" ✘ " , NamedTextColor .RED ))
830827 .append (Component .text ("线路 " + routeName + " 不存在!" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
831828 .build ());
832- invocation . source () .sendMessage (Component .text ()
829+ source .sendMessage (Component .text ()
833830 .append (Component .text (" ➤ " , net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
834831 .append (Component .text ("可用线路: " , NamedTextColor .WHITE ))
835- .append (Component .text (String .join (", " , configManager .getTransferServers ().keySet ()),
832+ .append (Component .text (String .join (", " , configManager .getTransferServers ().keySet ()),
836833 net .kyori .adventure .text .format .TextColor .color (0xAAFFFF )))
837834 .build ());
838835 return ;
839836 }
840-
841- // 执行转移
842- invocation .source ().sendMessage (Component .text (" " ));
843- invocation .source ().sendMessage (Component .text ()
844- .append (Component .text ("正在将玩家 " , NamedTextColor .WHITE ))
845- .append (Component .text (targetPlayer .getUsername (), net .kyori .adventure .text .format .TextColor .color (0x00FF88 )))
846- .append (Component .text (" 转移到线路 " , NamedTextColor .WHITE ))
847- .append (Component .text ("[" + routeName .toUpperCase () + "]" , net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
848- .append (Component .text ("..." , NamedTextColor .WHITE ))
849- .build ());
850- invocation .source ().sendMessage (Component .text (" " ));
851-
852- // 通知目标玩家
853- targetPlayer .sendMessage (Component .text (" " ));
854- targetPlayer .sendMessage (Component .text ()
855- .append (Component .text ("管理员正在将您转移到线路 " , NamedTextColor .WHITE ))
856- .append (Component .text ("[" + serverInfo .getDisplayName () + "]" , net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
837+
838+ Player onlinePlayer = server .getPlayer (targetInput ).orElse (null );
839+
840+ if (onlinePlayer != null ) {
841+ // 若在线玩家为 Geyser/Floodgate(Bedrock)连接,则跳过处理
842+ if (connectionManager .isFloodgatePlayer (onlinePlayer .getUniqueId ())) {
843+ source .sendMessage (Component .text ()
844+ .append (Component .text (" ✦ " , NamedTextColor .GOLD ))
845+ .append (Component .text ("目标玩家通过 Geyser/Floodgate 连接,已跳过 SimpleTransfer 处理" , net .kyori .adventure .text .format .TextColor .color (0xFFDD55 )))
846+ .build ());
847+ return ;
848+ }
849+ source .sendMessage (Component .text (" " ));
850+ source .sendMessage (Component .text ()
851+ .append (Component .text ("正在将玩家 " , NamedTextColor .WHITE ))
852+ .append (Component .text (onlinePlayer .getUsername (), net .kyori .adventure .text .format .TextColor .color (0x00FF88 )))
853+ .append (Component .text (" 转移到线路 " , NamedTextColor .WHITE ))
854+ .append (Component .text ("[" + routeName .toUpperCase () + "]" , net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
855+ .append (Component .text ("..." , NamedTextColor .WHITE ))
856+ .build ());
857+ source .sendMessage (Component .text (" " ));
858+
859+ onlinePlayer .sendMessage (Component .text (" " ));
860+ onlinePlayer .sendMessage (Component .text ()
861+ .append (Component .text ("管理员正在将您转移到线路 " , NamedTextColor .WHITE ))
862+ .append (Component .text ("[" + serverInfo .getDisplayName () + "]" , net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
863+ .build ());
864+ onlinePlayer .sendMessage (Component .text (" " ));
865+
866+ connectionManager .adminTransferPlayer (onlinePlayer , routeName , serverInfo );
867+ return ;
868+ }
869+
870+ source .sendMessage (Component .text ()
871+ .append (Component .text (" ➤ " , net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
872+ .append (Component .text ("玩家当前不在线,正在更新其历史线路记录..." , NamedTextColor .WHITE ))
857873 .build ());
858- targetPlayer .sendMessage (Component .text (" " ));
859-
860- // 执行转移
861- connectionManager .adminTransferPlayer (targetPlayer , routeName , serverInfo );
874+
875+ UUID directUuid = tryParseUuid (targetInput );
876+ if (directUuid != null ) {
877+ updateOfflineRouteRecord (source , directUuid , targetInput , routeName , serverInfo );
878+ return ;
879+ }
880+
881+ connectionManager .findPlayerUuidByName (targetInput )
882+ .ifPresentOrElse (uuid -> updateOfflineRouteRecord (source , uuid , targetInput , routeName , serverInfo ),
883+ () -> sendAsyncMessage (source , Component .text ()
884+ .append (Component .text (" ✘ " , NamedTextColor .RED ))
885+ .append (Component .text ("未找到玩家 " + targetInput + " 的历史线路记录,请确认名称是否正确或直接使用 UUID" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
886+ .build ()));
887+ connectionManager .findPlayerUuidByName (targetInput )
888+ .ifPresentOrElse (uuid -> {
889+ if (connectionManager .isFloodgatePlayer (uuid )) {
890+ sendAsyncMessage (source , Component .text ()
891+ .append (Component .text (" ✦ " , NamedTextColor .GOLD ))
892+ .append (Component .text ("目标玩家通过 Geyser/Floodgate 连接,已跳过 SimpleTransfer 处理" , net .kyori .adventure .text .format .TextColor .color (0xFFDD55 )))
893+ .build ());
894+ return ;
895+ }
896+
897+ updateOfflineRouteRecord (source , uuid , targetInput , routeName , serverInfo );
898+ }, () -> sendAsyncMessage (source , Component .text ()
899+ .append (Component .text (" ✘ " , NamedTextColor .RED ))
900+ .append (Component .text ("未找到玩家 " + targetInput + " 的历史线路记录,请确认名称是否正确或直接使用 UUID" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
901+ .build ()));
862902 }
903+
904+ private void updateOfflineRouteRecord (CommandSource source , UUID targetUuid , String targetName ,
905+ String routeName , ServerInfo serverInfo ) {
906+ String operatorName = getSourceName (source );
907+ String resolvedName = targetName ;
908+ if (resolvedName == null || tryParseUuid (resolvedName ) != null ) {
909+ resolvedName = connectionManager .getLastKnownUsername (targetUuid )
910+ .orElse (resolvedName != null ? resolvedName : targetUuid .toString ());
911+ }
912+
913+ final String displayName = resolvedName ;
914+
915+ connectionManager .updateOfflineRoute (targetUuid , routeName , displayName , operatorName )
916+ .whenComplete ((success , error ) -> {
917+ if (error != null || !Boolean .TRUE .equals (success )) {
918+ sendAsyncMessage (source , Component .text ()
919+ .append (Component .text (" ✘ " , NamedTextColor .RED ))
920+ .append (Component .text ("更新线路记录失败,请检查控制台日志。" , net .kyori .adventure .text .format .TextColor .color (0xFFAAAA )))
921+ .build ());
922+ return ;
923+ }
924+
925+ sendAsyncMessage (source , Component .text ()
926+ .append (Component .text (" ✓ " , NamedTextColor .GREEN ))
927+ .append (Component .text ("已将玩家 " , NamedTextColor .WHITE ))
928+ .append (Component .text (displayName , net .kyori .adventure .text .format .TextColor .color (0x00FF88 )))
929+ .append (Component .text (" 的历史线路更新为 " , NamedTextColor .WHITE ))
930+ .append (Component .text ("[" + routeName .toUpperCase () + "] " ,
931+ net .kyori .adventure .text .format .TextColor .color (0x00DDFF )))
932+ .append (Component .text (serverInfo .getDescription (), net .kyori .adventure .text .format .TextColor .color (0xAAFFFF )))
933+ .append (Component .text (",玩家下次登录时将自动应用。" , NamedTextColor .WHITE ))
934+ .build ());
935+ });
936+ }
937+
938+ private void sendAsyncMessage (CommandSource source , Component message ) {
939+ server .getScheduler ().buildTask (plugin , () -> source .sendMessage (message )).schedule ();
940+ }
941+
942+ private String getSourceName (CommandSource source ) {
943+ if (source instanceof Player player ) {
944+ return player .getUsername ();
945+ }
946+ return "控制台" ;
947+ }
948+
949+ private UUID tryParseUuid (String input ) {
950+ try {
951+ return UUID .fromString (input );
952+ } catch (IllegalArgumentException ignored ) {
953+ return null ;
954+ }
955+ }
863956}
0 commit comments