Advertisement
micycle

b3 (BigBrother) Bot AlterOps Parser

Jun 6th, 2014
646
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.82 KB | None | 0 0
  1. # BigBrotherBot(B3) (www.bigbrotherbot.net)
  2. # Copyright (C) 2010
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17. #
  18. # CHANGELOG
  19. #
  20. # 25.05.2011 - 1.2ao - NTAuthority
  21. #   * remove parser stuff
  22. #
  23.  
  24. ## @file
  25. #  CoD7 Parser
  26.  
  27. __author__  = 'Freelander, Courgette, Just a baka, Bravo17, NTAuthority'
  28. __version__ = '1.2ao'
  29.  
  30. import re
  31. import string
  32. import threading
  33. import b3.parsers.cod7_rcon as rcon
  34. import b3.parsers.cod5
  35. import os
  36.  
  37. class AlteropsParser(b3.parsers.cod5.Cod5Parser):
  38.     gameName = 'alterops'
  39.     IpsOnly = False
  40.     _guidLength = 1
  41.     OutputClass = rcon.Cod7Rcon
  42.  
  43.     _usePreMatchLogic = False
  44.     _preMatch = False
  45.     _elFound = True
  46.     _igBlockFound = False
  47.     _sgFound = False
  48.     _logTimer = 0
  49.     _logTimerOld = 0
  50.  
  51.     _commands = {}
  52.     _commands['message'] = 'tell %(cid)s %(prefix)s ^3[pm]^7 %(message)s'
  53.     _commands['deadsay'] = 'tell %(cid)s %(prefix)s [DEAD]^7 %(message)s'
  54.     _commands['say'] = 'say %(prefix)s %(message)s'
  55.     _commands['set'] = 'setadmindvar %(name)s "%(value)s"'
  56.     _commands['kick'] = 'clientkick %(cid)s "%(reason)s"'
  57.     _commands['ban'] = 'banclient %(cid)s'
  58.     _commands['unban'] = 'unbanuser %(name)s'
  59.     _commands['tempban'] = 'clientkick %(cid)s "%(reason)s"'
  60.  
  61.     """\
  62.    Next actions need translation to the EVT_CLIENT_ACTION (Treyarch has a different approach on actions)
  63.    While IW put all EVT_CLIENT_ACTION in the A; action, Treyarch creates a different action for each EVT_CLIENT_ACTION.
  64.    """
  65.     _actionMap = (
  66.         'AD', # Actor Damage (dogs)
  67.         'VD'  # Vehicle Damage
  68.     )
  69.  
  70.     #num score ping guid                             name            lastmsg address               qport rate
  71.     #--- ----- ---- -------------------------------- --------------- ------- --------------------- ----- -----
  72.     #  4     0   23 blablablabfa218d4be29e7168c637be ^1XLR^78^9or[^7^7               0 135.94.165.296:63564  25313 25000
  73.     _regPlayer = re.compile(r'^(?P<slot>[0-9]+)\s+(?P<score>[0-9-]+)\s+(?P<ping>[0-9]+)\s+(?P<guid>[0-9]+)\s+(?P<name>.*?)\s+(?P<last>[0-9]+)\s+(?P<ip>[0-9.]+):(?P<port>[0-9-]+)(?P<qportsep>[-\s]+)(?P<qport>[0-9-]+)\s+(?P<rate>[0-9]+)$', re.I)
  74.     _regPlayerWithDemoclient = re.compile(r'^(?P<slot>[0-9]+)\s+(?P<score>[0-9-]+)\s+(?P<ping>[0-9]+)\s+(?P<guid>[0-9]+)\s+(?P<name>.*?)\s+(?P<last>[0-9]+)\s+(?P<ip>[0-9.]+|unknown):?(?P<port>[0-9-]+)?(?P<qportsep>[-\s]+)(?P<qport>[0-9-]+)\s+(?P<rate>[0-9]+)$', re.I)
  75.  
  76.     def startup(self):
  77.         """Implements some necessary tasks on initial B3 start."""
  78.  
  79.         # add the world client
  80.         client = self.clients.newClient('-1', guid='WORLD', name='World', hide=True, pbid='WORLD')
  81.  
  82.         # get map from the status rcon command
  83.         map = self.getMap()
  84.         if map:
  85.             self.game.mapName = map
  86.             self.info('map is: %s'%self.game.mapName)
  87.  
  88.         if self.config.has_option('server', 'use_prematch_logic'):
  89.             self._usePreMatchLogic = self.config.getboolean('server', 'use_prematch_logic')
  90.  
  91.         # Pre-Match Logic part 1
  92.         if self._usePreMatchLogic:
  93.             self._regPlayer, self._regPlayerWithDemoclient = self._regPlayerWithDemoclient, self._regPlayer
  94.             playerList = self.getPlayerList()
  95.             self._regPlayer, self._regPlayerWithDemoclient = self._regPlayerWithDemoclient, self._regPlayer
  96.        
  97.             if len(playerList) >= 4:
  98.                 self.verbose('PREMATCH OFF: PlayerCount >=4: not a Pre-Match')
  99.                 self._preMatch = False
  100.             elif '0' in playerList and playerList['0']['guid'] == '0':
  101.                 self.verbose('PREMATCH OFF: Got a democlient presence: not a Pre-Match')
  102.                 self._preMatch = False
  103.             else:
  104.                 self.verbose('PREMATCH ON: PlayerCount < 4, got no democlient presence. Defaulting to a pre-match.')
  105.                 self._preMatch = True
  106.         else:
  107.             self._preMatch = False
  108.  
  109.         # Force g_logsync
  110.         self.debug('Forcing server cvar g_logsync to %s and turning UNIX timestamp log timers off.' % self._logSync)
  111.         self.write('g_logsync %s' % self._logSync)
  112.         self.write('g_logTimeStampInSeconds 0')
  113.  
  114.         self.setVersionExceptions()
  115.         self.debug('Parser started.')
  116.  
  117.     def parseLine(self, line):
  118.         """Called from parseLine method in Parser class to introduce pre-match logic
  119.        and action mapping
  120.        """
  121.  
  122.         m = self.getLineParts(line)
  123.         if not m:
  124.             return False
  125.  
  126.         match, action, data, client, target = m
  127.  
  128.         func = 'On%s' % string.capwords(action).replace(' ','')
  129.  
  130.         # Timer (in seconds) that always reflects the current event's timestamp
  131.         t = re.match(self._lineTime, line)
  132.         if t:
  133.             self._logTimerOld = self._logTimer
  134.             self._logTimer = int(t.group('minutes')) * 60 + int(t.group('seconds'))
  135.  
  136.         # Pre-Match Logic part 2
  137.         # Ignore Pre-Match K/D-events
  138.         if self._preMatch and (func == 'OnD' or func == 'OnK' or func == 'OnAd' or func == 'OnVd'):
  139.             self.verbose('PRE-MATCH: Ignoring kill/damage.')
  140.             return False
  141.  
  142.         # Prevent OnInitgame from being called twice due to server-side initGame doubling
  143.         elif func == 'OnInitgame':
  144.             if not self._igBlockFound:
  145.                 self._igBlockFound = True
  146.                 self.verbose('Found 1st InitGame from block')
  147.             elif self._logTimerOld <= self._logTimer:
  148.                 self.verbose('Found 2nd InitGame from block, ignoring')
  149.                 return False
  150.  
  151.         # ExitLevel means there will be Pre-Match right after the mapload
  152.         elif self._usePreMatchLogic and func == 'OnExitlevel':
  153.             self._preMatch = True
  154.             self.debug('PRE-MATCH ON: found ExitLevel')
  155.             self._elFound = True
  156.             self._igBlockFound = False
  157.  
  158.         # If we track ShutdownGame events, we could detect sudden server restarts and re-matches
  159.         elif func == 'OnShutdowngame':
  160.             self._sgFound = True
  161.             self._igBlockFound = False
  162.  
  163.         # Round switch (InitGame after ShutdownGame, but there was no ExitLevel):
  164.         if self._preMatch and not self._elFound and self._igBlockFound and self._sgFound and self._logTimerOld <= self._logTimer:
  165.             self.preMatch = False
  166.             self.debug('PRE-MATCH OFF: found a round change.')
  167.             self._igBlockFound = False
  168.             self._sgFound = False
  169.  
  170.         # Timer reset
  171.         elif self._logTimerOld > self._logTimer:
  172.             self.debug('Old timer: %s / New timer: %s' % (self._logTimerOld, self._logTimer))
  173.             if self._usePreMatchLogic:
  174.                 self._preMatch = True
  175.                 self.debug('PRE-MATCH ON: Server crash/restart detected.')
  176.             else:
  177.                 self.debug('Server crash/restart detected.')
  178.             self._elFound = False
  179.             self._igBlockFound = False
  180.             self._sgFound = False
  181.  
  182.             # Payload
  183.             self.write('setadmindvar g_logsync %s' % self._logSync)
  184.             self.write('setadmindvar g_logTimeStampInSeconds 0')
  185.  
  186.         # Initgame after ExitLevel
  187.         else:
  188.             self._elFound = False
  189.             self._sgFound = False
  190.  
  191.         #self.debug("-==== FUNC!!: " + func)
  192.  
  193.         if hasattr(self, func):
  194.             func = getattr(self, func)
  195.             event = func(action, data, match)
  196.  
  197.             if event:
  198.                 self.queueEvent(event)
  199.         elif action in self._eventMap:
  200.             self.queueEvent(b3.events.Event(
  201.                     self._eventMap[action],
  202.                     data,
  203.                     client,
  204.                     target
  205.                 ))
  206.  
  207.         # Addition for cod7 actionMapping
  208.         elif action in self._actionMap:
  209.             self.translateAction(action, data, match)
  210.  
  211.         else:
  212.             self.queueEvent(b3.events.Event(
  213.                     b3.events.EVT_UNKNOWN,
  214.                     str(action) + ': ' + str(data),
  215.                     client,
  216.                     target
  217.                 ))
  218.  
  219.     def OnJ(self, action, data, match=None):
  220.         """Client join"""
  221.  
  222.         codguid = match.group('guid')
  223.         cid = match.group('cid')
  224.         name = match.group('name')
  225.  
  226.         if codguid == '0' and cid == '0' and name == '[3arc]democlient':
  227.             self.verbose('Authentication not required for [3arc]democlient. Aborting Join.')
  228.             self._preMatch = 0
  229.             return None
  230.        
  231.         if len(codguid) < self._guidLength:
  232.             # invalid guid
  233.             self.verbose2('Invalid GUID: %s' %codguid)
  234.             codguid = None
  235.  
  236.         client = self.getClient(match)
  237.  
  238.         if client:
  239.             self.verbose2('ClientObject already exists')
  240.             # lets see if the name/guids match for this client, prevent player mixups after mapchange (not with PunkBuster enabled)
  241.             if not self.PunkBuster:
  242.                 if self.IpsOnly:
  243.                     # this needs testing since the name cleanup code may interfere with this next condition
  244.                     if name != client.name:
  245.                         self.debug('This is not the correct client (%s <> %s), disconnecting' %(name, client.name))
  246.                         client.disconnect()
  247.                         return None
  248.                     else:
  249.                         self.verbose2('client.name in sync: %s == %s' %(name, client.name))
  250.                 else:
  251.                     if codguid != client.guid:
  252.                         self.debug('This is not the correct client (%s <> %s), disconnecting' %(codguid, client.guid))
  253.                         client.disconnect()
  254.                         return None
  255.                     else:
  256.                         self.verbose2('client.guid in sync: %s == %s' %(codguid, client.guid))
  257.             # update existing client
  258.             client.state = b3.STATE_ALIVE
  259.             # possible name changed
  260.             client.name = name
  261.             # Join-event for mapcount reasons and so forth
  262.             return b3.events.Event(b3.events.EVT_CLIENT_JOIN, None, client)
  263.         else:
  264.             if self._counter.get(cid):
  265.                 self.verbose('cid: %s already in authentication queue. Aborting Join.' %cid)
  266.                 return None
  267.             self._counter[cid] = 1
  268.             t = threading.Timer(2, self.newPlayer, (cid, codguid, name))
  269.             t.start()
  270.             self.debug('%s connected, waiting for Authentication...' %name)
  271.             self.debug('Our Authentication queue: %s' % self._counter)
  272.  
  273.     # kill
  274.     def OnK(self, action, data, match=None):
  275.         victim = self.clients.getByGUID(match.group('guid'))
  276.         if not victim:
  277.             self.debug('No victim %s' % match.groupdict())
  278.             self.OnJ(action, data, match)
  279.             return None
  280.  
  281.         attacker = self.clients.getByGUID(match.group('aguid'))
  282.         if not attacker:
  283.             if match.group('acid') == '-1' or match.group('aname') == 'world':
  284.                 self.verbose('World kill')
  285.                 attacker = self.getClient(attacker=match)
  286.             else:
  287.                 self.debug('No attacker %s' % match.groupdict())
  288.                 return None
  289.  
  290.         # COD5 first version doesn't report the team on kill, only use it if it's set
  291.         # Hopefully the team has been set on another event
  292.         if match.group('ateam'):
  293.             attacker.team = self.getTeam(match.group('ateam'))
  294.  
  295.         if match.group('team'):
  296.             victim.team = self.getTeam(match.group('team'))
  297.  
  298.         event = b3.events.EVT_CLIENT_KILL
  299.  
  300.         if attacker == victim or attacker.cid == '-1':
  301.             self.verbose('Suicide Detected, attacker.cid: %s, victim.cid: %s' % (attacker.cid, victim.cid))
  302.             event = b3.events.EVT_CLIENT_SUICIDE
  303.         elif attacker.team != b3.TEAM_UNKNOWN and attacker.team and victim.team and attacker.team == victim.team:
  304.             self.verbose('Team kill detected, %s team killed %s' % (attacker.name, victim.name))
  305.             event = b3.events.EVT_CLIENT_KILL_TEAM
  306.  
  307.         victim.state = b3.STATE_DEAD
  308.         return b3.events.Event(event, (float(match.group('damage')), match.group('aweap'), match.group('dlocation'), match.group('dtype')), attacker, victim)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement