예제 #1
0
class AccountSyncData(object):
    def __init__(self):
        self.revision = 0
        self.__account = None
        self.__syncController = None
        self.__ignore = True
        self.__isSynchronized = False
        self.__syncID = 0
        self.__subscribers = []
        self.__isFirstSync = True
        self.__persistentCache = SimpleCache('account_caches', 'data')
        self.__persistentCache.data = None
        self.__persistentCache.isDirty = False
        return

    def onAccountBecomePlayer(self):
        self.__ignore = False
        self.__isFirstSync = True
        self._synchronize()

    def onAccountBecomeNonPlayer(self):
        self.__ignore = True
        self.__isSynchronized = False

    def setAccount(self, account):
        self.__account = account
        if self.__syncController is not None:
            self.__syncController.destroy()
            self.__syncController = None
        self.__savePersistentCache()
        if account is not None:
            oldName = self.__persistentCache.getAccountName()
            raise oldName is None or oldName == account.name or AssertionError
            self.__persistentCache.setAccountName(account.name)
            self.__syncController = SyncController(account,
                                                   self.__sendSyncRequest,
                                                   self.__onSyncResponse,
                                                   self.__onSyncComplete)
        return

    def waitForSync(self, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER)
            return
        elif self.__isSynchronized:
            callback(AccountCommands.RES_CACHE)
            return
        else:
            if callback is not None:
                self.__subscribers.append(callback)
            return

    def updatePersistentCache(self, ext, isFullSync):
        if ext.pop('__cache', None) is not None:
            if not self.__persistentCache.data:
                desc, cacheData = self.__persistentCache.data = self.__persistentCache.get(
                )
                if accountDataPersistentHash(cacheData) != desc:
                    LOG_ERROR('Local account data cache is corrupted: resync')
                    self._resynchronize()
                    return False
                self.__persistentCache.data = cacheData
                self.__persistentCache.isDirty = False
            else:
                cacheData = self.__persistentCache.data
            if cacheData is None:
                LOG_ERROR(
                    "Incorrect cache state while syncing data: server said to use cache but I don't have any"
                )
                self._resynchronize()
                return False
            accountDataMergePersistent(ext, cacheData)
            if synchronizeDicts(accountDataGetDiffForPersistent(ext),
                                self.__persistentCache.data)[1]:
                self.__persistentCache.isDirty = True
        else:
            if self.__persistentCache.data is None:
                self.__persistentCache.data = {}
            synchronizeDicts(accountDataGetDiffForPersistent(ext),
                             self.__persistentCache.data)
            self.__persistentCache.isDirty = True
        return True

    def _synchronize(self):
        if self.__ignore:
            return
        elif self.__isSynchronized:
            return
        else:
            self.__syncController.request(self.__getNextSyncID(), None)
            return

    def _resynchronize(self):
        LOG_DEBUG('resynchronize')
        if self.__ignore:
            return
        else:
            self.__isSynchronized = False
            self.revision = 0
            self.__clearPersistentCache()
            self.__syncController.request(self.__getNextSyncID(), None)
            return

    def __onSyncResponse(self, syncID, resultID, ext={}):
        if resultID == AccountCommands.RES_NON_PLAYER:
            return
        if syncID != self.__syncID:
            return
        if resultID < 0:
            LOG_ERROR('Data synchronization failed.')
            self._resynchronize()
            return
        if self.revision != ext.get('prevRev', self.revision):
            LOG_ERROR('Incorrect diff received', self.revision, ext['prevRev'])
            self._resynchronize()
            return
        self.revision = ext.get('rev', self.revision)
        self.__isSynchronized = True
        if not self.__account._update(not self.__isFirstSync, ext):
            return
        self.__isFirstSync = False
        subscribers = self.__subscribers
        self.__subscribers = []
        for callback in subscribers:
            callback(resultID)

    def __onSyncComplete(self, syncID, data):
        if syncID != self.__syncID:
            return
        elif data is None:
            return
        else:
            self.revision = data['rev']
            if not self.__account._update(False, data):
                return
            self._synchronize()
            return

    def __getNextSyncID(self):
        self.__syncID += 1
        if self.__syncID > 30000:
            self.__syncID = 1
        return self.__syncID

    def __sendSyncRequest(self, id, proxy):
        if self.__ignore:
            return
        crc = self.__persistentCache.getDescr()
        self.__account._doCmdInt3(AccountCommands.CMD_SYNC_DATA, self.revision,
                                  0 if not crc else crc, 0, proxy)

    def __clearPersistentCache(self):
        self.__persistentCache.data = None
        self.__persistentCache.isDirty = False
        self.__persistentCache.clear()
        return

    def __savePersistentCache(self):
        if self.__persistentCache.isDirty and self.__persistentCache.data:
            self.__persistentCache.data = accountDataExtractPersistent(
                self.__persistentCache.data)
            self.__persistentCache.save(
                accountDataPersistentHash(self.__persistentCache.data),
                self.__persistentCache.data)
            self.__persistentCache.isDirty = False
예제 #2
0
class Shop(object):

    def __init__(self):
        self.__account = None
        self.__syncController = None
        self.__cache = {}
        self.__persistentCache = SimpleCache('account_caches', 'shop')
        self.__ignore = True
        self.__isSynchronizing = False
        self.__syncID = 0
        self.__isFirstSync = True
        return

    def onAccountBecomePlayer(self):
        self.__ignore = False
        self.__isFirstSync = True
        self.synchronize()

    def onAccountBecomeNonPlayer(self):
        self.__ignore = True
        self.__isSynchronizing = False

    def setAccount(self, account):
        self.__account = account
        self.__persistentCache.setAccount(account)
        if self.__syncController is not None:
            self.__syncController.destroy()
            self.__syncController = None
        if account is not None:
            self.__syncController = SyncController(account, self.__sendSyncRequest, self.__onSyncResponse, self.__onSyncComplete)
        return

    def synchronize(self, serverCacheRev = None):
        LOG_DEBUG('Shop.synchronize: cli_rev=%s, serv_rev=%s' % (self.__getCacheRevision(), serverCacheRev))
        if self.__ignore:
            return
        elif self.__getCacheRevision() == serverCacheRev:
            return
        elif self.__isSynchronizing:
            return
        else:
            self.__isSynchronizing = True
            if not self.__isFirstSync:
                events.onShopResyncStarted()
            self.__syncController.request(self.__getNextSyncID(), None)
            return

    def resynchronize(self):
        LOG_DEBUG('resynchronize')
        if self.__ignore:
            return
        else:
            self.__cache.clear()
            self.__persistentCache.clear()
            self.__isSynchronizing = True
            self.__isFirstSync = False
            events.onShopResyncStarted()
            self.__syncController.request(self.__getNextSyncID(), None)
            return

    def waitForSync(self, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER, None)
            return
        elif not self.__isSynchronizing:
            callback(AccountCommands.RES_CACHE, self.__getCacheRevision())
            return
        else:
            proxy = lambda resultID, data: callback(resultID, self.__getCacheRevision())
            self.__syncController.request(self.__syncID, proxy)
            return

    def getCache(self, callback):
        self.__getValue(None, callback)
        return

    def getAllItems(self, callback):
        self.__getValue('items', callback)

    def getSellPriceModifiers(self, compDescr, callback):
        proxy = lambda resultID, _data, rev: self.__onGetSellPriceModifiers(resultID, compDescr, callback)
        self.__getValue('sellPriceModif', proxy)

    def getSellPrice(self, buyPrice, sellPriceModifiers, itemTypeID):
        shopRev, exchangeRate, exchangeRateForShellsAndEqs, sellPriceModif, sellPriceFactor, sellForGold = sellPriceModifiers
        if not shopRev == self.__getCacheRevision():
            raise AssertionError
            if itemTypeID in (_SHELL, _EQUIPMENT):
                exchangeRate = exchangeRateForShellsAndEqs
            sellPrice = sellForGold and (int(ceil(sellPriceFactor * buyPrice[0])), int(ceil(sellPriceFactor * buyPrice[1])))
        else:
            sellPrice = (int(ceil(sellPriceFactor * (buyPrice[0] + buyPrice[1] * exchangeRate))), 0)
        return sellPrice

    def getPrice(self, typeCompDescr, callback):
        proxy = lambda resultID, items, rev: self.__onGetPriceResponse(resultID, typeCompDescr, callback)
        self.__getValue('items', proxy)

    def getRentPackets(self, typeCompDescr, callback):
        proxy = lambda resultID, packets, rev: self.__onGetRentPacketsResponse(resultID, typeCompDescr, callback)
        self.__getValue('items', proxy)

    def getVehiclePrice(self, vehCompDescr, callback):
        proxy = lambda resultID, items, rev: self.__onGetVehiclePriceResponse(resultID, vehCompDescr, False, callback)
        self.__getValue('items', proxy)

    def getVehicleSellPrice(self, vehCompDescr, callback):
        proxy = lambda resultID, items, rev: self.__onGetVehiclePriceResponse(resultID, vehCompDescr, True, callback)
        self.__getValue('items', proxy)

    def getVehiclesSellPrices(self, vehCompDescrs, callback):
        proxy = lambda resultID, items, rev: self.__onGetVehiclesSellPriceResponse(resultID, vehCompDescrs, callback)
        self.__getValue('items', proxy)

    def getComponentPrice(self, compDescr, callback):
        proxy = lambda resultID, items, rev: self.__onGetComponentPriceResponse(resultID, compDescr, False, callback)
        self.__getValue('items', proxy)

    def getComponentSellPrice(self, compDescr, callback):
        proxy = lambda resultID, items, rev: self.__onGetComponentPriceResponse(resultID, compDescr, True, callback)
        self.__getValue('items', proxy)

    def getComponentsSellPrice(self, compDescrs, callback):
        proxy = lambda resultID, items, rev: self.__onGetComponentsPriceResponse(resultID, compDescrs, callback)
        self.__getValue('items', proxy)

    def getAmmoSellPrice(self, ammo, callback):
        proxy = lambda resultID, items, rev: self.__onGetAmmoSellPriceResponse(resultID, ammo, callback)
        self.__getValue('items', proxy)

    def getDailyXPFactor(self, callback):
        self.__getValue('dailyXPFactor', callback)

    def getSlotsPrices(self, callback):
        self.__getValue('slotsPrices', callback)

    def getNextSlotPrice(self, slots, slotsPrices):
        addSlotNumber = slots - slotsPrices[0]
        if addSlotNumber < 0:
            return 0
        if addSlotNumber < len(slotsPrices[1]):
            return slotsPrices[1][addSlotNumber]
        return slotsPrices[1][-1]

    def getBerthsPrices(self, callback):
        self.__getValue('berthsPrices', callback)

    def getNextBerthPackPrice(self, berths, berthsPrices):
        addPackNumber = (berths - berthsPrices[0]) / berthsPrices[1]
        if addPackNumber < 0:
            return 0
        if addPackNumber < len(berthsPrices[2]):
            return berthsPrices[2][addPackNumber]
        return berthsPrices[2][-1]

    def getExchangeRate(self, callback):
        self.__getValue('exchangeRate', callback)

    def getExchangeRateForShellsAndEqs(self, callback):
        self.__getValue('exchangeRateForShellsAndEqs', callback)

    def isEnabledBuyingGoldShellsForCredits(self, callback):
        self.__getValue('isEnabledBuyingGoldShellsForCredits', callback)

    def isEnabledBuyingGoldEqsForCredits(self, callback):
        self.__getValue('isEnabledBuyingGoldEqsForCredits', callback)

    def getFreeXPToTManXPRate(self, callback):
        self.__getValue('freeXPToTManXPRate', callback)

    def getFreeXPConversion(self, callback):
        self.__getValue('freeXPConversion', callback)

    def getPremiumCost(self, callback):
        self.__getValue('premiumCost', callback)

    def getTankmanCost(self, callback):
        self.__getValue('tankmanCost', callback)

    def getChangeRoleCost(self, callback):
        self.__getValue('changeRoleCost', callback)

    def getDropSkillsCost(self, callback):
        self.__getValue('dropSkillsCost', callback)

    def getPassportChangeCost(self, callback):
        self.__getValue('passportChangeCost', callback)

    def getFemalePassportChangeCost(self, callback):
        self.__getValue('femalePassportChangeCost', callback)

    def getPaidRemovalCost(self, callback):
        self.__getValue('paidRemovalCost', callback)

    def getCamouflageCost(self, callback):
        self.__getValue('camouflageCost', callback)

    def getPlayerEmblemCost(self, callback):
        self.__getValue('playerEmblemCost', callback)

    def getPlayerInscriptionCost(self, callback):
        self.__getValue('playerInscriptionCost', callback)

    def getHornCost(self, callback):
        self.__getValue('hornCost', callback)

    def buy(self, itemTypeIdx, nationIdx, itemShopID, count, goldForCredits, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER, {})
            return
        elif itemTypeIdx == _VEHICLE:
            self.buyVehicle(nationIdx, itemShopID, False, True, 0, -1, callback)
            return
        else:
            count = int(round(count))
            if callback is not None:
                proxy = lambda requestID, resultID, errorStr, ext = {}: callback(resultID)
            else:
                proxy = None
            self.__account._doCmdInt4(AccountCommands.CMD_BUY_ITEM, self.__getCacheRevision(), itemShopID, count, goldForCredits, proxy)
            return

    def buyAndEquipItem(self, vehInvID, compDescr, slotIdx, isPaidRemoval, gunCompDescr, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER, '', {})
            return
        else:
            if callback is not None:
                proxy = lambda requestID, resultID, errorStr, ext = {}: callback(resultID, errorStr, ext)
            else:
                proxy = None
            arr = [self.__getCacheRevision(),
             compDescr,
             vehInvID,
             slotIdx,
             isPaidRemoval,
             gunCompDescr]
            self.__account._doCmdIntArr(AccountCommands.CMD_BUY_AND_EQUIP_ITEM, arr, proxy)
            return

    def buyAndEquipTankman(self, vehInvID, slot, tmanCostTypeIdx, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER, '', {})
            return
        else:
            if callback is not None:
                proxy = lambda requestID, resultID, errorStr, ext = {}: callback(resultID, errorStr, ext)
            else:
                proxy = None
            self.__account._doCmdInt4(AccountCommands.CMD_BUY_AND_EQUIP_TMAN, self.__getCacheRevision(), vehInvID, slot, tmanCostTypeIdx, proxy)
            return

    def buyVehicle(self, nationIdx, innationIdx, buyShells, recruitCrew, tmanCostTypeIdx, rentPeriod, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER, {})
            return
        else:
            typeCompDescr = vehicles.makeIntCompactDescrByID('vehicle', nationIdx, innationIdx)
            flags = BUY_VEHICLE_FLAG.NONE
            if buyShells:
                flags |= BUY_VEHICLE_FLAG.SHELLS
            if recruitCrew:
                flags |= BUY_VEHICLE_FLAG.CREW
            if callback is not None:
                proxy = lambda requestID, resultID, errorStr, ext = {}: callback(resultID)
            else:
                proxy = None
            arr = [self.__getCacheRevision(),
             typeCompDescr,
             flags,
             tmanCostTypeIdx,
             rentPeriod]
            self.__account._doCmdIntArr(AccountCommands.CMD_BUY_VEHICLE, arr, proxy)
            return

    def buyTankman(self, nationIdx, innationIdx, role, tmanCostTypeIdx, callback):
        vehTypeCompDescr = vehicles.makeIntCompactDescrByID('vehicle', nationIdx, innationIdx)
        roleIdx = tankmen.SKILL_INDICES[role]
        if callback is not None:
            proxy = lambda requestID, resultID, errorStr, ext = {}: callback(resultID, ext.get('tmanInvID', None), ext.get('tmanCompDescr', None))
        else:
            proxy = None
        self.__account._doCmdInt4(AccountCommands.CMD_BUY_TMAN, self.__getCacheRevision(), vehTypeCompDescr, roleIdx, tmanCostTypeIdx, proxy)
        return

    def buyGoodie(self, goodieID, count, forGold, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER, {})
            return
        else:
            count = int(round(count))
            if callback is not None:
                proxy = lambda requestID, resultID, errorStr, ext = {}: callback(resultID)
            else:
                proxy = None
            self.__account._doCmdInt4(AccountCommands.CMD_BUY_GOODIE, self.__getCacheRevision(), goodieID, count, forGold, proxy)
            return

    def __onSyncResponse(self, syncID, resultID, ext = {}):
        if resultID == AccountCommands.RES_NON_PLAYER:
            return
        if syncID != self.__syncID:
            return
        if resultID < 0:
            LOG_ERROR('Shop synchronization failed. Repeating')
            self.resynchronize()
            return
        if resultID == AccountCommands.RES_CACHE:
            try:
                data = cPickle.loads(zlib.decompress(self.__persistentCache.getData()))
            except Exception:
                self.resynchronize()
                return

            self.__onSyncDataReceived(data)
        elif resultID == AccountCommands.RES_SUCCESS:
            if self.__isFirstSync:
                self.__isFirstSync = False
            else:
                events.onShopResync()
        self.__isSynchronizing = False

    def __onSyncComplete(self, syncID, data):
        if syncID != self.__syncID:
            return
        elif data is None:
            return
        else:
            streamData = self.__account.lastStreamData
            self.__persistentCache.save((streamData.origPacketLen, streamData.origCrc32), streamData.data)
            self.__onSyncDataReceived(data)
            return

    def __onSyncDataReceived(self, data):
        data['sellPriceModif'] = data['sellPriceFactor']
        self.__cache = data
        self.__isSynchronizing = False
        if self.__isFirstSync:
            self.__isFirstSync = False
        else:
            events.onShopResync()

    def __onGetItemsResponse(self, resultID, itemTypeIdx, nationIdx, callback):
        if resultID < 0:
            items = None
        else:
            items = self.__cache.get('items', {}).get(nationIdx, {}).get(itemTypeIdx, None)
        if callback is not None:
            callback(resultID, items, self.__getCacheRevision())
        return

    def __onGetValueResponse(self, resultID, key, callback):
        if resultID < 0:
            if callback is not None:
                callback(resultID, None, self.__getCacheRevision())
            return
        elif self.__isSynchronizing:
            self.__getValue(key, callback)
            return
        else:
            value = self.__cache if key is None else self.__cache.get(key, None)
            if callback is not None:
                callback(resultID, value, self.__getCacheRevision())
            return

    def __onGetPriceResponse(self, resultID, typeCompDescr, callback):
        if resultID < 0:
            price = None
        else:
            price = self.__getPriceFromCache(typeCompDescr)
        if callback is not None:
            callback(resultID, price, self.__getCacheRevision())
        return

    def __onGetRentPacketsResponse(self, resultID, typeCompDescr, callback):
        if resultID < 0:
            packets = None
        else:
            packets = self.__getRentPacketsFromCache(typeCompDescr)
        if callback is not None:
            callback(resultID, packets, self.__getCacheRevision())
        return

    def __onGetVehiclePriceResponse(self, resultID, vehCompDescr, isSellPrice, callback):
        if resultID < 0:
            if callback is not None:
                callback(resultID, None, self.__getCacheRevision())
            return
        else:
            price = self.__getVehiclePriceFromCache(vehCompDescr, None)
            if isSellPrice and price is not None:
                typeCompDescr = vehicles.getVehicleTypeCompactDescr(vehCompDescr)
                price = self.getSellPrice(price, self.__getSellPriceModifiersFromCache(typeCompDescr), _VEHICLE)
            if callback is not None:
                callback(resultID, price, self.__getCacheRevision())
            return

    def __onGetVehiclesSellPriceResponse(self, resultID, vehCompDescrs, callback):
        if resultID < 0:
            if callback is not None:
                callback(resultID, None, self.__getCacheRevision())
            return
        else:
            prices = []
            for vehCompDescr in vehCompDescrs:
                price = self.__getVehiclePriceFromCache(vehCompDescr, None)
                if price is None:
                    prices = None
                    break
                prices.append(self.getSellPrice(price, self.__getSellPriceModifiersFromCache(vehCompDescr), _VEHICLE))

            if callback is not None:
                callback(resultID, prices, self.__getCacheRevision())
            return

    def __onGetComponentPriceResponse(self, resultID, compDescr, isSellPrice, callback):
        if resultID < 0:
            if callback is not None:
                callback(resultID, None, self.__getCacheRevision())
            return
        else:
            itemTypeIdx, _, _ = vehicles.parseIntCompactDescr(compDescr)
            price = self.__getPriceFromCache(compDescr)
            if isSellPrice:
                price = self.getSellPrice(price, self.__getSellPriceModifiersFromCache(compDescr), itemTypeIdx)
            if callback is not None:
                callback(resultID, price, self.__getCacheRevision())
            return

    def __onGetComponentsPriceResponse(self, resultID, compDescrs, callback):
        if resultID < 0:
            if callback is not None:
                callback(resultID, None, self.__getCacheRevision())
            return
        else:
            prices = []
            for compDescr in compDescrs:
                itemTypeIdx, _, _ = vehicles.parseIntCompactDescr(compDescr)
                if itemTypeIdx == _VEHICLE:
                    continue
                price = self.__getPriceFromCache(compDescr, None)
                if price is None:
                    prices = None
                    break
                prices.append(self.getSellPrice(price, self.__getSellPriceModifiersFromCache(compDescr), itemTypeIdx))

            if callback is not None:
                callback(resultID, prices, self.__getCacheRevision())
            return

    def __onGetAmmoSellPriceResponse(self, resultID, ammo, callback):
        if resultID < 0:
            if callback is not None:
                callback(resultID, None, self.__getCacheRevision())
            return
        else:
            price = 0
            for shellCompDescr, count in AmmoIterator(ammo):
                if count == 0:
                    continue
                shellPrice = self.__getPriceFromCache(shellCompDescr)
                shellSellPrice = self.getSellPrice(shellPrice, self.__getSellPriceModifiersFromCache(shellCompDescr), _SHELL)
                price += shellSellPrice * count

            if callback is not None:
                callback(resultID, price, self.__getCacheRevision())
            return

    def __onGetSellPriceModifiers(self, resultID, compDescr, callback):
        callback(resultID, self.__getSellPriceModifiersFromCache(compDescr))

    def __getNextSyncID(self):
        self.__syncID += 1
        if self.__syncID > 30000:
            self.__syncID = 1
        return self.__syncID

    def __sendSyncRequest(self, syncID, proxy):
        if self.__ignore:
            return
        clientRev = self.__getCacheRevision()
        descr = self.__persistentCache.getDescr()
        dataLen, dataCrc = descr if descr else (0, 0)
        self.__account._doCmdInt3(AccountCommands.CMD_SYNC_SHOP, clientRev, dataLen, dataCrc, proxy)

    def __getCacheRevision(self):
        return self.__cache.get('rev', 0)

    def __getPriceFromCache(self, typeCompDescr, default = (0, 0)):
        vehPrices = self.__cache.get('items', {}).get('itemPrices', {})
        return vehPrices.get(typeCompDescr, default)

    def __getRentPacketsFromCache(self, vehTypeCompDescr):
        packets = self.__cache.get('items', {}).get('vehiclesRentPrices', {})
        return packets.get(vehTypeCompDescr, {})

    def __getVehiclePriceFromCache(self, vehCompDescr, default = (0, 0)):
        typeCompDescr = vehicles.getVehicleTypeCompactDescr(vehCompDescr)
        price = self.__getPriceFromCache(typeCompDescr, None)
        if price is None:
            return default
        else:
            vehDescr = vehicles.VehicleDescr(compactDescr=vehCompDescr)
            devices = vehDescr.getDevices()
            for defCompDescr, instCompDescr in izip(devices[0], devices[1]):
                if defCompDescr == instCompDescr:
                    continue
                compPrice = self.__getPriceFromCache(defCompDescr, None)
                if compPrice is None:
                    return default
                price = _subtractPrices(price, compPrice)
                compPrice = self.__getPriceFromCache(instCompDescr, None)
                if compPrice is None:
                    return default
                price = _summPrices(price, compPrice)

            for optDevCompDescr in devices[2]:
                compPrice = self.__getPriceFromCache(optDevCompDescr, None)
                if compPrice is None:
                    return default
                price = _summPrices(price, compPrice)

            return price

    def __getSellPriceModifiersFromCache(self, typeCompDescr):
        cache = self.__cache
        items = cache.get('items', {})
        sellPriceModif = cache.get('sellPriceModif', 0)
        sellPriceFactors = items.get('vehicleSellPriceFactors', {})
        sellForGold = items.get('vehiclesToSellForGold', {})
        return (self.__getCacheRevision(),
         cache.get('exchangeRate', 0),
         cache.get('exchangeRateForShellsAndEqs', 0),
         sellPriceModif,
         sellPriceFactors.get(typeCompDescr, sellPriceModif),
         typeCompDescr in sellForGold)

    def __getValue(self, key, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER, None, self.__getCacheRevision())
            return
        elif not self.__isSynchronizing:
            self.__onGetValueResponse(AccountCommands.RES_CACHE, key, callback)
            return
        else:
            proxy = lambda resultID, data: self.__onGetValueResponse(resultID, key, callback)
            self.__syncController.request(self.__syncID, proxy)
            return
예제 #3
0
class AccountSyncData(object):

    def __init__(self):
        self.revision = 0
        self.__account = None
        self.__syncController = None
        self.__ignore = True
        self.__isSynchronized = False
        self.__syncID = 0
        self.__subscribers = []
        self.__isFirstSync = True
        self.__persistentCache = SimpleCache('account_caches', 'data')
        self.__persistentCache.data = None
        self.__persistentCache.isDirty = False
        return

    def onAccountBecomePlayer(self):
        self.__ignore = False
        self.__isFirstSync = True
        self._synchronize()

    def onAccountBecomeNonPlayer(self):
        self.__ignore = True
        self.__isSynchronized = False

    def setAccount(self, account):
        self.__account = account
        if self.__syncController is not None:
            self.__syncController.destroy()
            self.__syncController = None
        self.__savePersistentCache()
        if account is not None:
            oldName = self.__persistentCache.getAccountName()
            raise oldName is None or oldName == account.name or AssertionError
            self.__persistentCache.setAccountName(account.name)
            self.__syncController = SyncController(account, self.__sendSyncRequest, self.__onSyncResponse, self.__onSyncComplete)
        return

    def waitForSync(self, callback):
        if self.__ignore:
            if callback is not None:
                callback(AccountCommands.RES_NON_PLAYER)
            return
        elif self.__isSynchronized:
            callback(AccountCommands.RES_CACHE)
            return
        else:
            if callback is not None:
                self.__subscribers.append(callback)
            return

    def updatePersistentCache(self, ext, isFullSync):
        if ext.pop('__cache', None) is not None:
            if not self.__persistentCache.data:
                desc, cacheData = self.__persistentCache.data = self.__persistentCache.get()
                if accountDataPersistentHash(cacheData) != desc:
                    LOG_ERROR('Local account data cache is corrupted: resync')
                    self._resynchronize()
                    return False
                self.__persistentCache.data = cacheData
                self.__persistentCache.isDirty = False
            else:
                cacheData = self.__persistentCache.data
            if cacheData is None:
                LOG_ERROR("Incorrect cache state while syncing data: server said to use cache but I don't have any")
                self._resynchronize()
                return False
            accountDataMergePersistent(ext, cacheData)
            if synchronizeDicts(accountDataGetDiffForPersistent(ext), self.__persistentCache.data)[1]:
                self.__persistentCache.isDirty = True
        else:
            if self.__persistentCache.data is None:
                self.__persistentCache.data = {}
            synchronizeDicts(accountDataGetDiffForPersistent(ext), self.__persistentCache.data)
            self.__persistentCache.isDirty = True
        return True

    def _synchronize(self):
        if self.__ignore:
            return
        elif self.__isSynchronized:
            return
        else:
            self.__syncController.request(self.__getNextSyncID(), None)
            return

    def _resynchronize(self):
        LOG_MX('resynchronize')
        if self.__ignore:
            return
        else:
            self.__isSynchronized = False
            self.revision = 0
            self.__clearPersistentCache()
            self.__syncController.request(self.__getNextSyncID(), None)
            return

    def __onSyncResponse(self, syncID, resultID, ext = {}):
        if resultID == AccountCommands.RES_NON_PLAYER:
            return
        if syncID != self.__syncID:
            return
        if resultID < 0:
            LOG_ERROR('Data synchronization failed.')
            self._resynchronize()
            return
        if self.revision != ext.get('prevRev', self.revision):
            LOG_ERROR('Incorrect diff received', self.revision, ext['prevRev'])
            self._resynchronize()
            return
        self.revision = ext.get('rev', self.revision)
        self.__isSynchronized = True
        if not self.__account._update(not self.__isFirstSync, ext):
            return
        self.__isFirstSync = False
        subscribers = self.__subscribers
        self.__subscribers = []
        for callback in subscribers:
            callback(resultID)

    def __onSyncComplete(self, syncID, data):
        if syncID != self.__syncID:
            return
        elif data is None:
            return
        else:
            self.revision = data['rev']
            if not self.__account._update(False, data):
                return
            self._synchronize()
            return

    def __getNextSyncID(self):
        self.__syncID += 1
        if self.__syncID > 30000:
            self.__syncID = 1
        return self.__syncID

    def __sendSyncRequest(self, id, proxy):
        if self.__ignore:
            return
        crc = self.__persistentCache.getDescr()
        self.__account._doCmdInt3(AccountCommands.CMD_SYNC_DATA, self.revision, 0 if not crc else crc, 0, proxy)

    def __clearPersistentCache(self):
        self.__persistentCache.data = None
        self.__persistentCache.isDirty = False
        self.__persistentCache.clear()
        return

    def __savePersistentCache(self):
        if self.__persistentCache.isDirty and self.__persistentCache.data:
            self.__persistentCache.data = accountDataExtractPersistent(self.__persistentCache.data)
            self.__persistentCache.save(accountDataPersistentHash(self.__persistentCache.data), self.__persistentCache.data)
            self.__persistentCache.isDirty = False