2022-04-20 12:21:38 +00:00
package emu.grasscutter.command ;
2022-04-18 22:24:08 +00:00
import emu.grasscutter.Grasscutter ;
2022-04-19 03:46:04 +00:00
import emu.grasscutter.game.Account ;
2022-04-27 04:24:25 +00:00
import emu.grasscutter.game.player.Player ;
2022-04-18 22:24:08 +00:00
import org.reflections.Reflections ;
import java.util.* ;
2022-04-20 12:21:38 +00:00
@SuppressWarnings ( { " UnusedReturnValue " , " unused " } )
2022-04-18 22:24:08 +00:00
public final class CommandMap {
2022-04-20 12:21:38 +00:00
private final Map < String , CommandHandler > commands = new HashMap < > ( ) ;
private final Map < String , Command > annotations = new HashMap < > ( ) ;
2022-05-05 05:03:08 +00:00
private final Map < String , Integer > targetPlayerIds = new HashMap < > ( ) ;
2022-05-04 06:32:09 +00:00
private static final String consoleId = " console " ;
2022-04-20 12:21:38 +00:00
public CommandMap ( ) {
this ( false ) ;
}
public CommandMap ( boolean scan ) {
if ( scan ) this . scan ( ) ;
}
2022-04-18 22:24:08 +00:00
public static CommandMap getInstance ( ) {
return Grasscutter . getGameServer ( ) . getCommandMap ( ) ;
}
/ * *
* Register a command handler .
2022-04-20 12:21:38 +00:00
*
* @param label The command label .
2022-04-18 22:24:08 +00:00
* @param command The command handler .
* @return Instance chaining .
* /
public CommandMap registerCommand ( String label , CommandHandler command ) {
2022-04-19 00:11:23 +00:00
Grasscutter . getLogger ( ) . debug ( " Registered command: " + label ) ;
2022-04-20 12:21:38 +00:00
2022-04-19 02:09:51 +00:00
// Get command data.
2022-04-19 00:11:23 +00:00
Command annotation = command . getClass ( ) . getAnnotation ( Command . class ) ;
2022-04-19 03:46:04 +00:00
this . annotations . put ( label , annotation ) ;
2022-04-19 02:09:51 +00:00
this . commands . put ( label , command ) ;
2022-04-20 12:21:38 +00:00
2022-04-19 02:09:51 +00:00
// Register aliases.
2022-04-20 12:21:38 +00:00
if ( annotation . aliases ( ) . length > 0 ) {
2022-04-19 02:09:51 +00:00
for ( String alias : annotation . aliases ( ) ) {
2022-04-19 00:11:23 +00:00
this . commands . put ( alias , command ) ;
2022-04-19 03:46:04 +00:00
this . annotations . put ( alias , annotation ) ;
2022-04-19 02:09:51 +00:00
}
2022-04-20 12:21:38 +00:00
}
return this ;
2022-04-18 22:24:08 +00:00
}
/ * *
* Removes a registered command handler .
2022-04-20 12:21:38 +00:00
*
2022-04-18 22:24:08 +00:00
* @param label The command label .
* @return Instance chaining .
* /
public CommandMap unregisterCommand ( String label ) {
2022-04-19 00:11:23 +00:00
Grasscutter . getLogger ( ) . debug ( " Unregistered command: " + label ) ;
CommandHandler handler = this . commands . get ( label ) ;
2022-04-20 12:21:38 +00:00
if ( handler = = null ) return this ;
2022-04-19 00:11:23 +00:00
Command annotation = handler . getClass ( ) . getAnnotation ( Command . class ) ;
2022-04-19 03:46:04 +00:00
this . annotations . remove ( label ) ;
2022-04-19 02:09:51 +00:00
this . commands . remove ( label ) ;
2022-04-20 12:21:38 +00:00
2022-04-19 02:09:51 +00:00
// Unregister aliases.
2022-04-20 12:21:38 +00:00
if ( annotation . aliases ( ) . length > 0 ) {
2022-04-19 02:09:51 +00:00
for ( String alias : annotation . aliases ( ) ) {
2022-04-19 00:11:23 +00:00
this . commands . remove ( alias ) ;
2022-04-19 03:46:04 +00:00
this . annotations . remove ( alias ) ;
2022-04-19 02:09:51 +00:00
}
}
2022-04-20 12:21:38 +00:00
2022-04-19 00:11:23 +00:00
return this ;
2022-04-18 22:24:08 +00:00
}
2022-04-19 03:06:03 +00:00
/ * *
* Returns a list of all registered commands .
2022-04-20 12:21:38 +00:00
*
2022-04-19 03:06:03 +00:00
* @return All command handlers as a list .
* /
2022-04-19 10:17:19 +00:00
public List < CommandHandler > getHandlersAsList ( ) {
2022-04-19 03:06:03 +00:00
return new LinkedList < > ( this . commands . values ( ) ) ;
}
2022-04-20 12:21:38 +00:00
public HashMap < String , CommandHandler > getHandlers ( ) {
return new LinkedHashMap < > ( this . commands ) ;
}
2022-04-19 10:17:19 +00:00
2022-04-19 03:06:03 +00:00
/ * *
* Returns a handler by label / alias .
2022-04-20 12:21:38 +00:00
*
2022-04-19 03:06:03 +00:00
* @param label The command label .
* @return The command handler .
* /
public CommandHandler getHandler ( String label ) {
return this . commands . get ( label ) ;
}
2022-04-18 22:24:08 +00:00
/ * *
* Invoke a command handler with the given arguments .
2022-04-20 12:21:38 +00:00
*
* @param player The player invoking the command or null for the server console .
2022-04-18 22:24:08 +00:00
* @param rawMessage The messaged used to invoke the command .
* /
2022-05-04 06:32:09 +00:00
public void invoke ( Player player , Player targetPlayer , String rawMessage ) {
2022-04-19 00:11:23 +00:00
rawMessage = rawMessage . trim ( ) ;
2022-04-24 13:52:50 +00:00
if ( rawMessage . length ( ) = = 0 ) {
2022-05-03 06:23:25 +00:00
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . No_command_specified ) ;
2022-04-24 13:52:50 +00:00
return ;
2022-04-19 00:11:23 +00:00
}
2022-04-20 12:21:38 +00:00
2022-04-18 22:24:08 +00:00
// Parse message.
String [ ] split = rawMessage . split ( " " ) ;
2022-04-19 00:11:23 +00:00
List < String > args = new LinkedList < > ( Arrays . asList ( split ) ) ;
2022-04-18 22:24:08 +00:00
String label = args . remove ( 0 ) ;
2022-05-04 06:32:09 +00:00
String playerId = ( player = = null ) ? consoleId : player . getAccount ( ) . getId ( ) ;
2022-05-04 09:16:42 +00:00
// Check for special cases - currently only target command
String targetUidStr = null ;
if ( label . startsWith ( " @ " ) ) { // @[UID]
targetUidStr = label . substring ( 1 ) ;
2022-05-05 05:03:08 +00:00
} else if ( label . equalsIgnoreCase ( " target " ) ) { // target [[@]UID]
if ( args . size ( ) > 0 ) {
targetUidStr = args . get ( 0 ) ;
if ( targetUidStr . startsWith ( " @ " ) ) {
targetUidStr = targetUidStr . substring ( 1 ) ;
}
} else {
targetUidStr = " " ;
2022-05-04 09:16:42 +00:00
}
}
2022-05-05 05:03:08 +00:00
if ( targetUidStr ! = null ) {
if ( targetUidStr . equals ( " " ) ) { // Clears default targetPlayer
targetPlayerIds . remove ( playerId ) ;
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Target_cleared ) ;
return ;
} else { // Sets default targetPlayer to the UID given
try {
int uid = Integer . parseInt ( targetUidStr ) ;
targetPlayer = Grasscutter . getGameServer ( ) . getPlayerByUid ( uid ) ;
if ( targetPlayer = = null ) {
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Player_not_found_or_offline ) ;
} else {
targetPlayerIds . put ( playerId , uid ) ;
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Target_set . replace ( " {uid} " , targetUidStr ) ) ;
}
} catch ( NumberFormatException e ) {
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Invalid_UID ) ;
2022-05-04 06:32:09 +00:00
}
2022-05-05 05:03:08 +00:00
return ;
2022-05-04 06:32:09 +00:00
}
2022-05-05 05:03:08 +00:00
}
2022-05-04 09:16:42 +00:00
2022-04-18 22:24:08 +00:00
// Get command handler.
CommandHandler handler = this . commands . get ( label ) ;
2022-04-20 12:21:38 +00:00
if ( handler = = null ) {
2022-05-03 06:23:25 +00:00
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Unknown_command + label ) ;
2022-04-20 12:21:38 +00:00
return ;
2022-04-18 22:24:08 +00:00
}
2022-04-20 12:21:38 +00:00
2022-05-04 06:32:09 +00:00
// If any @UID argument is present, override targetPlayer with it
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
String arg = args . get ( i ) ;
if ( ! arg . startsWith ( " @ " ) ) {
continue ;
} else {
arg = args . remove ( i ) . substring ( 1 ) ;
try {
int uid = Integer . parseInt ( arg ) ;
targetPlayer = Grasscutter . getGameServer ( ) . getPlayerByUid ( uid ) ;
if ( targetPlayer = = null ) {
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Player_not_found_or_offline ) ;
return ;
}
break ;
} catch ( NumberFormatException e ) {
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Invalid_UID ) ;
return ;
}
}
}
// If there's still no targetPlayer at this point, use previously-set target
if ( targetPlayer = = null ) {
2022-05-05 05:03:08 +00:00
if ( targetPlayerIds . containsKey ( playerId ) ) {
targetPlayer = Grasscutter . getGameServer ( ) . getPlayerByUid ( targetPlayerIds . get ( playerId ) ) ; // We check every time in case the target goes offline after being targeted
if ( targetPlayer = = null ) {
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . Player_not_found_or_offline ) ;
return ;
}
} else {
// If there's still no targetPlayer at this point, use local player
if ( targetPlayer = = null ) {
targetPlayer = player ;
}
}
2022-05-04 06:32:09 +00:00
}
2022-04-19 03:46:04 +00:00
// Check for permission.
2022-04-20 12:21:38 +00:00
if ( player ! = null ) {
2022-04-19 03:46:04 +00:00
String permissionNode = this . annotations . get ( label ) . permission ( ) ;
2022-05-04 06:32:09 +00:00
String permissionNodeTargeted = this . annotations . get ( label ) . permissionTargeted ( ) ;
2022-04-19 03:46:04 +00:00
Account account = player . getAccount ( ) ;
2022-05-04 06:32:09 +00:00
if ( player ! = targetPlayer ) { // Additional permission required for targeting another player
if ( ! permissionNodeTargeted . isEmpty ( ) & & ! account . hasPermission ( permissionNodeTargeted ) ) {
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . You_not_permission_run_command ) ;
return ;
}
}
2022-04-24 13:52:50 +00:00
if ( ! permissionNode . isEmpty ( ) & & ! account . hasPermission ( permissionNode ) ) {
2022-05-03 06:23:25 +00:00
CommandHandler . sendMessage ( player , Grasscutter . getLanguage ( ) . You_not_permission_run_command ) ;
2022-04-20 12:21:38 +00:00
return ;
2022-04-19 03:46:04 +00:00
}
}
2022-04-20 12:21:38 +00:00
2022-04-18 22:24:08 +00:00
// Invoke execute method for handler.
2022-04-24 13:52:50 +00:00
boolean threading = this . annotations . get ( label ) . threading ( ) ;
2022-05-04 06:32:09 +00:00
final Player targetPlayerF = targetPlayer ; // Is there a better way to do this?
Runnable runnable = ( ) - > handler . execute ( player , targetPlayerF , args ) ;
2022-04-24 13:52:50 +00:00
if ( threading ) {
Thread command = new Thread ( runnable ) ;
command . start ( ) ;
}
else {
runnable . run ( ) ;
}
2022-04-18 22:24:08 +00:00
}
/ * *
* Scans for all classes annotated with { @link Command } and registers them .
* /
private void scan ( ) {
Reflections reflector = Grasscutter . reflector ;
2022-04-19 00:11:23 +00:00
Set < Class < ? > > classes = reflector . getTypesAnnotatedWith ( Command . class ) ;
2022-04-18 22:24:08 +00:00
classes . forEach ( annotated - > {
try {
2022-04-19 00:11:23 +00:00
Command cmdData = annotated . getAnnotation ( Command . class ) ;
Object object = annotated . newInstance ( ) ;
2022-04-18 22:24:08 +00:00
if ( object instanceof CommandHandler )
this . registerCommand ( cmdData . label ( ) , ( CommandHandler ) object ) ;
2022-04-19 00:11:23 +00:00
else Grasscutter . getLogger ( ) . error ( " Class " + annotated . getName ( ) + " is not a CommandHandler! " ) ;
} catch ( Exception exception ) {
Grasscutter . getLogger ( ) . error ( " Failed to register command handler for " + annotated . getSimpleName ( ) , exception ) ;
}
2022-04-18 22:24:08 +00:00
} ) ;
}
}