# # Soldier of Fortune 2 parser for BigBrotherBot(B3) (www.bigbrotherbot.net) # Copyright (C) 2011 Mark Weirath (xlr8or@xlr8or.com) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # # CHANGELOG __author__ = 'xlr8or, ~cGs*Pr3z, ~cGs*AQUARIUS' __version__ = '1.0.0' from b3.parsers.q3a.abstractParser import AbstractParser import re, string import b3 import b3.events #---------------------------------------------------------------------------------------------------------------------------------------------- class Sof2Parser(AbstractParser): gameName = 'sof2' IpsOnly = False IpCombi = False privateMsg = False _empty_name_default = 'EmptyNameDefault' _settings = {} _settings['line_length'] = 65 _settings['min_wrap_length'] = 100 _commands = {} _commands['message'] = 'say %(prefix)s [^3%(name)s^7]: %(message)s' _commands['deadsay'] = 'say %(prefix)s^7 %(message)s' _commands['say'] = 'say %(prefix)s^7 %(message)s' _commands['set'] = 'set %(name)s "%(value)s"' _commands['kick'] = 'clientkick %(cid)s' _commands['ban'] = 'addip %(cid)s' _commands['tempban'] = 'clientkick %(cid)s' _eventMap = { 'warmup': b3.events.EVT_GAME_WARMUP, 'shutdowngame': b3.events.EVT_GAME_ROUND_END } # remove the time off of the line _lineClear = re.compile(r'^(?:[0-9:]+\s?)?') #0:00 ClientUserinfo: 0: _lineFormats = ( #Generated with : SoF2 version: SOF2MP GOLD V1.03 win-x86 Nov 5 2002 #Kill: 0 0 18: xlr8or killed xlr8or by MOD_SMOHG92_GRENADE #Kill: <killer> <victim> <meansofdeath> re.compile( r'^(?P<action>[a-z]+):\s*(?P<data>(?P<acid>[0-9]+)\s(?P<cid>[0-9]+)\s(?P<aweap>[0-9]+):\s*(?P<text>.*))$', re.IGNORECASE), #hit: 0 0 520 368 0: xlr8or hit xlr8or at location 520 for 368 #hit: 0 1 8192 80 0: xlr8or hit sh.andrei at location 8192 for 80 #hit: <acid> <cid> <location> <damage> <meansofdeath>: <aname> hit <name> at location <location> for <damage> re.compile( r'^(?P<action>[a-z]+):\s(?P<data>(?P<acid>[0-9]+)\s(?P<cid>[0-9]+)\s(?P<hitloc>[0-9]+)\s(?P<damage>[0-9]+)\s(?P<aweap>[0-9]+):\s+(?P<text>.*))$' , re.IGNORECASE), #say: xlr8or: hello re.compile(r'^(?P<action>say):\s*(?P<data>(?P<name>[^:]+):\s*(?P<text>.*))$', re.IGNORECASE), #ClientConnect: <cid> - <ip>:<port> [<guid>] re.compile( r'^(?P<action>[a-z]+):\s(?P<data>(?P<cid>[0-9]+)\s-\s(?P<ip>[0-9.]+):(?P<port>[-0-9]+)\s\[(?P<cl_guid>[0-9A-Z]{32})\])$', re.IGNORECASE), #Bot connecting #ClientConnect: 4 - [] re.compile(r'^(?P<action>ClientConnect):\s*(?P<data>(?P<bcid>[0-9]+)\s-\s\s\[\])$', re.IGNORECASE), #Falling thru? re.compile(r'^(?P<action>[a-z]+):\s*(?P<data>.*)$', re.IGNORECASE) ) #status #map: mp_shop #num score ping name lastmsg address qport rate #--- ----- ---- --------------- ------- --------------------- ----- ----- # 0 0 103 xlr8or 50 145.99.135.000:-2820 64603 9000 # 1 24 121 ~cGs*Pr3z~ 0 178.202.104.000:20100 23805 25000 # 2 20 108 ~cGs*Jonkie* 50 84.85.84.000:-268 18496 25000 # 3 18 999 *DS*88 18200 188.157.129.000:20100 29389 9000 # 4 3 0 Homer~Sexual 50 bot 54183 16384 # 7 6 0 Wet~Sponge 50 bot 0 16384 # 8 4 0 PaashaasSchaamhaarVerzamelaar 50 bot 0 16384 #_regPlayer = re.compile(r'^(?P<slot>[0-9]+)\s+(?P<score>[0-9-]+)\s+(?P<ping>[0-9]+)\s+(?P<name>.*?)\s+(?P<last>[0-9]+)\s+(?P<ip>[0-9.]+):(?P<port>[0-9-]+)\s+(?P<qport>[0-9]+)\s+(?P<rate>[0-9]+)$', re.I) _regPlayer = re.compile( r'^(?P<slot>[0-9]+)\s+(?P<score>[0-9-]+)\s+(?P<ping>[0-9]+)\s+(?P<name>.*?)\s+(?P<last>[0-9]+)\s+(?P<ip>[0-9.]+):(?P<port>[0-9-]+)\s+(?P<qport>[0-9]+)\s+(?P<rate>[0-9]+)$' , re.I) _reColor = re.compile(r'(\^.)|[\x00-\x20]|[\x7E-\xff]') PunkBuster = None #kill modes (aweap, meansofdeath) MOD_UNKNOWN = '0' MOD_KNIFE = '1' MOD_M1911A1_PISTOL = '2' MOD_USSOCOM_PISTOL = '3' MOD_SILVER_TALON = '4' MOD_M590_SHOTGUN = '5' MOD_MICRO_UZI_SUBMACHINEGUN = '6' MOD_M3A1_SUBMACHINEGUN = '7' MOD_MP5 = '8' MOD_USAS_12_SHOTGUN = '9' MOD_M4_ASSAULT_RIFLE = '10' MOD_AK74_ASSAULT_RIFLE = '11' MOD_SIG551 = '12' MOD_MSG90A1_SNIPER_RIFLE = '13' MOD_M60_MACHINEGUN = '14' MOD_MM1_GRENADE_LAUNCHER = '15' MOD_RPG7_LAUNCHER = '16' MOD_M84_GRENADE = '17' MOD_SMOHG92_GRENADE = '18' MOD_ANM14_GRENADE = '19' MOD_M15_GRENADE = '20' MOD_WATER = '21' MOD_CRUSH = '22' MOD_TELEFRAG = '23' MOD_FALLING = '24' MOD_SUICIDE = '25' MOD_TEAMCHANGE = '26' MOD_TARGET_LASER = '27' MOD_TRIGGER_HURT = '28' MOD_TRIGGER_HURT_NOSUICIDE = '29' MOD_ADMIN_STRIKE = '30' MOD_ADMIN_SLAP = '31' MOD_ADMIN_FRY = '32' MOD_ADMIN_EXPLODE = '33' MOD_ADMIN_TELEFRAG = '34' MOD_KNIFE_ALT = '35' MOD_M1911A1_PISTOL_ALT = '36' MOD_USSOCOM_PISTOL_ALT = '37' MOD_SILVER_TALON_ALT = '38' MOD_M590_SHOTGUN_ALT = '39' MOD_M4_ASSAULT_RIFLE_ALT = '40' MOD_AK74_ASSAULT_RIFLE_ALT = '41' MOD_M84_GRENADE_ALT = '42' MOD_SMOHG92_GRENADE_ALT = '43' MOD_ANM14_GRENADE_ALT = '44' MOD_M15_GRENADE_ALT = '45' def startup(self): # add the world client client = self.clients.newClient('-1', guid='WORLD', name='World', hide=True, pbid='WORLD') if self.privateMsg: self.warning('SoF2 will need a mod to enable private messaging!') if not self.config.has_option('server', 'punkbuster') or self.config.getboolean('server', 'punkbuster'): self.PunkBuster = b3.parsers.punkbuster.PunkBuster(self) # get map from the status rcon command map = self.getMap() if map: self.game.mapName = map self.info('map is: %s' % self.game.mapName) # initialize connected clients plist = self.getPlayerList() for cid, c in plist.iteritems(): #self.debug(c) userinfostring = self.queryClientUserInfoByName(cid, c['name']) if userinfostring: self.OnClientuserinfo(None, userinfostring) def getLineParts(self, line): line = re.sub(self._lineClear, '', line, 1) for f in self._lineFormats: m = re.match(f, line) if m: #self.debug('line matched %s' % f.pattern) break if m: client = None target = None return (m, m.group('action').lower(), m.group('data').strip(), client, target) else: self.verbose('line did not match format: %s' % line) def parseUserInfo(self, info): #0 \ip\145.99.135.000:-12553\cl_guid\XXXXD914662572D3649B94B1EA5F921\cl_punkbuster\0\details\5\name\xlr8or\rate\9000\snaps\20\identity\NPC_Sam/sam_gladstone\cl_anonymous\0\cg_predictItems\1\cg_antiLag\1\cg_autoReload\1\cg_smoothClients\0\team_identity\shopguard1\outfitting\GACAA playerID, info = string.split(info, ' ', 1) if info[:1] != '\\': info = '\\' + info options = re.findall(r'\\([^\\]+)\\([^\\]+)', info) data = {} for o in options: data[o[0]] = o[1] data['cid'] = playerID if data.has_key('n'): data['name'] = data['n'] t = 0 if data.has_key('team'): t = data['team'] elif data.has_key('t'): t = data['t'] data['team'] = self.getTeam(t) if data.has_key('cl_guid') and not data.has_key('pbid'): data['pbid'] = data['cl_guid'] return data # Need to override message format. Game does not support PM's def message(self, client, text): try: if client == None: self.say(text) elif client.cid == None: pass else: lines = [] for line in self.getWrap(text, self._settings['line_length'], self._settings['min_wrap_length']): lines.append(self.getCommand('message', prefix=self.msgPrefix, name=client.name, message=line)) self.writelines(lines) except: pass def OnClientconnect(self, action, data, match=None): # we get user info in two parts: # ClientConnect: 0 - 79.172.5.254:20100 [894E22B3636F8E9198C566C28AD87D0B] # ClientUserinfoChanged: 0 n\xlr8or\t\0\identity\NPC_Sam/sam_gladstone # we need to store the ClientConnect ID, the guid and IP for the next call to Clientuserinfochanged only on initial connection try: self._clientConnectID = match.group('cid') # Normal client connected except: try: self._clientConnectID = match.group('bcid') # Game Bot identifier self._clientConnectGuid = 'BOT' + str(match.group('bcid')) self._clientConnectIp = '0.0.0.0' self.bot('Bot Connected') return None except: self.error('Parser could not connect client') return None try: self._clientConnectGuid = match.group('cl_guid') # If we have no cl_guid we'll use the ip instead. except: self._clientConnectGuid = match.group('ip') self._clientConnectIp = match.group('ip') self.verbose('Client Connected cid: %s, guid: %s, ip: %s' % ( self._clientConnectID, self._clientConnectGuid, self._clientConnectIp)) # Parse Userinfo # Only called when bot is starting on a populated server def OnClientuserinfo(self, action, data, match=None): #0 \ip\145.99.135.000:-12553\cl_guid\XXXXD914662572D3649B94B1EA5F921\cl_punkbuster\0\details\5\name\xlr8or\rate\9000\snaps\20\identity\NPC_Sam/sam_gladstone\cl_anonymous\0\cg_predictItems\1\cg_antiLag\1\cg_autoReload\1\cg_smoothClients\0\team_identity\shopguard1\outfitting\GACAA bclient = self.parseUserInfo(data) if bclient.has_key('name'): # remove spaces from name bclient['name'] = bclient['name'].replace(' ','') # split port from ip field if bclient.has_key('ip'): if bclient['ip'] == 'bot': #not sure if this one works... self.bot('Bot Connected!') bclient['ip'] = '0.0.0.0' bclient['cl_guid'] = 'BOT' + str(bclient['cid']) else: ipPortData = string.split(bclient['ip'], ':', 1) bclient['ip'] = ipPortData[0] if len(ipPortData) > 1: bclient['port'] = ipPortData[1] if bclient.has_key('team'): bclient['team'] = self.getTeam(bclient['team']) if bclient.has_key('cl_guid') and not bclient.has_key('pbid') and self.PunkBuster: bclient['pbid'] = bclient['cl_guid'] self.verbose('Parsed user info %s' % bclient) if bclient: client = self.clients.getByCID(bclient['cid']) if client: # update existing client for k, v in bclient.iteritems(): setattr(client, k, v) else: #make a new client if self.PunkBuster: # we will use punkbuster's guid guid = None else: # use io guid if bclient.has_key('cl_guid'): guid = bclient['cl_guid'] else: guid = 'unknown' if not bclient.has_key('name'): bclient['name'] = self._empty_name_default if not bclient.has_key('ip') and guid == 'unknown': # happens when a client is (temp)banned and got kicked so client was destroyed, but # infoline was still waiting to be parsed. self.debug('Client disconnected. Ignoring.') return None nguid = '' # overide the guid... use ip's only if self.console.IpsOnly is set True. if self.IpsOnly: nguid = bclient['ip'] # replace last part of the guid with two segments of the ip elif self.IpCombi: i = bclient['ip'].split('.') d = len(i[0])+len(i[1]) nguid = guid[:-d]+i[0]+i[1] # Some Quake clients don't have a cl_guid, we'll use ip instead, this is pure fallback! elif guid == 'unknown': nguid = bclient['ip'] if nguid != '': guid = nguid client = self.clients.newClient(bclient['cid'], name=bclient['name'], ip=bclient['ip'], state=b3.STATE_ALIVE, guid=guid, data={ 'guid' : guid }) return None def OnClientuserinfochanged(self, action, data, match=None): #ClientUserinfoChanged: 0 n\xlr8or\t\0\identity\NPC_Sam/sam_gladstone try: id = self._clientConnectID except: id = None # We've already connected before self._clientConnectID = None bclient = self.parseUserInfo(data) self.verbose('Parsed user info %s' % bclient) if bclient: client = self.clients.getByCID(bclient['cid']) if id: bclient['cl_guid'] = self._clientConnectGuid self._clientConnectGuid = None bclient['ip'] = self._clientConnectIp self._clientConnectIp = None if client: # update existing client bclient['cl_guid'] = client.guid bclient['ip'] = client.ip for k, v in bclient.iteritems(): setattr(client, k, v) else: #make a new client client = self.clients.newClient(bclient['cid'], name=bclient['name'], ip=bclient['ip'], state=b3.STATE_ALIVE, guid=bclient['cl_guid'], data={'guid': bclient['cl_guid']}) if id: return b3.events.Event(b3.events.EVT_CLIENT_JOIN, None, client) else: return None # disconnect def OnClientdisconnect(self, action, data, match=None): client = self.clients.getByCID(data) if client: client.disconnect() return None def OnInitgame(self, action, data, match=None): options = re.findall(r'\\([^\\]+)\\([^\\]+)', data) for o in options: if o[0] == 'mapname': self.game.mapName = o[1] elif o[0] == 'g_gametype': self.game.gameType = self.defineGameType(o[1]) elif o[0] == 'fs_game': self.game.modName = o[1] else: setattr(self.game, o[0], o[1]) self.verbose('Current gameType: %s' % self.game.gameType) self.game.startRound() return b3.events.Event(b3.events.EVT_GAME_ROUND_START, self.game) # say def OnSay(self, action, data, match=None): #3:59 say: XLR8or: general chat msg = string.split(data, ': ', 1) if not len(msg) == 2: return None client = self.clients.getByExactName(msg[0]) if client: self.verbose('OnSay: Client Found: %s' % client.name) return b3.events.Event(b3.events.EVT_CLIENT_SAY, msg[1], client) else: self.verbose('OnSay: No Client Found!') return None # sayteam def OnSayteam(self, action, data, match=None): #4:06 sayteam: XLR8or: teamchat msg = string.split(data, ': ', 1) if not len(msg) == 2: return None client = self.clients.getByExactName(msg[0]) if client: self.verbose('OnSayTeam: Client Found: %s' % client.name) return b3.events.Event(b3.events.EVT_CLIENT_TEAM_SAY, msg[1], client, client.team) else: self.verbose('OnSayTeam: No Client Found!') return None # damage #hit: 0 0 520 368 0: xlr8or hit xlr8or at location 520 for 368 #Hit: cid acid hitloc damage aweap: text def OnHit(self, action, data, match=None): victim = self.clients.getByCID(match.group('cid')) if not victim: self.debug('No victim') #self.OnClientuserinfo(action, data, match) return None attacker = self.clients.getByCID(match.group('acid')) if not attacker: self.debug('No attacker') return None event = b3.events.EVT_CLIENT_DAMAGE if attacker.cid == victim.cid: event = b3.events.EVT_CLIENT_DAMAGE_SELF elif attacker.team != b3.TEAM_UNKNOWN and attacker.team == victim.team: event = b3.events.EVT_CLIENT_DAMAGE_TEAM victim.hitloc = match.group('hitloc') #victim.state = b3.STATE_ALIVE return b3.events.Event(event, (match.group('damage'), match.group('aweap'), victim.hitloc), attacker, victim) # kill #kill: acid cid aweap: <text> def OnKill(self, action, data, match=None): # kill modes characteristics : """ 0: MOD_UNKNOWN, UNKNOWN 1: MOD_KNIFE, Killed by KNIFE 2: MOD_M1911A1_PISTOL, Killed by M1911A1_PISTOL 3: MOD_USSOCOM_PISTOL, Killed by USSOCOM_PISTOL 4: MOD_SILVER_TALON, Killed by SILVER_TALON 5: MOD_M590_SHOTGUN, Killed by M590_SHOTGUN 6: MOD_MICRO_UZI_SUBMACHINEGUN, Killed by MICRO_UZI_SUBMACHINEGUN 7: MOD_M3A1_SUBMACHINEGUN, Killed by M3A1_SUBMACHINEGUN 8: MOD_MP5, Killed by MP5 9: MOD_USAS_12_SHOTGUN, Killed by USAS_12_SHOTGUN 10: MOD_M4_ASSAULT_RIFLE, Killed by M4_ASSAULT_RIFLE 11: MOD_AK74_ASSAULT_RIFLE, Killed by AK74_ASSAULT_RIFLE 12: MOD_SIG551, Killed by SIG551 13: MOD_MSG90A1_SNIPER_RIFLE, Killed by MSG90A1_SNIPER_RIFLE 14: MOD_M60_MACHINEGUN, Killed by M60_MACHINEGUN 15: MOD_MM1_GRENADE_LAUNCHER, Killed by MM1_GRENADE_LAUNCHER 16: MOD_RPG7_LAUNCHER, Killed by RPG7_LAUNCHER 17: MOD_M84_GRENADE, Killed by M84_GRENADE 18: MOD_SMOHG92_GRENADE, Killed by SMOHG92_GRENADE 19: MOD_ANM14_GRENADE, Killed by ANM14_GRENADE 20: MOD_M15_GRENADE, Killed by M15_GRENADE 21: MOD_WATER, Killed by WATER 22: MOD_CRUSH, Killed by Mover 23: MOD_TELEFRAG, Killed by TELEFRAG 24: MOD_FALLING, Killed by FALLING 25: MOD_SUICIDE, Killed by SUICIDE 26: MOD_TEAMCHANGE, Killed by TEAMCHANGE 27: MOD_TARGET_LASER, Killed by TARGET_LASER 28: MOD_TRIGGER_HURT, Killed by TRIGGER_HURT 29: MOD_TRIGGER_HURT_NOSUICIDE, Killed by TRIGGER_HURT_NOSUICIDE 30: MOD_ADMIN_STRIKE, Killed by ADMIN_STRIKE 31: MOD_ADMIN_SLAP, Killed by ADMIN_SLAP 32: MOD_ADMIN_FRY, Killed by ADMIN_FRY 33: MOD_ADMIN_EXPLODE, Killed by ADMIN_EXPLODE 34: MOD_ADMIN_TELEFRAG, Killed by ADMIN_TELEFRAG 35: MOD_KNIFE_ALT, Killed by KNIFE_ALT 36: MOD_M1911A1_PISTOL_ALT, Killed by M1911A1_PISTOL_ALT 37: MOD_USSOCOM_PISTOL_ALT, Killed by USSOCOM_PISTOL_ALT 38: MOD_SILVER_TALON_ALT, Killed by SILVER_TALON_ALT 39: MOD_M590_SHOTGUN_ALT, Killed by M590_SHOTGUN_ALT 40: MOD_M4_ASSAULT_RIFLE_ALT, Killed by M4_ASSAULT_RIFLE_ALT 41: MOD_AK74_ASSAULT_RIFLE, Killed by AK74_ASSAULT_RIFLE 42: MOD_M84_GRENADE_ALT, Killed by M84_GRENADE_ALT 43: MOD_SMOHG92_GRENADE_ALT, Killed by SMOHG92_GRENADE_ALT 44: MOD_ANM14_GRENADE_ALT, Killed by ANM14_GRENADE_ALT 45: MOD_M15_GRENADE_ALT, Killed by M15_GRENADE_ALT """ self.debug('OnKill: %s (%s)' % (match.group('aweap'), match.group('text'))) victim = self.clients.getByCID(match.group('cid')) if not victim: self.debug('No victim') #self.OnClientuserinfo(action, data, match) return None weapon = match.group('aweap') if not weapon: self.debug('No weapon') return None ## Fix attacker if match.group('aweap') in ( self.MOD_MM1_GRENADE_LAUNCHER, self.MOD_RPG7_LAUNCHER, self.MOD_M84_GRENADE, self.MOD_SMOHG92_GRENADE, self.MOD_ANM14_GRENADE, self.MOD_M15_GRENADE, self.MOD_WATER, self.MOD_FALLING, self.MOD_SUICIDE, self.MOD_TRIGGER_HURT, self.MOD_M4_ASSAULT_RIFLE_ALT, self.MOD_M84_GRENADE_ALT, self.MOD_SMOHG92_GRENADE_ALT , self.MOD_ANM14_GRENADE_ALT, self.MOD_M15_GRENADE_ALT): # those kills should be considered suicides self.debug( 'OnKill: mm1_grenade_launcher/rpg7_launcher/m84_grenade/smohg92_grenade/anm14/m15_grenade/water/suicide/trigger_hurt/m4_assault_rifle_alt/m84_grenade_alt/smohg92_grenade_alt/anm14_alt/m15_grenade_alt should be suicides') attacker = victim else: attacker = self.clients.getByCID(match.group('acid')) ## end fix attacker if not attacker: self.debug('No attacker') return None dType = match.group('text').split()[-1:][0] if not dType: self.debug('No damageType, weapon: %s' % weapon) return None event = b3.events.EVT_CLIENT_KILL # fix event for team change and suicides and tk if attacker.cid == victim.cid: if weapon == self.MOD_TEAMCHANGE: """ Do not pass a teamchange event here. That event is passed shortly after the kill. """ self.verbose('Team Change Event Caught, exiting') return None else: event = b3.events.EVT_CLIENT_SUICIDE elif attacker.team != b3.TEAM_UNKNOWN and attacker.team == victim.team: event = b3.events.EVT_CLIENT_KILL_TEAM # if not logging damage we need a general hitloc (for xlrstats) if not hasattr(victim, 'hitloc'): victim.hitloc = 'body' victim.state = b3.STATE_DEAD #self.verbose('OnKill Victim: %s, Attacker: %s, Weapon: %s, Hitloc: %s, dType: %s' % (victim.name, attacker.name, weapon, victim.hitloc, dType)) # need to pass some amount of damage for the teamkill plugin - 100 is a kill return b3.events.Event(event, (100, weapon, victim.hitloc, dType), attacker, victim) # item def OnItem(self, action, data, match=None): #Item: 5 weapon_betty cid, item = string.split(data, ' ', 1) client = self.clients.getByCID(cid) if client: #self.verbose('OnItem: %s picked up %s' % (client.name, item) ) return b3.events.Event(b3.events.EVT_CLIENT_ITEM_PICKUP, item, client) return None # Translate the gameType to a readable format def defineGameType(self, gameTypeInt): _gameType = '' _gameType = str(gameTypeInt) #self.debug('gameTypeInt: %s' % gameTypeInt) if gameTypeInt == '0': _gameType = 'ass' elif gameTypeInt == '1': _gameType = 'cnh' elif gameTypeInt == '2': _gameType = 'ctb' elif gameTypeInt == '3': _gameType = 'cctf' elif gameTypeInt == '4': _gameType = 'ctf' elif gameTypeInt == '5': _gameType = 'dem' elif gameTypeInt == '6': _gameType = 'dm' elif gameTypeInt == '7': _gameType = 'dom' elif gameTypeInt == '8': _gameType = 'elim' elif gameTypeInt == '9': _gameType = 'gold' elif gameTypeInt == '10': _gameType = 'inf' elif gameTypeInt == '11': _gameType = 'knockback' elif gameTypeInt == '12': _gameType = 'lms' elif gameTypeInt == '13': _gameType = 'rctf' elif gameTypeInt == '14': _gameType = 'stq' elif gameTypeInt == '15': _gameType = 'tctb' elif gameTypeInt == '16': _gameType = 'tdm' elif gameTypeInt == '17': _gameType = 'tstq' #self.debug('_gameType: %s' % _gameType) return _gameType def joinPlayers(self): plist = self.getPlayerList() for cid, c in plist.iteritems(): client = self.clients.getByCID(cid) if client: self.debug('Joining %s' % client.name) self.queueEvent(b3.events.Event(b3.events.EVT_CLIENT_JOIN, None, client)) return None def queryClientUserInfoByName(self, cid, name): """ ]\dumpuser xlr8or Player xlr8or is not on the server ]\dumpuser xlr8or userinfo -------- ip 145.99.135.000:-12892 cl_guid XXXXD914662572D3649B94B1EA5F921 cl_punkbuster 0 details 5 name xlr8or rate 9000 snaps 20 identity NPC_Sam/sam_gladstone cl_anonymous 0 cg_predictItems 1 cg_antiLag 1 cg_autoReload 1 cg_smoothClients 0 team_identity shopguard1 outfitting GACAA """ data = self.write('dumpuser %s' % name) if not data: return None if data.split('\n')[0] != "userinfo": self.debug("dumpuser %s returned : %s" % (name, data)) self.debug('client probably disconnected, but its character is still hanging in game...') return None datatransformed = "%s " % cid for line in data.split('\n'): if line.strip() == "userinfo" or line.strip() == "--------": continue var = line[:20].strip() val = line[20:].strip() datatransformed += "\\%s\\%s" % (var, val) #self.debug(datatransformed) return datatransformed def getByNameOrJoinPlayer(self, name): client = self.clients.getByExactName(name) if client: return client else: userinfostring = self.queryClientUserInfoByName(name) if userinfostring: self.OnClientuserinfo(None, userinfostring) return self.clients.getByExactName(name) #HL_FOOT_RT #HL_FOOT_LT #HL_LEG_LOWER_RT #HL_LEG_LOWER_LT #HL_LEG_UPPER_RT #HL_LEG_UPPER_LT #HL_ARM_RT #HL_ARM_LT #HL_HAND_RT #HL_HAND_LT #HL_HEAD #HL_NECK #HL_WAIST #HL_BACK #HL_BACK_RT #HL_BACK_LT #HL_CHEST #HL_CHEST_RT #HL_CHEST_LT #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (256, 'right arm'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (32768, 'right chest'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (0, 'Undetected Hits'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (1024, 'Head'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (4, 'upper right Leg'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (8, 'upper left Leg'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (16, 'lower right Leg'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (32, 'lower left Leg'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (262144, 'Neck'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (1, 'right Foot'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (2, 'left Foot'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (512, 'left lower hand'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (128, 'left hand'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (2048, 'Waist'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (131072, 'Chest'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (64, 'right hand'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (4096, 'back right'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (8192, 'back left'); #INSERT INTO `stats_hitlocations` (`ID`, `BODYPART`) VALUES (16384, 'back');