Advertisement
Guest User

SAMP Python command parser

a guest
Nov 24th, 2011
576
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.65 KB | None | 0 0
  1.  
  2. import types, inspect
  3. try:
  4.     import samp
  5. except ImportError:
  6.     pass
  7.  
  8. t_unauthorized='You are not allowed to use this command'
  9. t_unauthd_color=0xFF1111FF
  10. _cmd_list = dict()
  11. current_cmd = None
  12.  
  13. def cmd(command, raw=False, requires=None, convert=None, unauthorized_text=t_unauthorized, unauthd_color=t_unauthd_color):
  14.     """function decorator for command functions
  15.    
  16.     Arguments:
  17.     function:
  18.         Command name as a string or a tuple of multiple aliases
  19.         If there is no command name passed, the command's name will be the function's name
  20.     raw:
  21.         whether the cmdtext should be passed as the raw string (True) or parsed as a whitespace-seperated tuple
  22.     requires:
  23.         function object or a tuple with multiple function objects,
  24.         which are called with a playerid and should return True if the player is allowed to use that command
  25.     convert:
  26.         contains a dictionary of callable objects, that take a string and return any other type, which is
  27.         called for the given command parameter, for example the following will parse prm1 as int:
  28.         @cmd("test", convert=dict(prm1=int))
  29.         def cmd_test(playerid, prm1):
  30.             pass
  31.     unauthorized_text:
  32.         a string which is sent to the player, if he/she/it is not allowed to use the command (see requires)
  33.     unauthd_color:
  34.         a color code which is used for sending the unauthorized message
  35.        
  36.     Example usages:
  37.     >>> @cmd
  38.     ... def testcmd(playerid, arg1):
  39.     ...     pass
  40.    
  41.     >>> @cmd("testcmd", requires=samp.IsPlayerAdmin)
  42.     ... def cmd_test(playerid, arg1):
  43.     ...     pass
  44.     """
  45.    
  46.     ret_obj = True
  47.     # get command names
  48.     cmd_names = []
  49.     if type(command) == str:
  50.         cmd_names.append(command) # directly add this string as the name
  51.     elif hasattr(command, '__iter__'):
  52.         cmd_names.extend(command) # extend the list with the iterable object
  53.     elif hasattr(command, '__call__'): # in that case we (should) have a decorator @cmd, where the function object is passed here
  54.         cmd_names.append(command.func_name)
  55.         ret_obj = False # in that case we don't have to return the cmd_obj instance
  56.     else: # invalid parameter
  57.         raise TypeError('invalid type: command')
  58.    
  59.     inst = _cmd_obj(cmd_names, raw, requires, convert, unauthorized_text, unauthd_color)
  60.     if not ret_obj:
  61.         # set function in inst and return command
  62.         inst.function = command
  63.         inst.read_function_data()
  64.         return command
  65.     return inst
  66.    
  67.  
  68. class _cmd_obj(object):
  69.     def __init__(self, aliases, raw, requires, convert, unauthorized_text, unauthd_color):
  70.         # we always have a list of command names in aliases
  71.         # iterate through all strings and add the commands
  72.         for alias in aliases:
  73.             _cmd_list[(alias if alias[0] != '/' else alias[1:])] = self # strip leading slash if there is one
  74.        
  75.         self.function = None
  76.         self.raw = raw
  77.         self.requires = requires if hasattr(requires, '__iter__') else (requires,)
  78.         self.convert = convert if type(convert) == dict else dict()
  79.         self.unauthorized_text = unauthorized_text
  80.         self.unauthd_color = unauthd_color
  81.        
  82.     def __call__(self, funcobj):
  83.         if self.function == None:
  84.             # in this case, we have a @cmd(...) decorator -> the function object is passed here
  85.             self.function = funcobj
  86.             self.read_function_data()
  87.         return funcobj
  88.        
  89.     def read_function_data(self):
  90.         """
  91.         reads parameter count and type of the function parameters
  92.         """
  93.         args = None
  94.         # read parameter info, depending on what callable type it is
  95.         if type(self.function) == types.FunctionType:
  96.             args = inspect.getargspec(self.function)
  97.         else:
  98.             args = inspect.getargspec(self.function.__call__)
  99.             # check if there is a self parameter
  100.             try: args.args.remove('self')
  101.             except ValueError: pass
  102.         # set parameter count
  103.         if args.varargs != None:# or args.keywords != None: # keyword arguments wouldn't make any sense here
  104.             # unlimited parameter count
  105.             self.param_count = -1
  106.         else:
  107.             self.param_count = len(args.args) - 1
  108.         # save the argument names for the convert parameter
  109.         self.args = args.args
  110.        
  111. def handle_command(playerid, cmdtext):
  112.     """This function handles a command sent by a player and has to be called in OnPlayerCommandText
  113.    
  114.     >>> @cmd
  115.     ... def test(pid, prm1, prm2):
  116.     ...     print('called')
  117.     >>> @cmd("t2", convert=dict(prm1=int))
  118.     ... def t2(pid, prm1):
  119.     ...     print(type(prm1))
  120.     >>> handle_command(0, "/test prm1 prm2")
  121.     called
  122.     True
  123.     >>> handle_command(0, "/t2 5")
  124.     <type 'int'>
  125.     True
  126.     >>> handle_command(1, "/nocmd")
  127.     False
  128.     """
  129.     # split cmdtext
  130.     prt = cmdtext.split()
  131.     cmdname = prt[0][1:]
  132.     # find the cmd instance
  133.     if not cmdname in _cmd_list:
  134.         return False # command does not exist
  135.     cmd = _cmd_list[cmdname]
  136.     if cmd.function == None:
  137.         raise AttributeError("command has no callable object assigned: [%d]: %s" % (playerid, cmdtext))
  138.        
  139.     # check if this user is authorized to use this command
  140.     for req in cmd.requires:
  141.         try:
  142.             if not req(playerid): # it's the user's fault if he doesn't pass callable objects
  143.                 samp.SendClientMessage(playerid, cmd.unauthd_color, cmd.unauthorized_text)
  144.                 return True
  145.         except TypeError: pass
  146.    
  147.     if cmd.param_count != -1 and len(prt) - 1 > cmd.param_count: # too many parameters passed -> cut out
  148.         prt = prt[:-(len(prt) - cmd.param_count - 1)]
  149.        
  150.     # convert parameters
  151.     for p in xrange(len(prt)):
  152.         if cmd.args[p] in cmd.convert: # check if this parameter exists
  153.             try: # call the converter
  154.                 prt[p] = cmd.convert[cmd.args[p]](prt[p])
  155.             except: pass # we don't know which exceptions are thrown if conversion fails, so always drop it
  156.    
  157.     # call the function object
  158.     cmd.function(playerid, *prt[1:] if not cmd.raw else cmdtext)
  159.    
  160.     return True
  161.    
  162. # doctest
  163. if __name__ == "__main__":
  164.     # mock for samp module with needed functions for doctests
  165.     class samp(object):
  166.         @staticmethod
  167.         def IsPlayerAdmin(playerid):
  168.             return True if playerid % 2 else False
  169.         @staticmethod
  170.         def SendClientMessage(playerid, color, text):
  171.             return None
  172.            
  173.     import doctest
  174.     doctest.testmod()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement