Ejemplo n.º 1
0
class ClanBattleMgrMsgProcessor(OpsUnpacker):
    _opsFormatDefs = initOpsFormatDef({
        CBM_OP.SET_ROUND: ('B', '_setRound'),
        CBM_OP.SET_RESULTS: ('b', '_setResults'),
        CBM_OP.SET_ENEMY_READY: ('B', '_setEnemyReady')
    })

    def __init__(self, unit):
        self._unit = unit

    def _setRound(self, isBattleRound):
        LOG_DEBUG_DEV('ClanBattleMgrMsgProcessor._setRound: %r' %
                      isBattleRound)
        extras = self._unit._extras
        extras['isBattleRound'] = int(isBattleRound)

    def _setResults(self, result):
        LOG_DEBUG_DEV('ClanBattleMgrMsgProcessor._setResults: res=%r' % result)
        extras = self._unit._extras
        extras['battleResultList'].append(result)

    def _setEnemyReady(self, enemyReady):
        LOG_DEBUG_DEV(
            'ClanBattleMgrMsgProcessor._setEnemyReady: enemyReady=%r' %
            enemyReady)
        extras = self._unit._extras
        extras['isEnemyReadyForBattle'] = enemyReady
Ejemplo n.º 2
0
class ClanBattleMgrMsgProcessor(OpsUnpacker):
    _opsFormatDefs = initOpsFormatDef({
        CBM_OP.SET_ROUND: ('B', '_setRound'),
        CBM_OP.SET_BUILDNUM: ('bi', '_setBuildnum'),
        CBM_OP.SET_RESULTS: ('b', '_setResults'),
        CBM_OP.SET_ENEMY_READY: ('B', '_setEnemyReady'),
        CBM_OP.SET_EQUIPMENTS: ('i', '_setEquipments', 'N', [('H', 'qB')]),
        CBM_OP.CLEAR_EQUIPMENTS: ('x', '_clearEquipments')
    })

    def __init__(self, unit):
        self._unit = unit

    def _clearEquipments(self):
        extras = self._unit._extras
        LOG_DEBUG_DEV('_clearEquipments')
        extras['clanEquipments'] = None
        return

    def _setEquipments(self, rev, clanEquipments):
        extras = self._unit._extras
        LOG_DEBUG_DEV('_setEquipments', clanEquipments)
        extras['lastEquipRev'] = rev
        extras['clanEquipments'] = (rev, clanEquipments)

    def _setRound(self, isBattleRound):
        LOG_DEBUG_DEV('ClanBattleMgrMsgProcessor._setRound: %r' %
                      isBattleRound)
        extras = self._unit._extras
        extras['isBattleRound'] = int(isBattleRound)

    def _setBuildnum(self, packedBuildsNum, roundStart=0):
        extras = self._unit._extras
        prevBuildNum, currentBuildNum = parseDirPosByte(packedBuildsNum)
        LOG_DEBUG_DEV(
            'ClanBattleMgrMsgProcessor._setBuildnum: prev=%r, cur=%r' %
            (prevBuildNum, currentBuildNum))
        extras['prevBuildNum'] = prevBuildNum
        extras['currentBuildNum'] = currentBuildNum - 1
        extras['roundStart'] = roundStart

    def _setResults(self, result):
        LOG_DEBUG_DEV('ClanBattleMgrMsgProcessor._setResults: res=%r' % result)
        extras = self._unit._extras
        extras['battleResultList'].append(result)

    def _setEnemyReady(self, enemyReady):
        LOG_DEBUG_DEV(
            'ClanBattleMgrMsgProcessor._setEnemyReady: enemyReady=%r' %
            enemyReady)
        extras = self._unit._extras
        extras['isEnemyReadyForBattle'] = enemyReady
Ejemplo n.º 3
0
class GlobalMapBase(OpsUnpacker):
    _opsFormatDefs = initOpsFormatDef({GM_OP.UNPACK_BATTLE: ('', '_unpackBattle'),
     GM_OP.FINISH_BATTLE: ('q', '_finishBattle'),
     GM_OP.REMOVE_BATTLE: ('q', '_removeBattle'),
     GM_OP.UNPACK_BATTLE_UNIT: ('', '_unpackBattleUnit'),
     GM_OP.REMOVE_BATTLE_UNIT: ('q', '_removeBattleUnit')})
    FORMAT_HEADER = '<ii'
    SIZE_HEADER = struct.calcsize(FORMAT_HEADER)
    FORMAT_ADD_BATTLE_HEADER = '<qHii?'
    SIZE_ADD_BATTLE_HEADER = struct.calcsize(FORMAT_ADD_BATTLE_HEADER)
    FORMAT_ADD_BATTLE_UNIT_HEADER = '<qh'
    SIZE_ADD_BATTLE_UNIT_HEADER = struct.calcsize(FORMAT_ADD_BATTLE_UNIT_HEADER)

    def __init__(self):
        self._empty()

    def _empty(self):
        self.battles = {}
        self.battleUnits = {}
        self._dirty = False
        self._packed = ' '

    def __repr__(self):
        s = ''
        if self.battles:
            s += 'battles(%s)' % len(self.battles)
            for key, args in self.battles.iteritems():
                s += '\n  [%s] %s' % (key, args)

        if self.battleUnits:
            s += '\n battleUnits(%s)' % len(self.battleUnits)
        return s

    def _persist(self):
        pass

    def pack(self, isForced = False):
        if not self._dirty and not isForced:
            return self._packed
        self._dirty = False
        if self.battles:
            packed = struct.pack(self.FORMAT_HEADER, len(self.battles), len(self.battleUnits))
            fmt = self.FORMAT_ADD_BATTLE_HEADER
            for battleID, data in self.battles.iteritems():
                peripheryID = data['peripheryID']
                createTime = data['createTime']
                startTime = data['startTime']
                isFinished = data['isFinished']
                localizedData = packPascalString(cPickle.dumps(data['localizedData'], -1))
                packed += struct.pack(fmt, battleID, peripheryID, createTime, startTime, isFinished)
                packed += localizedData

            fmt = self.FORMAT_ADD_BATTLE_UNIT_HEADER
            for battleID, unitStr in self.battleUnits.iteritems():
                packed += struct.pack(fmt, battleID, len(unitStr))
                packed += unitStr

            self._packed = packed
        else:
            self._empty()
        self._persist()
        return self._packed

    def unpack(self, packedData):
        self._packed = packedData
        if len(packedData) <= 1:
            self._empty()
            return
        lenBattles, lenBattleUnits = struct.unpack_from(self.FORMAT_HEADER, packedData)
        self.battles = {}
        offset = self.SIZE_HEADER
        sz = self.SIZE_ADD_BATTLE_HEADER
        fmt = self.FORMAT_ADD_BATTLE_HEADER
        for i in xrange(lenBattles):
            battleID, peripheryID, createTime, startTime, isFinished = struct.unpack_from(fmt, packedData, offset)
            localizedDataStr, localizedDataLen = unpackPascalString(packedData, offset + sz)
            localizedData = cPickle.loads(localizedDataStr)
            offset += sz + localizedDataLen
            self.battles[battleID] = {'peripheryID': peripheryID,
             'createTime': createTime,
             'startTime': startTime,
             'isFinished': isFinished,
             'localizedData': localizedData}

        self.battleUnits = {}
        sz = self.SIZE_ADD_BATTLE_UNIT_HEADER
        fmt = self.FORMAT_ADD_BATTLE_UNIT_HEADER
        for i in xrange(lenBattleUnits):
            battleID, unitStrLen = struct.unpack_from(fmt, packedData, offset)
            offset += sz
            unitStr = packedData[offset:offset + unitStrLen]
            offset += unitStrLen
            self.battleUnits[battleID] = unitStr

        raise offset == len(packedData) or AssertionError

    def serialize(self):
        if not self.battles:
            return {}
        return dict(battles=self.battles)

    def deserialize(self, pdata):
        if not isinstance(pdata, dict) or not pdata:
            self._empty()
            return
        self._dirty = True
        self.battles = pdata['battles']
        self.battleUnits = {}
        try:
            self.pack()
        except Exception as e:
            LOG_CURRENT_EXCEPTION()
            LOG_DEBUG_DEV('GlobalMapBase.self: %s' % repr(self))
            raise e

    def _addBattle(self, battleID, peripheryID, createTime, startTime, localizedData):
        self.battles[battleID] = {'peripheryID': peripheryID,
         'createTime': createTime,
         'startTime': startTime,
         'isFinished': False,
         'localizedData': localizedData}
        packedData = struct.pack(self.FORMAT_ADD_BATTLE_HEADER, battleID, peripheryID, createTime, startTime, False)
        packedData += packPascalString(cPickle.dumps(localizedData, -1))
        self._appendOp(GM_OP.UNPACK_BATTLE, packedData)

    def _unpackBattle(self, packedData):
        battleID, peripheryID, createTime, startTime, isFinished = struct.unpack_from(self.FORMAT_ADD_BATTLE_HEADER, packedData)
        localizedDataStr, localizedDataStrLen = unpackPascalString(packedData, self.SIZE_ADD_BATTLE_HEADER)
        self.battles[battleID] = {'peripheryID': peripheryID,
         'createTime': createTime,
         'startTime': startTime,
         'isFinished': isFinished,
         'localizedData': cPickle.loads(localizedDataStr)}
        return packedData[self.SIZE_ADD_BATTLE_HEADER + localizedDataStrLen:]

    def _finishBattle(self, battleID):
        if battleID in self.battles:
            self.battles[battleID]['isFinished'] = True
            self.battleUnits.pop(battleID, None)
            self.storeOp(GM_OP.FINISH_BATTLE, battleID)
        return

    def _removeBattle(self, battleID):
        self.battles.pop(battleID, None)
        self.battleUnits.pop(battleID, None)
        self.storeOp(GM_OP.REMOVE_BATTLE, battleID)
        return

    def _addBattleUnit(self, battleID, strUnitUpdate):
        self.battleUnits[battleID] = strUnitUpdate
        packedData = struct.pack(self.FORMAT_ADD_BATTLE_UNIT_HEADER, battleID, len(strUnitUpdate))
        packedData += strUnitUpdate
        self._appendOp(GM_OP.UNPACK_BATTLE_UNIT, packedData)

    def _unpackBattleUnit(self, packedData):
        battleID, unitStrLen = struct.unpack_from(self.FORMAT_ADD_BATTLE_UNIT_HEADER, packedData)
        begin, end = self.SIZE_ADD_BATTLE_UNIT_HEADER, self.SIZE_ADD_BATTLE_UNIT_HEADER + unitStrLen
        self.battleUnits[battleID] = packedData[begin:end]
        return packedData[end:]

    def _removeBattleUnit(self, battleID):
        self.battleUnits.pop(battleID, None)
        self.storeOp(GM_OP.REMOVE_BATTLE_UNIT, battleID)
        return
Ejemplo n.º 4
0
class UnitBase(OpsUnpacker):
    _opsFormatDefs = initOpsFormatDef({
        UNIT_OP.SET_VEHICLE: ('qHi', '_setVehicle'),
        UNIT_OP.SET_MEMBER: ('qB', '_setMember'),
        UNIT_OP.DEL_MEMBER: ('B', '_delMemberBySlot'),
        UNIT_OP.ADD_PLAYER: ('', '_unpackPlayer'),
        UNIT_OP.REMOVE_PLAYER: ('q', '_removePlayer'),
        UNIT_OP.READY_MASK: ('H', '_setReadyMask'),
        UNIT_OP.SET_SLOT: ('', '_unpackRosterSlot'),
        UNIT_OP.CLEAR_VEHICLE: ('q', '_clearVehicle'),
        UNIT_OP.VEHICLE_DICT: ('', '_unpackVehicleDict'),
        UNIT_OP.UNIT_FLAGS: ('H', '_setUnitFlags'),
        UNIT_OP.CLOSE_SLOT: ('B', '_closeSlot'),
        UNIT_OP.OPEN_SLOT: ('B', '_openSlot'),
        UNIT_OP.SET_COMMENT: ('', 'setComment', 'S', ['']),
        UNIT_OP.CHANGE_ROLE: ('qH', '_changePlayerRole'),
        UNIT_OP.MODAL_TIMESTAMP: ('i', '_setModalTimestamp'),
        UNIT_OP.GIVE_LEADERSHIP: ('Q', '_giveLeadership'),
        UNIT_OP.CHANGE_DIVISION: ('i', '_changeSortieDivision'),
        UNIT_OP.EXTRAS_UPDATE: ('', 'updateUnitExtras', 'S', ['']),
        UNIT_OP.EXTRAS_RESET: (None, 'resetExtras'),
        UNIT_OP.GAMEPLAYS_MASK: ('i', '_setGameplaysMask'),
        UNIT_OP.SET_VEHICLE_LIST: ('q', '_setVehicleList', 'N', [('H', 'iH')]),
        UNIT_OP.CHANGE_FALLOUT_TYPE: ('i', '_changeFalloutQueueType')
    })
    MAX_PLAYERS = 250

    def __init__(self,
                 limitsDefs={},
                 slotDefs={},
                 slotCount=0,
                 packedRoster='',
                 extrasInit=None,
                 packedUnit='',
                 rosterTypeID=ROSTER_TYPE.UNIT_ROSTER,
                 extrasHandlerID=EXTRAS_HANDLER_TYPE.EMPTY,
                 prebattleTypeID=PREBATTLE_TYPE.UNIT):
        if packedUnit:
            self.unpack(packedUnit)
        else:
            self._rosterTypeID = rosterTypeID
            RosterType = ROSTER_TYPE_TO_CLASS.get(rosterTypeID)
            if slotDefs and not slotCount:
                slotCount = len(slotDefs)
            self._roster = RosterType(limitsDefs, slotDefs, slotCount,
                                      packedRoster)
            self._prebattleTypeID = prebattleTypeID
            self._freeSlots = set(xrange(0, slotCount))
            self._dirty = 1
            self._flags = UNIT_FLAGS.DEFAULT
            self._extrasHandlerID = extrasHandlerID
            eHandler = self._initExtrasHandler()
            self._extras = eHandler.new(initial=extrasInit)
            self._initClean()

    def _initClean(self):
        self._members = {}
        self._vehicles = {}
        self._players = {}
        self._playerSlots = {}
        self._readyMask = 0
        self._gameplaysMask = 0
        self._fullReadyMask = 0
        self._strComment = ''
        self._packedOps = ''
        self._packedCmdrOps = ''
        self._closedSlotMask = 0
        self._notifications = []
        self._reservedSlots = set()
        self._modalTimestamp = 0

    def _initExtrasHandler(self):
        weakSelf = weakref.proxy(self)
        eHnd = self.__extrasHandler = EXTRAS_HANDLER_TYPE_TO_HANDLER[
            self._extrasHandlerID](weakSelf)
        return eHnd

    @property
    def _extrasHandler(self):
        return self.__extrasHandler

    def _setVehicle(self, accountDBID, vehTypeCompDescr, vehInvID):
        return self._setVehicleList(accountDBID,
                                    [(vehInvID, vehTypeCompDescr)])

    def _setVehicleList(self, accountDBID, vehShortList):
        vehs = []
        vehInvIDs = []
        for vehInvID, vehTypeCompDescr in vehShortList:
            classTag = vehicles.getVehicleClass(vehTypeCompDescr)
            vehType = vehicles.getVehicleType(vehTypeCompDescr)
            vehClassIdx = VEHICLE_CLASS_INDICES.get(classTag, _BAD_CLASS_INDEX)
            vehTuple = UnitVehicle(vehInvID, vehTypeCompDescr, vehType.level,
                                   vehClassIdx)
            vehs.append(vehTuple)
            vehInvIDs.append(vehInvID)

        self._vehicles[accountDBID] = vehs
        self.storeOp(UNIT_OP.SET_VEHICLE_LIST, accountDBID, vehShortList)
        self._storeNotification(accountDBID, UNIT_NOTIFY_CMD.SET_VEHICLE_LIST,
                                [vehInvIDs])
        self._dirty = 1
        return True

    def _clearVehicle(self, accountDBID):
        self._vehicles.pop(accountDBID, None)
        slotIdx = self._playerSlots.get(accountDBID)
        if slotIdx is not None:
            self.setMemberReady(accountDBID, False)
        self._dirty = 1
        self.storeOp(UNIT_OP.CLEAR_VEHICLE, accountDBID)
        self._storeNotification(accountDBID, UNIT_NOTIFY_CMD.SET_VEHICLE_LIST,
                                [[]])
        return

    def _setMember(self, accountDBID, slotChosenIdx):
        member = dict(accountDBID=accountDBID, slotIdx=slotChosenIdx)
        self._members[slotChosenIdx] = member
        self._freeSlots.discard(slotChosenIdx)
        self._playerSlots[accountDBID] = slotChosenIdx
        self._fullReadyMask |= 1 << slotChosenIdx
        self.storeOp(UNIT_OP.SET_MEMBER, accountDBID, slotChosenIdx)
        self._dirty = 1

    def _delMemberBySlot(self, slotIdx):
        member = self._members.get(slotIdx, None)
        if not member:
            return UNIT_ERROR.FAIL_UNIT_METHOD
        else:
            accountDBID = member['accountDBID']
            self.setMemberReady(accountDBID, False)
            self._members.pop(slotIdx)
            self._freeSlots.add(slotIdx)
            self._playerSlots.pop(accountDBID, None)
            clearMask = ~(1 << slotIdx)
            self._fullReadyMask &= clearMask
            self._dirty = 1
            self.storeOp(UNIT_OP.DEL_MEMBER, slotIdx)
            return OK

    def _addPlayer(self, accountDBID, **kwargs):
        self._players[accountDBID] = kwargs
        self._dirty = 1
        packed = struct.pack(self._PLAYER_DATA, accountDBID,
                             kwargs.get('accountID', 0),
                             kwargs.get('timeJoin', 0), kwargs.get('role', 0),
                             kwargs.get('igrType', 0), kwargs.get('rating', 0),
                             kwargs.get('peripheryID', 0))
        packed += packPascalString(kwargs.get('nickName', ''))
        packed += packPascalString(kwargs.get('clanAbbrev', ''))
        self._appendOp(UNIT_OP.ADD_PLAYER, packed)

    def _removePlayer(self, accountDBID):
        self._players.pop(accountDBID, None)
        self._vehicles.pop(accountDBID, None)
        self._dirty = 1
        self.storeOp(UNIT_OP.REMOVE_PLAYER, accountDBID)
        return

    def _changePlayerRole(self, accountDBID, roleFlags):
        playerData = self._players.get(accountDBID)
        if playerData:
            playerData['role'] = roleFlags
            self._dirty = 1
            self.storeOp(UNIT_OP.CHANGE_ROLE, accountDBID, roleFlags)

    def _setRosterSlot(self, rosterSlotIdx, packedSlot):
        packedArgs = struct.pack('<B', rosterSlotIdx)
        packedArgs += packedSlot
        slot = self._roster.SLOT_TYPE(packed=packedSlot)
        roster = self._roster
        if slot.vehTypeCompDescr is None and slot.nationMask == 0 and slot.vehClassMask == 0:
            roster.slots.pop(rosterSlotIdx, None)
        else:
            neighbourSlotIdx = rosterSlotIdx ^ 1
            neighbourSlot = roster.slots.get(neighbourSlotIdx)
            if neighbourSlot and packedSlot == roster.DEFAULT_SLOT_PACK:
                LOG_DEBUG('_setRosterSlot: removing default slotIdx=%s' %
                          rosterSlotIdx)
                roster.slots.pop(rosterSlotIdx, None)
            else:
                roster.slots[rosterSlotIdx] = slot
                if neighbourSlot and neighbourSlot.pack(
                ) == roster.DEFAULT_SLOT_PACK:
                    LOG_DEBUG(
                        '_setRosterSlot: removing default neighbour slotIdx=%s'
                        % rosterSlotIdx)
                    roster.slots.pop(neighbourSlotIdx, None)
        roster.pack()
        self._dirty = 1
        self._appendOp(UNIT_OP.SET_SLOT, packedArgs)
        return

    def isEmpty(self):
        for accountDBID, playerInfo in self._players.iteritems():
            role = playerInfo.get('role', 0)
            if role & UNIT_ROLE.INVITED == 0:
                return False

        return True

    _HEADER = '<HHHHHHBii'
    _PLAYER_DATA = '<qiIHBHH'
    _PLAYER_VEHICLES_LIST = '<qH'
    _PLAYER_VEHICLE_TUPLE = '<iH'
    _SLOT_PLAYERS = '<Bq'
    _IDS = '<HBB'
    _VEHICLE_DICT_HEADER = '<Hq'
    _VEHICLE_DICT_ITEM = '<Hi'
    _HEADER_SIZE = struct.calcsize(_HEADER)
    _SLOT_PLAYERS_SIZE = struct.calcsize(_SLOT_PLAYERS)
    _PLAYER_DATA_SIZE = struct.calcsize(_PLAYER_DATA)
    _PLAYER_VEHICLES_LIST_SIZE = struct.calcsize(_PLAYER_VEHICLES_LIST)
    _PLAYER_VEHICLE_TUPLE_SIZE = struct.calcsize(_PLAYER_VEHICLE_TUPLE)
    _IDS_SIZE = struct.calcsize(_IDS)
    _VEHICLE_DICT_HEADER_SIZE = struct.calcsize(_VEHICLE_DICT_HEADER)
    _VEHICLE_DICT_ITEM_SIZE = struct.calcsize(_VEHICLE_DICT_ITEM)

    def pack(self):
        packed = struct.pack(self._IDS, self._rosterTypeID,
                             self._extrasHandlerID, self._prebattleTypeID)
        packed += self._roster.getPacked()
        members = self._members
        players = self._players
        vehs = self._vehicles
        extras = self._extras
        extrasStr = self._extrasHandler.pack(extras)
        args = (len(members), len(vehs), len(players), len(extrasStr),
                self._readyMask, self._flags, self._closedSlotMask,
                self._modalTimestamp, self._gameplaysMask)
        packed += struct.pack(self._HEADER, *args)
        for accountDBID, vehList in vehs.iteritems():
            packed += struct.pack(self._PLAYER_VEHICLES_LIST, accountDBID,
                                  len(vehList))
            for vehTuple in vehList:
                packed += struct.pack(self._PLAYER_VEHICLE_TUPLE,
                                      vehTuple.vehInvID,
                                      vehTuple.vehTypeCompDescr)

        for slotIdx, member in members.iteritems():
            packed += struct.pack(self._SLOT_PLAYERS, slotIdx,
                                  member['accountDBID'])

        for accountDBID, playerData in players.iteritems():
            packed += struct.pack(self._PLAYER_DATA, accountDBID,
                                  playerData.get('accountID', 0),
                                  playerData.get('timeJoin', 0),
                                  playerData.get('role', 0),
                                  playerData.get('igrType', 0),
                                  playerData.get('rating', 0),
                                  playerData.get('peripheryID', 0))
            packed += packPascalString(playerData.get('nickName', ''))
            packed += packPascalString(playerData.get('clanAbbrev', ''))

        packed += extrasStr
        packed += packPascalString(self._strComment)
        self._packed = packed
        self._dirty = 0
        return packed

    def unpack(self, packed):
        self._initClean()
        self._rosterTypeID, self._extrasHandlerID, self._prebattleTypeID = struct.unpack_from(
            self._IDS, packed)
        RosterType = ROSTER_TYPE_TO_CLASS.get(self._rosterTypeID)
        self._roster = RosterType()
        self._initExtrasHandler()
        unpacking = packed[self._IDS_SIZE:]
        unpacking = self._roster.unpack(unpacking)
        slotCount = self.getMaxSlotCount()
        self._freeSlots = set(xrange(0, slotCount))
        memberCount, vehCount, playerCount, extrasLen, self._readyMask, self._flags, self._closedSlotMask, self._modalTimestamp, self._gameplaysMask = struct.unpack_from(
            self._HEADER, unpacking)
        unpacking = unpacking[self._HEADER_SIZE:]
        for i in xrange(0, vehCount):
            accountDBID, vehListCount = struct.unpack_from(
                self._PLAYER_VEHICLES_LIST, unpacking)
            unpacking = unpacking[self._PLAYER_VEHICLES_LIST_SIZE:]
            vehDataList = []
            for i in xrange(0, vehListCount):
                vehInvID, vehTypeCompDescr = struct.unpack_from(
                    self._PLAYER_VEHICLE_TUPLE, unpacking)
                unpacking = unpacking[self._PLAYER_VEHICLE_TUPLE_SIZE:]
                vehDataList.append((vehInvID, vehTypeCompDescr))

            self._setVehicleList(accountDBID, vehDataList)

        for i in xrange(0, memberCount):
            slotIdx, accountDBID = struct.unpack_from(self._SLOT_PLAYERS,
                                                      unpacking)
            self._setMember(accountDBID, slotIdx)
            unpacking = unpacking[self._SLOT_PLAYERS_SIZE:]

        sz = self._PLAYER_DATA_SIZE
        for i in xrange(0, playerCount):
            accountDBID, accountID, timeJoin, role, igrType, rating, peripheryID = struct.unpack_from(
                self._PLAYER_DATA, unpacking)
            nickName, lenNickBytes = unpackPascalString(unpacking, sz)
            clanAbbrev, lenClanBytes = unpackPascalString(
                unpacking, sz + lenNickBytes)
            unpacking = unpacking[sz + lenNickBytes + lenClanBytes:]
            self._addPlayer(accountDBID,
                            accountID=accountID,
                            timeJoin=timeJoin,
                            role=role,
                            rating=rating,
                            nickName=nickName,
                            clanAbbrev=clanAbbrev,
                            peripheryID=peripheryID,
                            igrType=igrType)

        self._extras = self._extrasHandler.unpack(unpacking[:extrasLen])
        unpacking = unpacking[extrasLen:]
        for slotIdx in range(0, 7):
            slotMask = 1 << slotIdx
            if self._closedSlotMask & slotMask:
                self._closeSlot(slotIdx)

        self._strComment, lenCommentBytes = unpackPascalString(unpacking)
        unpacking = unpacking[lenCommentBytes:]
        lengthDiff = len(packed) - len(unpacking)
        self._packed = packed[:lengthDiff]
        self._packedOps = ''
        self._dirty = 0
        return unpacking

    def isDirty(self):
        return self._dirty

    def getCommanderDBID(self):
        return self._members.get(LEADER_SLOT, {}).get('accountDBID', 0)

    def getAccountsStates(self):
        statesDict = {}
        assignedPlayers = self._playerSlots.keys()
        accountsVehicles = self._vehicles
        isMemberReadyFunc = self.isMemberReady
        for accountDBID, playerData in self._players.iteritems():
            vehs = accountsVehicles.get(accountDBID)
            statesDict[accountDBID] = (vehs[0].vehTypeCompDescr if vehs else
                                       None, accountDBID in assignedPlayers,
                                       isMemberReadyFunc(accountDBID))

        return statesDict

    def updateUnitExtras(self, updateStr):
        oldExtras = copy.deepcopy(self._extras)
        self._extrasHandler.updateUnitExtras(self._extras, updateStr)
        newExtras = self._extras
        LOG_DEBUG_DEV('updateUnitExtras', oldExtras, newExtras)
        self.storeOp(UNIT_OP.EXTRAS_UPDATE, updateStr)
        for accountDBID, playerData in self._players.iteritems():
            if playerData and playerData.get('role',
                                             0) & UNIT_ROLE.INVITED == 0:
                self._storeNotification(accountDBID,
                                        UNIT_NOTIFY_CMD.EXTRAS_UPDATED,
                                        [newExtras])

        self._storeNotification(UNIT_NOTIFY_ID.PARENT_UNIT_MGR,
                                UNIT_NOTIFY_CMD.EXTRAS_UPDATED,
                                [oldExtras, newExtras])
        self._dirty = 1

    def getPacked(self):
        if self._dirty:
            return self.pack()
        else:
            return self._packed

    def isReady(self):
        readyMask = self._readyMask
        return readyMask == self._fullReadyMask and readyMask

    def anyReady(self):
        return bool(self._readyMask)

    def isLocked(self):
        return bool(self._flags & UNIT_FLAGS.LOCKED)

    def isInviteOnly(self):
        return bool(self._flags & UNIT_FLAGS.INVITE_ONLY)

    def isIdle(self):
        return self._flags & UNIT_FLAGS.MODAL_STATES == 0

    def isDevMode(self):
        return bool(self._flags & UNIT_FLAGS.DEV_MODE)

    def isInQueue(self):
        return bool(self._flags & UNIT_FLAGS.IN_QUEUE)

    def isInArena(self):
        return bool(self._flags & UNIT_FLAGS.IN_ARENA)

    def isInPreArena(self):
        return bool(self._flags & UNIT_FLAGS.IN_PRE_ARENA)

    def isSortiesForbidden(self):
        return bool(self._flags & UNIT_FLAGS.SORTIES_FORBIDDEN)

    def isRatedBattleForbidden(self):
        return bool(self._flags & UNIT_FLAGS.RATED_BATTLE_FORBIDDEN)

    def isDynamic(self):
        return bool(self._flags & UNIT_FLAGS.IS_DYNAMIC)

    def isSquad(self):
        return bool(self._rosterTypeID & UNIT_MGR_FLAGS.SQUAD)

    def shouldPublish(self):
        return not (self.isInviteOnly() or self.isSortiesForbidden()
                    or self.isRatedBattleForbidden())

    def __repr__(self):
        repr = 'Unit(\n  _members len=%s {' % len(self._members)
        for slotIdx, member in self._members.iteritems():
            repr += '\n    [%d] %s' % (slotIdx, member)

        repr += '\n  },'
        repr += '\n  state=0x%02X, readyMask=0x%02X, fullReadyMask=0x%02X, closedSlotMask=0x%02X' % (
            self._flags, self._readyMask, self._fullReadyMask,
            self._closedSlotMask)
        repr += '\n  state(names):%s' % reprBitMaskFromDict(
            self._flags, UNIT_STATE_NAMES)
        repr += '\n  modalTimestamp:%s' % self._modalTimestamp
        repr += '\n  _vehicles len=%s {' % len(self._vehicles)
        for accountDBID, veh in self._vehicles.iteritems():
            repr += '\n    [%d] %s' % (accountDBID, str(veh))

        repr += '\n  },'
        repr += '\n  _players len=%s {' % len(self._players)
        for accountDBID, playerData in self._players.iteritems():
            repr += '\n    [%d] %s role=%s' % (accountDBID, playerData,
                                               reprBitMaskFromDict(
                                                   playerData.get('role', 0),
                                                   UNIT_ROLE_NAMES))

        repr += '\n  },'
        repr += '\n  _freeSlots=%r,' % self._freeSlots
        repr += '\n  _roster=%r' % self._roster
        repr += '\n _extras=%r' % self._extras
        repr += '\n  _strComment=%r' % self._strComment
        repr += '\n)'
        return repr

    def dump(self):
        repr = 'Unit(\n membs(%s)={' % len(self._members)
        for slotIdx, member in self._members.iteritems():
            repr += '%d:%s, ' % (slotIdx, member.get('accountDBID', 0))

        repr += '},'
        repr += '\n state=%02X, rdy=%02X, fullRdy=%02X, closed=%02X' % (
            self._flags, self._readyMask, self._fullReadyMask,
            self._closedSlotMask)
        repr += ', stamp:%s, free=%r' % (self._modalTimestamp,
                                         list(self._freeSlots))
        repr += '\n vehs(%s)={' % len(self._vehicles)
        for accountDBID, veh in self._vehicles.iteritems():
            repr += '%d:%s, ' % (accountDBID, str(veh))

        repr += '},'
        repr += '\n plrs(%s)={' % len(self._players)
        for accountDBID, playerData in self._players.iteritems():
            repr += '%d:%r:%02X, ' % (accountDBID,
                                      playerData.get('nickName', ''),
                                      playerData.get('role', 0))

        repr += '},'
        repr += '\n roster=%r' % self._roster.getPacked()
        repr += '\n extras=%r' % self._extras
        repr += '\n)'
        return repr

    def setMemberReady(self, accountDBID, isReady=True):
        slotIdx = self._playerSlots.get(accountDBID)
        if slotIdx is None:
            return UNIT_ERROR.BAD_SLOT_IDX
        else:
            prevReadyMask = self._readyMask
            if isReady:
                vehs = self._vehicles.get(accountDBID)
                if vehs is None:
                    return UNIT_ERROR.VEHICLE_NOT_CHOSEN
                vehList = [(vehicle.vehInvID, vehicle.vehTypeCompDescr)
                           for vehicle in vehs]
                if not self._canUseVehicles(vehList):
                    return UNIT_ERROR.BAD_VEHICLES_SET
                newReadyMask = prevReadyMask | 1 << slotIdx
            else:
                newReadyMask = prevReadyMask & ~(1 << slotIdx)
            if newReadyMask != prevReadyMask:
                self._readyMask = newReadyMask
                self.storeOp(UNIT_OP.READY_MASK, newReadyMask)
                self._dirty = 1
                self._storeNotification(accountDBID,
                                        UNIT_NOTIFY_CMD.SET_MEMBER_READY,
                                        [isReady])
            return OK

    def _canUseVehicles(self, vehiclesList, isSet=False):
        return True

    def _setGameplaysMask(self, newGameplaysMask):
        prevGameplaysMask = self._gameplaysMask
        if prevGameplaysMask != newGameplaysMask:
            self._gameplaysMask = newGameplaysMask
            self.storeOp(UNIT_OP.GAMEPLAYS_MASK, newGameplaysMask)
        return OK

    def isMemberReady(self, accountDBID):
        slotIdx = self._playerSlots.get(accountDBID)
        if slotIdx is not None:
            return bool(self._readyMask & 1 << slotIdx)
        else:
            return False

    def _setModalTimestamp(self, timestamp):
        self._modalTimestamp = timestamp
        self.storeOp(UNIT_OP.MODAL_TIMESTAMP, timestamp)
        self._dirty = 1

    def _setReadyMask(self, mask):
        self._readyMask = mask

    def _setUnitFlags(self, state):
        self._flags = state

    def _closeSlot(self, slotIdx):
        self._closedSlotMask |= 1 << slotIdx
        self._freeSlots.discard(slotIdx)
        self._dirty = 1
        self.storeOp(UNIT_OP.CLOSE_SLOT, slotIdx)

    def getMaxSlotCount(self):
        if self._roster.slots:
            _max = max(self._roster.slots.iterkeys())
        else:
            _max = 0
        return _max / 2 + 1

    def getClosedSlotsCount(self):
        count = 0
        for i in xrange(self._roster.MAX_SLOTS):
            if self._closedSlotMask & 1 << i:
                count += 1

        return count

    def getPointsSum(self):
        sum = 0
        for slotIdx, member in self._members.iteritems():
            accountDBID = member.get('accountDBID', 0)
            vehs = self._vehicles.get(accountDBID)
            if vehs:
                sum += vehs[0].vehLevel

        return sum

    def checkVehicleLevelsRange(self, lvlsByClass, commonLvls):
        for slotIdx, member in self._members.iteritems():
            accountDBID = member.get('accountDBID', None)
            if accountDBID is None:
                continue
            vehs = self._vehicles.get(accountDBID, None)
            if not vehs:
                continue
            for vehTuple in vehs:
                vehLevel = vehTuple.vehLevel
                levelLimits = commonLvls
                if lvlsByClass is not None:
                    levelLimits = lvlsByClass.get(vehTuple.vehClassIdx,
                                                  commonLvls)
                if vehLevel < levelLimits[0] or levelLimits[1] < vehLevel:
                    return False

        return True

    def checkVehicleTypesRange(self, vehicleTypes):
        vehicleCount = {}
        for slotIdx, member in self._members.iteritems():
            accountDBID = member.get('accountDBID', None)
            if accountDBID is None:
                continue
            vehs = self._vehicles.get(accountDBID)
            if not vehs:
                continue
            for vehTuple in vehs:
                vehTypeCompDescr = vehTuple.vehTypeCompDescr
                if vehTypeCompDescr is None:
                    continue
                if vehTypeCompDescr not in vehicleTypes:
                    return UNIT_ERROR.BAD_VEHICLE_TYPE
                if vehTypeCompDescr not in vehicleCount:
                    vehicleCount[vehTypeCompDescr] = 0
                vehicleCount[vehTypeCompDescr] += 1

        if any((value < vehicleTypes[key][0]
                for key, value in vehicleCount.iteritems())):
            return UNIT_ERROR.TOO_FEW_VEHICLE_TYPE
        elif any((value > vehicleTypes[key][1]
                  for key, value in vehicleCount.iteritems())):
            return UNIT_ERROR.TOO_MANY_VEHICLE_TYPE
        else:
            return OK

    def hasInArenaMembers(self):
        for slotIdx, member in self._members.iteritems():
            accountDBID = member.get('accountDBID', 0)
            role = self.getAccountRole(accountDBID)
            if role & UNIT_ROLE.IN_ARENA:
                return True

        return False

    def getLegionaryCount(self):
        count = 0
        for accountDBID, slotIdx in self._playerSlots.iteritems():
            playerData = self._players[accountDBID]
            role = playerData.get('role', 0)
            if role & UNIT_ROLE.LEGIONARY:
                count += 1

        return count

    def getLegionaryMaxCount(self):
        return self._roster.getLegionariesMaxCount()

    def resetExtras(self):
        self._extras = self._extrasHandler.reset(self._extras)
        self.storeOp(UNIT_OP.EXTRAS_RESET)
        self._dirty = 1

    def _openSlot(self, slotIdx):
        self._closedSlotMask &= ~(1 << slotIdx)
        self._freeSlots.add(slotIdx)
        self._dirty = 1
        self.storeOp(UNIT_OP.OPEN_SLOT, slotIdx)

    def _reserveUnitSlot(self, unitSlotIdx):
        self._reservedSlots.add(unitSlotIdx)
        reservedRosterSlot = self._roster.SLOT_TYPE()
        reservedRosterSlot.vehTypeCompDescr = 0
        for slotIdx in (unitSlotIdx * 2, unitSlotIdx * 2 + 1):
            self._roster.slots[slotIdx] = reservedRosterSlot

    def setComment(self, strComment):
        self._strComment = strComment
        self.storeOp(UNIT_OP.SET_COMMENT, strComment)
        self._dirty = 1
        return OK

    def _appendCmdrOp(self, op, packedArgs):
        pass

    def _storeNotification(self, accountDBID, notifyCmd, argList=[]):
        pass

    def _unpackRosterSlot(self, packedOps):
        rosterSlotIdx = struct.unpack_from('<B', packedOps)[0]
        opLen = BaseUnitRosterSlot.getPackSize(packedOps[1]) + 1
        packedSlot = packedOps[1:opLen]
        self._setRosterSlot(rosterSlotIdx, packedSlot)
        return packedOps[opLen:]

    def _packVehicleDict(self, accountDBID, vehDict={}):
        packedArgs = struct.pack(self._VEHICLE_DICT_HEADER, len(vehDict),
                                 accountDBID)
        for vehTypeCompDescr, vehInvID in vehDict.iteritems():
            packedArgs += struct.pack(self._VEHICLE_DICT_ITEM,
                                      vehTypeCompDescr, vehInvID)

        self._appendCmdrOp(UNIT_OP.VEHICLE_DICT, packedArgs)

    def _packFullVehDictUpdates(self):
        for accountDBID, playerData in self._players.iteritems():
            if playerData and playerData.get('role',
                                             0) & UNIT_ROLE.INVITED == 0:
                vehDict = playerData.get('vehDict')
                if vehDict:
                    self._packVehicleDict(accountDBID, vehDict)

    def _unpackVehicleDict(self, packedOps):
        vehCount, accountDBID = struct.unpack_from(self._VEHICLE_DICT_HEADER,
                                                   packedOps)
        vehDict = {}
        opLen = self._VEHICLE_DICT_HEADER_SIZE
        for i in xrange(vehCount):
            vehTypeCompDescr, vehInvID = struct.unpack_from(
                self._VEHICLE_DICT_ITEM, packedOps, opLen)
            vehDict[vehTypeCompDescr] = vehInvID
            opLen += self._VEHICLE_DICT_ITEM_SIZE

        playerData = self._players.get(accountDBID)
        if playerData:
            playerData['vehDict'] = vehDict
        return packedOps[opLen:]

    def _unpackPlayer(self, packedOps):
        sz = self._PLAYER_DATA_SIZE
        accountDBID, accountID, timeJoin, role, igrType, rating, peripheryID = struct.unpack_from(
            self._PLAYER_DATA, packedOps)
        nickName, lenNickBytes = unpackPascalString(packedOps, sz)
        clanAbbrev, lenClanBytes = unpackPascalString(packedOps,
                                                      sz + lenNickBytes)
        playerInfo = dict(accountID=accountID,
                          role=role,
                          timeJoin=timeJoin,
                          rating=rating,
                          nickName=nickName,
                          clanAbbrev=clanAbbrev,
                          peripheryID=peripheryID,
                          igrType=igrType)
        self._addPlayer(accountDBID, **playerInfo)
        return packedOps[sz + lenNickBytes + lenClanBytes:]

    def _giveLeadership(self, newLeaderDBID):
        swapSlotIdx = self._playerSlots.get(newLeaderDBID)
        prevLeaderDBID = self._members[LEADER_SLOT]['accountDBID']
        self.setMemberReady(prevLeaderDBID, False)
        self.setMemberReady(newLeaderDBID, False)
        if swapSlotIdx is not None:
            self._members[swapSlotIdx] = dict(accountDBID=prevLeaderDBID,
                                              slotIdx=swapSlotIdx)
            self._playerSlots[prevLeaderDBID] = swapSlotIdx
        else:
            self._playerSlots.pop(prevLeaderDBID)
        self._players[prevLeaderDBID]['role'] &= ~UNIT_ROLE.CREATOR
        self._members[LEADER_SLOT] = dict(accountDBID=newLeaderDBID,
                                          slotIdx=LEADER_SLOT)
        self._playerSlots[newLeaderDBID] = LEADER_SLOT
        self._players[newLeaderDBID]['role'] |= UNIT_ROLE.CREATOR
        self.storeOp(UNIT_OP.GIVE_LEADERSHIP, newLeaderDBID)
        return prevLeaderDBID

    def _refreshFreeSlots(self, prevMax, newMax):
        if prevMax > newMax:
            for indx in xrange(newMax, prevMax):
                self._freeSlots.discard(indx)

        elif prevMax < newMax:
            for indx in xrange(prevMax, newMax):
                self._freeSlots.add(indx)

    def _getSortieRosterType(self, division):
        prevRosterTypeID = self._rosterTypeID
        newRosterTypeID = SORTIE_DIVISION_LEVEL_TO_FLAGS.get(division, None)
        if newRosterTypeID is None:
            LOG_DEBUG_DEV('Wrong division={}.', division)
            return
        elif newRosterTypeID == prevRosterTypeID:
            LOG_DEBUG_DEV('Division has not changed.')
            return
        RosterType = ROSTER_TYPE_TO_CLASS.get(newRosterTypeID, None)
        if RosterType is None:
            LOG_DEBUG_DEV('Wrong RosterTypeID={}', newRosterTypeID)
            return
        else:
            return (newRosterTypeID, RosterType)

    def _changeSortieDivision(self, division):
        prevRosterTypeID = self._rosterTypeID
        prevRoster = self._roster
        LOG_DEBUG_DEV('Previous roster type: {0} : {1}', prevRosterTypeID,
                      prevRoster.__class__)
        res = self._getSortieRosterType(division)
        if res is None:
            return False
        else:
            self._rosterTypeID, RosterType = res
            self._roster = RosterType()
            LOG_DEBUG_DEV('New roster type: {0} : {1}', self._rosterTypeID,
                          self._roster.__class__)
            self._refreshFreeSlots(prevRoster.MAX_SLOTS,
                                   self._roster.MAX_SLOTS)
            self.storeOp(UNIT_OP.CHANGE_DIVISION, division)
            return True

    def _getFalloutRosterType(self, queueType):
        prevRosterTypeID = self._rosterTypeID
        newRosterTypeID = FALLOUT_QUEUE_TYPE_TO_ROSTER.get(queueType, None)
        if newRosterTypeID is None:
            LOG_DEBUG_DEV('Wrong fallout queue type={}.', queueType)
            return
        elif newRosterTypeID == prevRosterTypeID:
            LOG_DEBUG_DEV('Queue type has not changed.')
            return
        RosterType = ROSTER_TYPE_TO_CLASS.get(newRosterTypeID, None)
        if RosterType is None:
            LOG_DEBUG_DEV('Wrong RosterTypeID={}', newRosterTypeID)
            return
        else:
            return (newRosterTypeID, RosterType)

    def _changeFalloutQueueType(self, queueType):
        prevRosterTypeID = self._rosterTypeID
        prevRoster = self._roster
        LOG_DEBUG_DEV('Previous roster type: {0} : {1}', prevRosterTypeID,
                      prevRoster.__class__)
        res = self._getFalloutRosterType(queueType)
        if res is None:
            return False
        else:
            self._rosterTypeID, RosterType = res
            self._roster = RosterType()
            LOG_DEBUG_DEV('New roster type: {0} : {1}', self._rosterTypeID,
                          self._roster.__class__)
            self._refreshFreeSlots(prevRoster.MAX_SLOTS,
                                   self._roster.MAX_SLOTS)
            self.storeOp(UNIT_OP.CHANGE_FALLOUT_TYPE, queueType)
            return True

    def _getLeaderDBID(self):
        return self._members.get(LEADER_SLOT, {}).get('accountDBID', 0)

    def isRatedBattle(self):
        return bool(self._extras.get('isRatedBattle'))

    def isMultiVehicle(self):
        return self._roster.MAX_VEHICLES > 1

    def getRosterType(self):
        return self._rosterTypeID

    def _checkAllVehiclesMatchSlot(self, accountDBID, unitSlotIdx):
        vehList = self._vehicles.get(accountDBID, [])
        for veh in vehList:
            res, slotChosenIdx = self._roster.checkVehicle(
                veh.vehTypeCompDescr, unitSlotIdx)
            if not res:
                return False

        return True