ID:1662284
 
Keywords: admin, commands, text
/*
Written by Daaok
Forum Post http://www.byond.com/forum/?post=1662284
*/


#define admin_cmd_trig "/admin" // This is the string you use for text2admin() to trigger in a message ( default is /admin )
#define admin_cmd_cncl " " // This is the string you use to end the first text2admin() that is used after
#define admin_cmd_lowr TRUE // Make all commands parsed by text2admin() lowercase, aNnoUNcE will be announce, set to FALSE to disable
// A good reason to use this is so your text2admin() can be all lower-case, such as, /admin announce hello!

#define text2admin_log TRUE // Log the called admin commands used via text2admin(), set to FALSE to disable
#define per_admin_logs TRUE // Log the called admin commands used via text2admin(), split into different files for each admin
// If text2admin_log is FALSE, this does nothing, if this is FALSE, all logging (if enabled) will be in ONE file
#define log_directory "Logs/text2admin/" // This is the directory text2admin() logs information to, change this to whatever you want
// If per_admin_logs is enabled, each log file's name will be admin's_ckey.txt, otherwise it will be compiled in text2admin_logs.txt

proc
text2admin ( t , client/c )
. = findtext ( t , admin_cmd_trig ) // Find admin_cmd_trig in the string passed to text2admin()
if ( !. )
// End the text2admin() if . is null (admin_cmd_trig not found), a check for admin_cmd_trig can also be done before you call text2admin() to negate the need for this, it's just here for performance "protection"
return

var tmp
ftx = null // Variable to hold information based on if a comma is found (more parameters), or if admin_cmd_cncl is found (to end text2admin())
par = null // Variable to hold any found parameter
cmd = copytext ( t , . + length ( admin_cmd_trig ) + 1 , findtext ( t , " " , . + length ( admin_cmd_trig ) + 1 ) ) // Find the command to call
list/pars = list ( ) // List to hold all of the found parameters, to later pass to any admin command

ltxt = length ( t ) ; lcmd = length ( cmd ) ; lacc = length ( admin_cmd_cncl ) ; fcmd = findtext ( t , cmd )

if ( !cmd ) // Checks to see if cmd is valid
return // If it isn't, end text2admin() right here

if ( admin_cmd_lowr )
cmd = lowertext ( cmd ) // If admin_cmd_lowr is TRUE, this if() will execute and lowertext() the found command

if ( ! ( findtext ( t , admin_cmd_cncl , fcmd , fcmd + lcmd + lacc ) ) && ! ( length ( admin_cmd_trig ) + lcmd + 1 >= ltxt ) )
// if it doesn't find admin_cmd_cncl (no parameter admin command), it will begin the search for parameters or if the entire string isn't just calling a command with no parameters
. = findtext ( t , " " , fcmd ) + 1 // Find the space after the command to start the search for parameters

if ( findtext ( t , admin_cmd_cncl , fcmd ) && ! ( findtext ( t , "," , fcmd ) ) )
// if it finds the admin_cmd_cncl in the message, and doesn't find a comma for more parameters, it grabs the last parameter and ends the parameter search by nulling the . variable
. = null
pars += copytext ( t , fcmd + lcmd + 1 , findtext ( t , admin_cmd_cncl ) )
else if ( ! ( findtext ( t , admin_cmd_cncl , fcmd , fcmd + lcmd + lacc ) ) && ! ( findtext ( t , "," , fcmd ) ) )
// if it reaches the end of the message, it grabs the last parameter and ends the parameter search by nulling the . variable
. = null
pars += copytext ( t , fcmd + lcmd + 1 )

while ( . )
if ( findtext ( t , "," , . ) ) // Checks if there is another parameter to search for
ftx = ","
else if ( findtext ( t , admin_cmd_cncl , . ) ) // If there isn't, checks for admin_cmd_cncl
ftx = admin_cmd_cncl

par = copytext ( t , . , findtext ( t , ftx , . ) ) // Get the parameter based on the position of . (which will be at the beginning of the first parameter, or at the end one) and finding ftx
pars += par // Add par to the parameter list

if ( ftx == "," ) // If there is another parameter to find, it continues the search
. = findtext ( t , par , . ) + length ( par ) + 1 // This places . right before the comma for the next parameter
if ( . >= ltxt ) // If . has reached the end the of the string given to text2admin(), force the parameter search to end
break
else // If ftx found admin_cmd_cncl, it ends parameter searching
break

call ( "/proc/[ cmd ]" ) ( pars , c )
// call()() will pass the found parameters to the admin command, as well as the client that called it, to see why the latter is done check out the example announce command
// You could probably make your admin procedures mob procedures, an obj's, datum's, or whatever if you wish to use hascall()
// You could also add all of your admin commands to a list, and check if cmd is in that list, this will also allow for capitalizing typos in your cmd, if you don't use findtextEx()

if ( text2admin_log )
. = "\[ [ time2text ( world.timeofday , "hh:mm:ss DD/MM/YY" ) ] ] \[ [ c.address ? c.address : "Host" ] | [ c.computer_id ] | [ c.ckey ] ] [ cmd ] called with parameters \[ [ pars2text ( pars ) ] ]" // Set the text to log to ., so it reduces the need to use the same string twice for no reason
if ( per_admin_logs ) // If per-admin logging is TRUE, log each action from each admin in their own log file
text2file ( . , "[ log_directory ][ c.ckey ].txt" )
else // Otherwise, it goes to the generic text2admin_logs.txt file
text2file ( . , "[ log_directory ]text2admin_logs.txt" )


pars2text ( list/l ) // This is a parameter used in text2admin() to log the called parameters, if logging is enabled
if ( l.len ) // Checks to see if the given list has any contents
. = l[ 1 ] // Make . the first item in the list
if ( l.len > 1 ) for ( var/p = 2 to l.len ) // If it's longer than one item, loop through the rest of it
. += ",[ l[ p ] ]" // Add each parameter in the list to .
else
. = "-none specified-" // If no parameters are specified, -none specified- is the text displayed

The above code is simply text2admin(), how to utilize it in your game is the example code below.

var list/administrators = list ( "put_your_admins_in_this_list" ) // A list to store all of your admin's in

mob/verb/say ( t as text )
if ( findtext ( t , admin_cmd_trig ) )
// It should be noted admin_cmd_trig is a #define, and #defines are only defined for the file they're in, and all files below that file
// You may wish to add the #defines from text2admin() to a file with all of your other #defines, or just make them variables, it's up to you
// This check is also done in text2admin() itself, however, doing it here will prevent a call to text2admin() and a search in the administrators list
if ( ckey in administrators ) // Search for the src's ckey in the administrators list (you may want to use keys, it's up to you)
spawn text2admin ( t , client ) // Call text2admin() with the appropriate parameters
world << "[ key ]: [ t ]" // Send your message to all players in the world

proc
announce ( list/pars = list ( ) , client/c ) // Basic admin command to announce a message to all players
if ( ! ( pars.len ) )
return // We don't want to announce nothing, do we?
// text2admin() breaks any string with commas in it into different parameters, so we need to reconstruct that string if you had any commas in your announcement
world << "Announcement from [ c.ckey ]: [ pars2text ( pars ) ]" // We already have a nice procedure for this made, so let's reuse it


This may be a very basic example, because how you use text2admin() is up to you. The way you handle arguments in the commands you call is based on what you want to make, I can't make an example for everything!

Performance test
                        Profile results (total time)
Proc Name Self CPU Total CPU Real Time Calls
------------------------- --------- --------- --------- ---------
/proc/text2admin 0.283 0.394 0.394 10000
/proc/pars2text 0.093 0.093 0.094 10000
/proc/announce 0.017 0.110 0.112 10000
/mob/verb/test_text2admin 0.279 0.672 0.672 1
// This is with logging DISABLED

Testing verb/proc
mob/verb/test_text2admin ( )
for ( var/i = 1 to 10000 )
spawn text2admin ( "/admin announce testing performance of text2admin, filler parameter 1, filler parameter 2, filler parameter 3, filler parameter 4, filler parameter 5" , client )
world << "Done"

proc/announce ( list/pars = list ( ) , client/c ) // Basic admin command to announce a message to all players
if ( ! ( pars.len ) )
return // We don't want to announce nothing, do we?
// text2admin() breaks any string with commas in it into different parameters, so we need to reconstruct that string if you had any commas in your announcement
. = pars2text ( pars )
// Tested without sending the message to the world to prevent DS from freezing

Specs of computer tested on
Intel Core i5-3570k CPU (Turbo Boosted to 3.8GHz)
HIS AMD Radeon HD 6670 1GB GDDR5
G.Skill Ares 8GB 1600MHz CL9 RAM (actually CL11 on this computer)
Seagate Barracuda 2TB 7200RPM HDD


As niche as this probably is, this was a fun project for me to do. You could probably rename this to text2call() or something and just call any proc with parameters, but I wanted a game moderating system so I designed it around administrating.
I'd also like to apologize in advance if this isn't a snippet, but I don't know what to define this as.


Possible future features
I may modify this to allow parameters to have values, as it's actually not difficult.
Example : /admin ban daaok&admin abuse
"daaok" is the list item (normal parameter), "admin abuse" is the value of the item, in this case, the reason for the ban.

Limitations
You are limited to one command with text2admin() per text string, no matter the amount of /admin announce hi!'s you put in one string.
Make it more clear
In response to NinjaSealions
NinjaSealions wrote:
Make it more clear

Added more comments to the text2admin() code, so anyone can better understand/follow along with the code and put the example usage of text2admin() in the OP.

I did some minor tweaking as well, and added one or two things.