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
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
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
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