import es import os, re, inspect, shelve selfmodule = __import__(__name__) ES_IF_OPS = ( 'equalto', '=', '==', 'notequalto', '!=', '!==', 'greaterthan', '>', 'lessthan', '<', 'notlessthan', '>=', '=>', 'notgreaterthan', '<=', '=<', 'in', 'notin', ) ES_IFX_OPS = ( 'true', 'false', 'parse', ) ES_BRACES = ( '{', '}', ) ES_PARENS = ( '(', ')', ) ES_COND_CMDS = ( 'if', 'es_xif', 'ifx', ) ES_ELSE_CMDS = ( 'else', 'es_xelse', ) scripts = {} commands = {} args = None def Main(): global EsCmd EsCmd = EsCmd() for cmdname in EsCmd.commandlist: cmd = getattr(EsCmd, cmdname) if cmd.special: es.regcmd(cmdname, 'esshell/servercmd_es', cmd.desc) else: es.regcmd('ess_%s' % cmdname, 'esshell/servercmd_es', cmd.desc) es.regcmd('ess_x%s' % cmdname, 'esshell/servercmd_es', '%s %s' % (cmd.desc, 'Expands event and server variables')) def eventlistener(ev): event = str(ev['es_event']) for script in scripts: if event in script.events: script.events[event].run() def servercmd(): global args argc = es.getargc() args = { 'argv': [es.getargv(n) for n in xrange(1, argc)], 'argc': argc, 'args': es.getargs(), } commands[es.getargv(0)].run() args = None def servercmd_es(): command = es.getargv(0) exp = False if command.startswith('ess_'): command = command[4:] if command[0] == 'x': command = command[1:] else: exp = True cmd = getattr(EsCmd, command) passargs = {} if cmd.argv: argv = [es.getargv(n) for n in xrange(1, es.getargc())] if exp: passargs['argv'] = expand(argv) else: passargs['argv'] = argv if cmd.args: if exp: passargs['args'] = None else: passargs['args'] = es.getargs() cmd.run(passargs) def expand(argv, ev=None): if ev is None: ev = es.event_var for n, arg in reversed(list(enumerate(argv))): if arg in ['server_var', 'event_var'] and len(argv[n:]) >= 4 and argv[n+1] == '(' and argv[n+3] == ')': try: result = str({'server_var': es.server_var, 'event_var': es.event_var}[arg][argv[n+2]]) except: result = '0' del argv[n:n+4] argv.insert(n, result) return argv parseargs_regex = re.compile('//|"[^"]*"|[{}()\':]|[^{}()\': ]+') def getargsfrom(cmdstring, n=0): for i in xrange(n): match = parseargs_regex.match(cmdstring) cmdstring = cmdstring[match.span()[1]:].strip() return cmdstring class ByteCode: def __init__(self, scriptname='', es=None, cl=None, plain=False): if not plain: self.es_commands = EsCmd.commandlist self.scriptname = scriptname self.scriptdir = 'cstrike/addons/eventscripts/%s/' % scriptname self.scriptfile = '%ses_%s.txt' % (self.scriptdir, scriptname) self.bytefile = '%ses_%s.ess' % (self.scriptdir, scriptname) def createbytecode(self): if not os.path.exists(self.scriptfile): return False updated = 0 if os.path.exists(self.bytefile): updated = os.stat(self.bytefile).st_mtime if updated < os.stat(self.scriptfile).st_mtime: self.blocks = self._getblocks(open(self.scriptfile).read()) for block in self.blocks: code = self._parseblock(self.blocks[block]) self.blocks[block] = code bytecode = shelve.open(self.bytefile) bytecode['script'] = self.blocks bytecode.close() else: bytecode = shelve.open(self.bytefile) self.blocks = bytecode['script'] return self.blocks def _getblocks(self, raw_code): code = '\n'.join(line.strip().replace('\t', ' ') for line in raw_code.split('\n')) matches = list(re.finditer('^((block|event) +.+)$', '%s' % code, re.M)) ln = len(matches)-1 results = [] for n, match in enumerate(matches): if n == ln: end = None else: end = matches[n+1].span()[0] result = code[match.span()[1]+2:end] if '}' in result: result = result.rpartition('}')[0] rs = [] for line in result.strip().split('\n'): if line: for line in self._parsecmdlines(line): line = self._parsecmdline(line) if line: rs.append(line) results.append((match.group(1), rs)) return dict(results) def _parseblock(self, block): code = [] prior_cond = None while True: if not block: return code for n, line in enumerate(block): print line if type(line) != str and line['type'] in ('cond_if', 'cond_ifx', 'cond_else') and block[n+1] == '{': section = None code += [cmdstring for cmdstring in block[:n] if not cmdstring in ES_BRACES] c = 0 bracks = {'{': 1, '}': -1} for i, brack in (l for l in enumerate(block[n+1:]) if l[1] in ES_BRACES): c += bracks[brack] if c == 0: if n == i: block = block[n+i+2:] break section = block[n+2:n+i+1] block = block[n+i+2:] break else: section = block[n+2:] block = None if not section: if line['type'] in ('cond_if', 'cond_ifx'): prior_cond = {'this_is': 'a_hack'} break if line['type'] == 'cond_if' and not line['exp']: op = line['op'] one, two = line['vars'] if self.compare_esstyle(one, op, two): code += section prior_cond = {'this_is': 'a_hack'} break section = self._parseblock(section) if line['type'] in ('cond_if', 'cond_ifx'): prior_cond = line line['code'] = section code.append(line) elif prior_cond: prior_cond['code_else'] = section prior_cond = None else: code += section break else: prior_cond = None else: code += [cmdstring for cmdstring in block if not cmdstring in ES_BRACES] block = None _parseargs_regex = re.compile('//|"[^"]*"|[{}()\':]|[^{}()\': ]+') def _parseargs(self, cmdstring, remquotes=True): if cmdstring.count('"') % 2 != 0: cmdstring = '%s"' % cmdstring result = [arg[1:-1] if (remquotes or not re.search('[; {}()\';:]', arg)) and arg[0] == arg[-1] == '"' else arg for arg in self._parseargs_regex.findall(cmdstring)] if '//' in result: result = result[:result.index('//')] return result def _parseargsfrom(self, cmdstring, n=0): for i in xrange(n+1): match = self._parseargs_regex.match(cmdstring) cmdstring = cmdstring[match.span()[1]:].strip() return cmdstring def _remquotes(self, args): return [arg[1:-1] if arg[0] == arg[-1] == '"' else arg for arg in args] def _parseexp(self, args): exp = '' rule = {'es': 'e', 'esnq': 'n', 'es_xnq': 'x'} args = list(args) while args: cmd = args[0] if not cmd in rule: break args.pop(0) exp += rule[cmd] if 'n' in exp: exp = 'n' elif 'e' in exp: exp = 'e' elif 'x' in exp: exp = 'x' return exp, args def _parsevars(self, args): contains_var = False args = list(args) for n, arg in reversed(list(enumerate(args))): if arg in ['server_var', 'event_var'] and len(args[n:]) >= 4 and args[n+1] == '(' and args[n+3] == ')': value = args[n+2] sv = {'server_var': 's', 'event_var': 'e'}[arg] if type(value) == dict: sv = '%s%s' % (sv, value['sv']) value = value['var'] var = {'type': 'variable', 'var': value, 'sv': sv} del args[n:n+4] args.insert(n, var) contains_var = True return contains_var, args def _getcmdstrings(self, lines): return [line for line in (self._parsecmdline(line) for line in lines) if line] _splitline_regex = re.compile('((?:"[^"]*"|[^";]+)+)(?:;|$)', re.M) def _parsecmdlines(self, cmdstring): if not ';' in cmdstring: return [cmdstring] return [cmdstring for cmdstring in (cmdstring.strip() for cmdstring in self._splitline_regex.findall(cmdstring)) if cmdstring] def _parsecmdline(self, cmdstring): if cmdstring in ES_BRACES: return cmdstring args = self._parseargs(cmdstring) if not args: return None exp, args = self._parseexp(args) if not args: return None contains_var, args_withvars = self._parsevars(args) if (exp == 'n' and not contains_var) or exp == 'x': if '"' in cmdstring: cmdstring = cmdstring.replace('"', '') args = self._parseargs(cmdstring) if not args: return None contains_var, args_withvars = self._parsevars(args) exp = '' command = args_withvars.pop(0) args = args_withvars if not command: return None _condlengths = {'if': 6, 'es_xif': 6, 'ifx': 5} if command in ES_COND_CMDS: if contains_var and command in ('ifx', 'es_xif') and not exp: return None if _condlengths[command] == len(args) and args[-1] == 'do': if command == 'ifx': if (args[1], args[3]) == ES_PARENS and args[0] in ES_IFX_OPS: var = args[2] op = args[0] if not op == 'parse': if type(var) == dict: var['sv'] = 's%s' % var['sv'] else: var = {'type': 'variable', 'var': var, 'sv': 's'} return {'type': 'cond_ifx', 'op': op, 'var': var, 'exp': exp} else: if (args[0], args[4]) == ES_PARENS and args[2] in ES_IF_OPS: if command == 'if' and contains_var and not exp == 'e': exp = 'e' op = { 'equalto': '==', '=': '==', '==': '==', 'notequalto': '!=', '!=': '!=', '!==': '!=', 'greaterthan': '>', '>': '>', 'lessthan': '<', '<': '<', 'notlessthan': '<=', '>=': '<=', '=>': '<=', 'notgreaterthan': '>=', '<=': '>=', '=<': '>=', 'in': 'in', 'notin': 'notin', }[args[2]] return {'type': 'cond_if', 'op': op, 'vars': (args[1], args[3]), 'exp': exp} elif command in ES_ELSE_CMDS: if args[:1] == ['do']: return {'type': 'cond_else'} cmd_type = None if type(command) == str: match = re.match('^es_(x)?([a-z]+)', command) if match: if match.group(2) in self.es_commands: command = match.group(2) cmd_type = 'cmd_es' if not match.group(1) and not exp == 'e': exp = 'e' if contains_var and not exp: args = self._parseargs(cmdstring)[1:] if cmd_type == 'cmd_es': result = {'type': 'cmd_es', 'command': command, 'exp': exp} _args = inspect.getargspec(getattr(EsCmd, command).func) _args, _def = _args[0], _args[3] if 'argv' in _args: result['argv'] = args if 'args' in _args and not exp: result['args'] = self._parseargsfrom(cmdstring, _def[0]) elif type(command) == dict and exp: result = {'type': 'cmd_unk', 'command': command, 'args': self._parseargsfrom(cmdstring, 3), 'argv': args, 'exp': exp} else: # expansion!? result = {'type': 'cmd_con', 'cmdstring': cmdstring} return result def compare_esstyle(self, one, op, two): typ = re.compile('^-?\d+(\.\d+)?$') tone, ttwo = bool(typ.match(one)), bool(typ.match(two)) if op in ('==', '!=', 'in', 'notin'): one, two = str(one), str(two) elif op in ('<', '>', '<=', '>='): one = int(one) if tone else 0 two = int(two) if ttwo else 0 if op == '==': res = one == two elif op == '!=': res = one != two elif op == '>': res = one > two elif op == '<': res = one < two elif op == '>=': res = one >= two elif op == '<=': res = one <= two elif op == 'in': res = one in two elif op == 'notin': res = one not in two return res class cmd: def __init__(self, desc='', argsfrom=0, special=False): self.desc = desc self.argsfrom = argsfrom self.special = special def __call__(self, func): self.func = func ins = inspect.getargspec(func)[0] self.argv = 'argv' in ins self.args = 'args' in ins return self def run(self, args): result = self.func(**args) if not result is None: es.dbgmsg(0, result) class EsCmd: def __init__(self): self.commandlist = dir(self) for element in '__doc__', '__module__', '__init__': self.commandlist.remove(element) @cmd(desc='', special=True) def ess(argv): pass @cmd(desc='', special=True) def essnq(argv): pass @cmd(desc='', special=True) def es_xnq(args): pass @cmd(desc='Loads a script or lists all loaded scripts if no script is provided') def load(argv): if argv: name = argv[0] if name in scripts: return '[EventScripts] Load script failed. It might already be loaded, try to es_unload it first' byte = ByteCode(name) if not os.path.exists(byte.scriptfile): es.load(byte.scriptname) return 'Could not open script for addons/eventscripts/%s/es_%s.txt\n Error source (console): (no script)' % (byte.scriptname, byte.scriptname) script = byte.createbytecode() del byte if not script: es.load(byte.scriptname) return 'Error loading script' scripts[name] = Script(name, script) else: pass @cmd(desc='') def getargv(argv): print argv @cmd(desc='Allows you to format a string by filling in a list of strings into a format string.') def format(argv): pass @cmd('es_msg', 1) def msg(argv, args): print argv, args es = args is None class Script: def __init__(self, name, code): self.name = name self.blocks = {} self.events = {} for blockname in code: block = blockname.partition(' ') typ = block[0] name = block[2] if typ == 'event': es.addons.registerForEvent(selfmodule, name, eventlistener) {'block': self.blocks, 'event': self.events}[typ][name] = Block(blockname, typ == 'event', code[blockname]) if 'load' in self.blocks: self.blocks['load'].run() class Block: def __init__(self, name, event, code): self.name = name self.event = event self.code = [] for line in code: self.code.append(linetypes[line['type']](line)) def run(self): for line in self.code: line.run() class IfBlock: def __init__(self, cond): self.code = [] for line in cond['code']: self.code.append(linetypes[line['type']](line)) self.elsecode = [] if 'code_else' in cond: for line in cond['code_else']: self.elsecode.append(linetypes[line['type']](line)) var1, var2 = cond['vars'] op = cond['op'] _blank = lambda x: x if op in ('==', '!=', 'in', 'notin'): self._str = _blank self._int = lambda x: str(x) else: self._str = lambda x: 0 self._int = _blank self.typ = re.compile('^-?\d+(\.\d+)?$') self._typ = {True: self._int, False: self._str} if type(var1) == str: var1 = self._typ[bool(self.typ.match(var1))](var1) else: var1 = GetVar(var1) if type(var2) == str: var2 = self._typ[bool(self.typ.match(var2))](var2) else: var2 = GetVar(var2) self.condfunc = { '==': self._equalto, '!=': self._notequalto, 'in': self._in, 'notin': self._notin, '>': self._greaterthan, '<': self._lessthan, '>=': self._greaterthanorequalto, '<=': self._lessthanorequalto, }[op] self.types = (str, int) def _equalto(self, var1, var2): return var1 == var2 def _notequalto(self, var1, var2): return var1 != var2 def _in(self, var1, var2): return var1 in var2 def _notin(self, var1, var2): return var1 not in var2 def _greaterthan(self, var1, var2): return var1 > var2 def _lessthan(self, var1, var2): return var1 < var2 def _greaterthanorequalto(self, var1, var2): return var1 >= var2 def _lessthanorequalto(self, var1, var2): return var1 <= var2 def run(self): if not type(var1) in self.types: var1 = var1.resolve() if type(var1) == str: var1 = self._typ[bool(self.typ.match(var1))](var1) if not type(var2) in self.types: var2 = var2.resolve() if type(var2) == str: var2 = self._typ[bool(self.typ.match(var2))](var2) code = self.code if self.condfunc(var1, var2) else self.elsecode for line in code: line.run() class IfxBlock: def __init__(self, cond): self.code = [] for line in cond['code']: self.code.append(linetypes[line['type']](line)) self.elsecode = [] if 'code_else' in cond: for line in cond['code_else']: self.elsecode.append(linetypes[line['type']](line)) op = cond['op'] var = cond['var'] if type(var) == dict: var = GetVar(var) self.var = var self.condfunc = { 'true': self._true, 'false': self._false, 'parse': self._parse, }[op] def _true(self, var): return not var in ('0', '') def _false(self, var): return var in ('0', '') def _parse(self, var): es.set('_tempcore', '0') es.mathparse('_tempcore', var) return bool(int(str(es.server_var['_tempcore']))) def run(self): if not type(self.var) == str: var = var.resolve() code = self.code if self.condfunc(var1) else self.elsecode for line in code: line.run() class EsLine: def __init__(self, line): if 'argv' in line: self.argv = line['argv'] else: self.argv = None if 'args' in line: self.args = line['args'] else: self.args = None self.exp = line['exp'] self.cmd = getattr(EsCmd, line['command']) self.expindexes = [] if self.exp: self.expindexes = [n for n, arg in enumerate(self.argv if self.argv else []) if type(arg) == dict] def run(self): passargs = {} if self.exp: if not self.args is None: passargs['args'] = None if not self.argv is None: argv = list(self.argv) for expindex in self.expindexes: argv = argv[:expindex] + self.argv[expindex].resolve() + argv[expindex+1:] passargs['argv'] = argv else: if not self.args is None: passargs['args'] = self.args if not self.argv is None: passargs['args'] = self.argv self.cmd.run(passargs) class UnkLine: def __init__(self, line): pass def run(self): pass class ConLine: def __init__(self, line): self.exp = line def run(self): pass linetypes = { 'cond_if': IfBlock, 'cond_ifx': IfxBlock, 'cmd_es': EsLine, 'cmd_unk': UnkLine, 'cmd_con': ConLine, } def GetVar(var, exp='e'): typ, var['sv'] = var['sv'][0], var['sv'][1:] return {'s': ServerVar, 'e': EventVar}[typ](var, exp == 'n') class ServerVar: getvar = es.server_var def __init__(self, var, nq=False): _var = var['var'] for typ in reversed(var['sv']): _var = {'s': ServerVar, 'e': EventVar}[typ](_var) self.var = _var self.nq = nq def resolve(self): if type(self.var) == str: result = str(self.getvar[self.var]).replace('"', '`') if self.nq: return bytecode._parseargs(result) else: return [result] else: return self.var.resolve(True) class EventVar(ServerVar): getvar = es.event_var Main()