2021-02-20 20:52:23 +00:00
using System.Collections.Generic ;
2021-02-20 22:56:55 +00:00
using System.Linq ;
2021-02-20 20:52:23 +00:00
using SanAndreasUnity.Net ;
using UnityEngine ;
namespace SanAndreasUnity.Commands
{
public class CommandManager : MonoBehaviour
{
public static CommandManager Singleton { get ; private set ; }
readonly Dictionary < string , CommandInfo > m_registeredCommands = new Dictionary < string , CommandInfo > ( ) ;
public IEnumerable < string > RegisteredCommands = > m_registeredCommands . Keys ;
public static string invalidSyntaxText = > "Invalid syntax" ;
[SerializeField] private List < string > m_forbiddenCommands = new List < string > ( ) ;
/// <summary> Forbidden commands can not be registered. </summary>
public List < string > ForbiddenCommands = > m_forbiddenCommands ;
[SerializeField] private bool m_registerHelpCommand = true ;
2021-02-22 16:43:18 +00:00
private struct PlayerData
{
public float timeWhenLastExecutedCommand ;
}
readonly Dictionary < Player , PlayerData > m_perPlayerData = new Dictionary < Player , PlayerData > ( ) ;
2021-02-20 20:52:23 +00:00
public struct CommandInfo
{
public string command ;
2021-02-22 14:50:43 +00:00
public string description ;
2021-02-20 20:52:23 +00:00
public System . Func < ProcessCommandContext , ProcessCommandResult > commandHandler ;
public bool allowToRunWithoutServerPermissions ;
2021-02-22 14:50:43 +00:00
public bool runOnlyOnServer ;
2021-02-22 16:43:18 +00:00
public float limitInterval ;
2021-02-20 20:52:23 +00:00
public CommandInfo ( string command , bool allowToRunWithoutServerPermissions )
2021-02-22 14:50:43 +00:00
: this ( )
2021-02-20 20:52:23 +00:00
{
this . command = command ;
this . allowToRunWithoutServerPermissions = allowToRunWithoutServerPermissions ;
2021-02-22 14:50:43 +00:00
}
2021-02-22 16:43:18 +00:00
public CommandInfo ( string command , string description , bool allowToRunWithoutServerPermissions , bool runOnlyOnServer , float limitInterval )
2021-02-22 14:50:43 +00:00
: this ( )
{
this . command = command ;
this . description = description ;
this . allowToRunWithoutServerPermissions = allowToRunWithoutServerPermissions ;
this . runOnlyOnServer = runOnlyOnServer ;
2021-02-22 16:43:18 +00:00
this . limitInterval = limitInterval ;
2021-02-20 20:52:23 +00:00
}
}
public class ProcessCommandResult
{
public string response ;
public static ProcessCommandResult UnknownCommand = > new ProcessCommandResult { response = "Unknown command" } ;
public static ProcessCommandResult InvalidCommand = > new ProcessCommandResult { response = "Invalid command" } ;
public static ProcessCommandResult NoPermissions = > new ProcessCommandResult { response = "You don't have permissions to run this command" } ;
2021-02-22 14:50:43 +00:00
public static ProcessCommandResult CanOnlyRunOnServer = > new ProcessCommandResult { response = "This command can only run on server" } ;
2021-02-22 16:43:18 +00:00
public static ProcessCommandResult LimitInterval ( float interval ) = > new ProcessCommandResult { response = $"This command can only be used on an interval of {interval} seconds" } ;
public static ProcessCommandResult Error ( string errorMessage ) = > new ProcessCommandResult { response = errorMessage } ;
public static ProcessCommandResult Success = > new ProcessCommandResult ( ) ;
2021-02-20 20:52:23 +00:00
}
public class ProcessCommandContext
{
public string command ;
public bool hasServerPermissions ;
2021-02-20 23:46:48 +00:00
public Player player ;
2021-02-20 20:52:23 +00:00
}
void Awake ( )
{
if ( null = = Singleton )
Singleton = this ;
2021-02-22 16:43:18 +00:00
Player . onDisable + = PlayerOnDisable ;
2021-02-20 20:52:23 +00:00
if ( m_registerHelpCommand )
RegisterCommand ( new CommandInfo { command = "help" , commandHandler = ProcessHelpCommand , allowToRunWithoutServerPermissions = true } ) ;
}
2021-02-22 16:43:18 +00:00
void PlayerOnDisable ( Player player )
{
m_perPlayerData . Remove ( player ) ;
}
2021-02-20 20:52:23 +00:00
public bool RegisterCommand ( CommandInfo commandInfo )
{
commandInfo . command = commandInfo . command . Trim ( ) ;
if ( this . ForbiddenCommands . Contains ( commandInfo . command ) )
return false ;
if ( m_registeredCommands . ContainsKey ( commandInfo . command ) )
return false ;
m_registeredCommands . Add ( commandInfo . command , commandInfo ) ;
return true ;
}
public bool RemoveCommand ( string command )
{
return m_registeredCommands . Remove ( command ) ;
}
public static string [ ] SplitCommandIntoArguments ( string command )
{
// TODO: add support for arguments that have spaces, i.e. those enclosed with quotes
return command . Split ( new string [ ] { " " , "\t" } , System . StringSplitOptions . RemoveEmptyEntries ) ;
}
public static string GetRestOfTheCommand ( string command , int argumentIndex )
{
if ( argumentIndex < 0 )
return "" ;
string [ ] args = SplitCommandIntoArguments ( command ) ;
if ( argumentIndex > args . Length - 2 )
return "" ;
return string . Join ( " " , args , argumentIndex + 1 , args . Length - argumentIndex - 1 ) ;
}
2021-02-22 17:50:35 +00:00
public static Vector3 ParseVector3 ( string [ ] arguments , int startIndex )
{
2021-02-27 15:26:59 +00:00
if ( startIndex + 2 > = arguments . Length )
2021-02-22 17:50:35 +00:00
throw new System . ArgumentException ( "Failed to parse Vector3: not enough arguments" ) ;
Vector3 v = Vector3 . zero ;
for ( int i = 0 ; i < 3 ; i + + )
{
if ( ! float . TryParse ( arguments [ startIndex + i ] , out float f ) )
throw new System . ArgumentException ( "Failed to parse Vector3: invalid number" ) ;
v [ i ] = f ;
}
return v ;
}
public static Quaternion ParseQuaternion ( string [ ] arguments , int startIndex )
{
2021-02-27 15:26:59 +00:00
if ( startIndex + 3 > = arguments . Length )
2021-02-22 17:50:35 +00:00
throw new System . ArgumentException ( "Failed to parse Quaternion: not enough arguments" ) ;
Quaternion quaternion = Quaternion . identity ;
for ( int i = 0 ; i < 4 ; i + + )
{
if ( ! float . TryParse ( arguments [ startIndex + i ] , out float f ) )
throw new System . ArgumentException ( "Failed to parse Quaternion: invalid number" ) ;
quaternion [ i ] = f ;
}
return quaternion ;
}
2021-11-14 22:54:14 +00:00
public static Color ParseColor ( string [ ] arguments , int startIndex )
{
if ( startIndex + 1 > = arguments . Length )
throw new System . ArgumentException ( "Failed to parse color: not enough arguments" ) ;
if ( ! ColorUtility . TryParseHtmlString ( arguments [ startIndex ] , out Color color ) )
throw new System . ArgumentException ( "Failed to parse color" ) ;
return color ;
}
2021-02-20 23:46:48 +00:00
ProcessCommandResult ProcessCommand ( ProcessCommandContext context )
2021-02-20 20:52:23 +00:00
{
2021-02-20 23:46:48 +00:00
if ( string . IsNullOrWhiteSpace ( context . command ) )
2021-02-20 20:52:23 +00:00
return ProcessCommandResult . UnknownCommand ;
2021-02-20 23:46:48 +00:00
string [ ] arguments = SplitCommandIntoArguments ( context . command ) ;
2021-02-20 20:52:23 +00:00
if ( 0 = = arguments . Length )
return ProcessCommandResult . InvalidCommand ;
if ( ! m_registeredCommands . TryGetValue ( arguments [ 0 ] , out CommandInfo commandInfo ) )
return ProcessCommandResult . UnknownCommand ;
2021-02-22 14:50:43 +00:00
if ( commandInfo . runOnlyOnServer & & ! NetStatus . IsServer )
return ProcessCommandResult . CanOnlyRunOnServer ;
2021-02-20 23:46:48 +00:00
if ( ! context . hasServerPermissions & & ! commandInfo . allowToRunWithoutServerPermissions )
2021-02-20 20:52:23 +00:00
return ProcessCommandResult . NoPermissions ;
2021-02-22 16:43:18 +00:00
if ( context . player ! = null )
{
m_perPlayerData . TryGetValue ( context . player , out PlayerData playerData ) ;
if ( commandInfo . limitInterval > 0 & & Time . time - playerData . timeWhenLastExecutedCommand < commandInfo . limitInterval )
return ProcessCommandResult . LimitInterval ( commandInfo . limitInterval ) ;
playerData . timeWhenLastExecutedCommand = Time . time ;
m_perPlayerData [ context . player ] = playerData ;
}
2021-02-20 20:52:23 +00:00
return commandInfo . commandHandler ( context ) ;
}
public ProcessCommandResult ProcessCommandAsServer ( string command )
{
2021-02-20 23:46:48 +00:00
return ProcessCommand ( new ProcessCommandContext { command = command , hasServerPermissions = true } ) ;
2021-02-20 20:52:23 +00:00
}
public ProcessCommandResult ProcessCommandForPlayer ( Player player , string command )
{
bool hasServerPermissions = player = = Player . Local ;
2021-02-20 23:46:48 +00:00
return ProcessCommand ( new ProcessCommandContext
{
command = command ,
hasServerPermissions = hasServerPermissions ,
player = player ,
} ) ;
2021-02-20 20:52:23 +00:00
}
ProcessCommandResult ProcessHelpCommand ( ProcessCommandContext context )
{
2021-02-20 22:56:55 +00:00
string response = "List of available commands: " +
string . Join ( ", " , m_registeredCommands
. Where ( pair = > context . hasServerPermissions | | pair . Value . allowToRunWithoutServerPermissions )
. Select ( pair = > pair . Key ) ) ;
2021-02-20 20:52:23 +00:00
return new ProcessCommandResult { response = response } ;
}
public bool HasCommand ( string command )
{
return m_registeredCommands . ContainsKey ( command ) ;
}
}
}