mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-25 12:42:41 -06:00
1864 lines
83 KiB
Python
1864 lines
83 KiB
Python
import sys
|
|
import os
|
|
import time
|
|
import string
|
|
import __builtin__
|
|
from panda3d.core import *
|
|
from direct.showbase.MessengerGlobal import *
|
|
from direct.showbase.DirectObject import DirectObject
|
|
from direct.showbase.EventManagerGlobal import *
|
|
from direct.task.MiniTask import MiniTask, MiniTaskManager
|
|
from direct.directnotify.DirectNotifyGlobal import *
|
|
|
|
class LogAndOutput:
|
|
|
|
def __init__(self, orig, log):
|
|
self.orig = orig
|
|
self.log = log
|
|
self.console = False
|
|
|
|
def write(self, str):
|
|
self.log.write(str)
|
|
self.log.flush()
|
|
if self.console:
|
|
self.orig.write(str)
|
|
self.orig.flush()
|
|
|
|
def flush(self):
|
|
self.log.flush()
|
|
self.orig.flush()
|
|
|
|
|
|
class LauncherBase(DirectObject):
|
|
GameName = 'game'
|
|
ArgCount = 6
|
|
LauncherPhases = [1,
|
|
2,
|
|
3,
|
|
4]
|
|
TmpOverallMap = [0.25,
|
|
0.25,
|
|
0.25,
|
|
0.25]
|
|
BANDWIDTH_ARRAY = [1800,
|
|
3600,
|
|
4200,
|
|
6600,
|
|
8000,
|
|
12000,
|
|
16000,
|
|
24000,
|
|
32000,
|
|
48000,
|
|
72000,
|
|
96000,
|
|
128000,
|
|
192000,
|
|
250000,
|
|
500000,
|
|
750000,
|
|
1000000,
|
|
1250000,
|
|
1500000,
|
|
1750000,
|
|
2000000,
|
|
3000000,
|
|
4000000,
|
|
6000000,
|
|
8000000,
|
|
10000000,
|
|
12000000,
|
|
14000000,
|
|
16000000,
|
|
24000000,
|
|
32000000,
|
|
48000000,
|
|
64000000,
|
|
96000000,
|
|
128000000,
|
|
256000000,
|
|
512000000,
|
|
1024000000]
|
|
win32con_FILE_PERSISTENT_ACLS = 8
|
|
InstallDirKey = 'INSTALL_DIR'
|
|
GameLogFilenameKey = 'GAMELOG_FILENAME'
|
|
PandaWindowOpenKey = 'PANDA_WINDOW_OPEN'
|
|
PandaErrorCodeKey = 'PANDA_ERROR_CODE'
|
|
NewInstallationKey = 'IS_NEW_INSTALLATION'
|
|
LastLoginKey = 'LAST_LOGIN'
|
|
UserLoggedInKey = 'USER_LOGGED_IN'
|
|
PaidUserLoggedInKey = 'PAID_USER_LOGGED_IN'
|
|
ReferrerKey = 'REFERRER_CODE'
|
|
PeriodTimeRemainingKey = 'PERIOD_TIME_REMAINING'
|
|
PeriodNameKey = 'PERIOD_NAME'
|
|
SwidKey = 'SWID'
|
|
PatchCDKey = 'FROM_CD'
|
|
DISLTokenKey = 'DISLTOKEN'
|
|
ProxyServerKey = 'PROXY_SERVER'
|
|
ProxyDirectHostsKey = 'PROXY_DIRECT_HOSTS'
|
|
launcherFileDbFilename = 'launcherFileDb'
|
|
webLauncherFlag = False
|
|
|
|
def __init__(self):
|
|
self.started = False
|
|
self.taskMgrStarted = False
|
|
self._downloadComplete = False
|
|
self.pandaErrorCode = 0
|
|
self.WIN32 = os.name == 'nt'
|
|
if self.WIN32:
|
|
self.VISTA = sys.getwindowsversion()[3] == 2 and sys.getwindowsversion()[0] == 6
|
|
else:
|
|
self.VISTA = 0
|
|
ltime = time.localtime()
|
|
logSuffix = '%02d%02d%02d_%02d%02d%02d' % (ltime[0] - 2000,
|
|
ltime[1],
|
|
ltime[2],
|
|
ltime[3],
|
|
ltime[4],
|
|
ltime[5])
|
|
logPrefix = ''
|
|
if not self.WIN32:
|
|
logPrefix = os.environ.get('LOGFILE_PREFIX', '')
|
|
logfile = logPrefix + self.getLogFileName() + '-' + logSuffix + '.log'
|
|
self.errorfile = 'errorCode'
|
|
log = open(logfile, 'a')
|
|
logOut = LogAndOutput(sys.__stdout__, log)
|
|
logErr = LogAndOutput(sys.__stderr__, log)
|
|
sys.stdout = logOut
|
|
sys.stderr = logErr
|
|
if sys.platform == 'darwin':
|
|
os.system('/usr/sbin/system_profiler >>' + logfile)
|
|
elif sys.platform == 'linux2':
|
|
os.system('cat /proc/meminfo >>' + logfile)
|
|
os.system('/sbin/ifconfig -a >>' + logfile)
|
|
print '\n\nStarting %s...' % self.GameName
|
|
print 'Current time: ' + time.asctime(time.localtime(time.time())) + ' ' + time.tzname[0]
|
|
print 'sys.path = ', sys.path
|
|
print 'sys.argv = ', sys.argv
|
|
if len(sys.argv) >= self.ArgCount:
|
|
Configrc_args = sys.argv[self.ArgCount - 1]
|
|
print "generating configrc using: '" + Configrc_args + "'"
|
|
else:
|
|
Configrc_args = ''
|
|
print 'generating standard configrc'
|
|
os.environ['CONFIG_CONFIG'] = ':_:configdir_.:configpath_:configname_Configrc.exe:configexe_1:configargs_-stdout ' + Configrc_args
|
|
cpMgr = ConfigPageManager.getGlobalPtr()
|
|
cpMgr.reloadImplicitPages()
|
|
launcherConfig = getConfigExpress()
|
|
__builtin__.config = launcherConfig
|
|
if config.GetBool('log-private-info', 0):
|
|
print 'os.environ = ', os.environ
|
|
elif '__COMPAT_LAYER' in os.environ:
|
|
print '__COMPAT_LAYER = %s' % (os.environ['__COMPAT_LAYER'],)
|
|
self.miniTaskMgr = MiniTaskManager()
|
|
self.VerifyFiles = self.getVerifyFiles()
|
|
self.setServerVersion(launcherConfig.GetString('server-version', 'no_version_set'))
|
|
self.ServerVersionSuffix = launcherConfig.GetString('server-version-suffix', '')
|
|
self.UserUpdateDelay = launcherConfig.GetFloat('launcher-user-update-delay', 0.5)
|
|
self.TELEMETRY_BANDWIDTH = launcherConfig.GetInt('launcher-telemetry-bandwidth', 2000)
|
|
self.INCREASE_THRESHOLD = launcherConfig.GetFloat('launcher-increase-threshold', 0.75)
|
|
self.DECREASE_THRESHOLD = launcherConfig.GetFloat('launcher-decrease-threshold', 0.5)
|
|
self.BPS_WINDOW = launcherConfig.GetFloat('launcher-bps-window', 8.0)
|
|
self.DECREASE_BANDWIDTH = launcherConfig.GetBool('launcher-decrease-bandwidth', 1)
|
|
self.MAX_BANDWIDTH = launcherConfig.GetInt('launcher-max-bandwidth', 0)
|
|
self.nout = MultiplexStream()
|
|
Notify.ptr().setOstreamPtr(self.nout, 0)
|
|
self.nout.addFile(Filename(logfile))
|
|
if launcherConfig.GetBool('console-output', 0):
|
|
self.nout.addStandardOutput()
|
|
sys.stdout.console = True
|
|
sys.stderr.console = True
|
|
self.notify = directNotify.newCategory('Launcher')
|
|
self.clock = TrueClock.getGlobalPtr()
|
|
self.logPrefix = logPrefix
|
|
downloadServerString = launcherConfig.GetString('download-server', '')
|
|
if downloadServerString:
|
|
self.notify.info('Overriding downloadServer to %s.' % downloadServerString)
|
|
else:
|
|
downloadServerString = self.getValue('DOWNLOAD_SERVER', '')
|
|
self.notify.info('Download Server List %s' % downloadServerString)
|
|
self.downloadServerList = []
|
|
for name in downloadServerString.split(';'):
|
|
url = URLSpec(name, 1)
|
|
self.downloadServerList.append(url)
|
|
|
|
self.nextDownloadServerIndex = 0
|
|
self.getNextDownloadServer()
|
|
self.gameServer = self.getGameServer()
|
|
self.notify.info('Game Server %s' % self.gameServer)
|
|
self.downloadServerRetries = 3
|
|
self.multifileRetries = 1
|
|
self.curMultifileRetry = 0
|
|
self.downloadServerRetryPause = 1
|
|
self.bandwidthIndex = len(self.BANDWIDTH_ARRAY) - 1
|
|
self.everIncreasedBandwidth = 0
|
|
self.goUserName = ''
|
|
self.downloadPercentage = 90
|
|
self.decompressPercentage = 5
|
|
self.extractPercentage = 4
|
|
self.lastLauncherMsg = None
|
|
self.topDir = Filename.fromOsSpecific(self.getValue(self.InstallDirKey, '.'))
|
|
self.setRegistry(self.GameLogFilenameKey, logfile)
|
|
tmpVal = self.getValue(self.PatchCDKey)
|
|
if tmpVal == None:
|
|
self.fromCD = 0
|
|
else:
|
|
self.fromCD = tmpVal
|
|
self.notify.info('patch directory is ' + `(self.fromCD)`)
|
|
self.dbDir = self.topDir
|
|
self.patchDir = self.topDir
|
|
self.mfDir = self.topDir
|
|
self.contentDir = 'content/'
|
|
self.clientDbFilename = 'client.ddb'
|
|
self.compClientDbFilename = self.clientDbFilename + '.pz'
|
|
self.serverDbFilename = 'server.ddb'
|
|
self.compServerDbFilename = self.serverDbFilename + '.pz'
|
|
self.serverDbFilePath = self.contentDir + self.compServerDbFilename
|
|
self.clientStarterDbFilePath = self.contentDir + self.compClientDbFilename
|
|
self.progressFilename = 'progress'
|
|
self.overallComplete = 0
|
|
self.progressSoFar = 0
|
|
self.patchExtension = 'pch'
|
|
self.scanForHacks()
|
|
self.firstPhase = self.LauncherPhases[0]
|
|
self.finalPhase = self.LauncherPhases[-1]
|
|
self.showPhase = 3.5
|
|
self.numPhases = len(self.LauncherPhases)
|
|
self.phaseComplete = {}
|
|
self.phaseNewDownload = {}
|
|
self.phaseOverallMap = {}
|
|
tmpOverallMap = self.TmpOverallMap
|
|
tmpPhase3Map = [0.001,
|
|
0.996,
|
|
0.0,
|
|
0.0,
|
|
0.003]
|
|
phaseIdx = 0
|
|
for phase in self.LauncherPhases:
|
|
percentPhaseCompleteKey = 'PERCENT_PHASE_COMPLETE_' + `phase`
|
|
self.setRegistry(percentPhaseCompleteKey, 0)
|
|
self.phaseComplete[phase] = 0
|
|
self.phaseNewDownload[phase] = 0
|
|
self.phaseOverallMap[phase] = tmpOverallMap[phaseIdx]
|
|
phaseIdx += 1
|
|
|
|
self.patchList = []
|
|
self.reextractList = []
|
|
self.byteRate = 0
|
|
self.byteRateRequested = 0
|
|
self.resetBytesPerSecond()
|
|
self.dldb = None
|
|
self.currentMfname = None
|
|
self.currentPhaseIndex = 0
|
|
self.currentPhase = self.LauncherPhases[self.currentPhaseIndex]
|
|
self.currentPhaseName = self.Localizer.LauncherPhaseNames[self.currentPhaseIndex]
|
|
if self.getServerVersion() == 'no_version_set':
|
|
self.setPandaErrorCode(10)
|
|
self.notify.info('Aborting, Configrc did not run!')
|
|
sys.exit()
|
|
self.launcherMessage(self.Localizer.LauncherStartingMessage)
|
|
self.http = HTTPClient()
|
|
if self.http.getProxySpec() == '':
|
|
self.http.setProxySpec(self.getValue(self.ProxyServerKey, ''))
|
|
self.http.setDirectHostSpec(self.getValue(self.ProxyDirectHostsKey, ''))
|
|
self.notify.info('Proxy spec is: %s' % self.http.getProxySpec())
|
|
if self.http.getDirectHostSpec() != '':
|
|
self.notify.info('Direct hosts list is: %s' % self.http.getDirectHostSpec())
|
|
self.httpChannel = self.http.makeChannel(0)
|
|
self.httpChannel.setDownloadThrottle(1)
|
|
connOk = 0
|
|
while not connOk:
|
|
proxies = self.http.getProxiesForUrl(self.downloadServer)
|
|
if proxies == 'DIRECT':
|
|
self.notify.info('No proxy for download.')
|
|
else:
|
|
self.notify.info('Download proxy: %s' % proxies)
|
|
testurl = self.addDownloadVersion(self.launcherFileDbFilename)
|
|
connOk = self.httpChannel.getHeader(DocumentSpec(testurl))
|
|
statusCode = self.httpChannel.getStatusCode()
|
|
statusString = self.httpChannel.getStatusString()
|
|
if not connOk:
|
|
self.notify.warning('Could not contact download server at %s' % testurl.cStr())
|
|
self.notify.warning('Status code = %s %s' % (statusCode, statusString))
|
|
if statusCode == 407 or statusCode == 1407 or statusCode == HTTPChannel.SCSocksNoAcceptableLoginMethod:
|
|
self.setPandaErrorCode(3)
|
|
elif statusCode == 404:
|
|
self.setPandaErrorCode(13)
|
|
elif statusCode < 100:
|
|
self.setPandaErrorCode(4)
|
|
elif statusCode > 1000:
|
|
self.setPandaErrorCode(9)
|
|
else:
|
|
self.setPandaErrorCode(6)
|
|
if not self.getNextDownloadServer():
|
|
sys.exit()
|
|
|
|
self.notify.info('Download server: %s' % self.downloadServer.cStr())
|
|
if self.notify.getDebug():
|
|
self.accept('page_up', self.increaseBandwidth)
|
|
self.accept('page_down', self.decreaseBandwidth)
|
|
self.httpChannel.setPersistentConnection(1)
|
|
self.foreground()
|
|
self.prepareClient()
|
|
self.setBandwidth()
|
|
self.downloadLauncherFileDb()
|
|
return
|
|
|
|
def getTime(self):
|
|
return self.clock.getShortTime()
|
|
|
|
def isDummy(self):
|
|
return 0
|
|
|
|
def getNextDownloadServer(self):
|
|
if self.nextDownloadServerIndex >= len(self.downloadServerList):
|
|
self.downloadServer = None
|
|
return 0
|
|
self.downloadServer = self.downloadServerList[self.nextDownloadServerIndex]
|
|
self.notify.info('Using download server %s.' % self.downloadServer.cStr())
|
|
self.nextDownloadServerIndex += 1
|
|
return 1
|
|
|
|
def getProductName(self):
|
|
config = getConfigExpress()
|
|
productName = config.GetString('product-name', '')
|
|
if productName and productName != 'DisneyOnline-US':
|
|
productName = '_%s' % productName
|
|
else:
|
|
productName = ''
|
|
return productName
|
|
|
|
def background(self):
|
|
self.notify.info('background: Launcher now operating in background')
|
|
self.backgrounded = 1
|
|
|
|
def foreground(self):
|
|
self.notify.info('foreground: Launcher now operating in foreground')
|
|
self.backgrounded = 0
|
|
|
|
def setRegistry(self, key, value):
|
|
self.notify.info('DEPRECATED setRegistry: %s = %s' % (key, value))
|
|
|
|
def getRegistry(self, key):
|
|
self.notify.info('DEPRECATED getRegistry: %s' % key)
|
|
return None
|
|
|
|
def handleInitiateFatalError(self, errorCode):
|
|
self.notify.warning('handleInitiateFatalError: ' + errorToText(errorCode))
|
|
sys.exit()
|
|
|
|
def handleDecompressFatalError(self, task, errorCode):
|
|
self.notify.warning('handleDecompressFatalError: ' + errorToText(errorCode))
|
|
self.miniTaskMgr.remove(task)
|
|
self.handleGenericMultifileError()
|
|
|
|
def handleDecompressWriteError(self, task, errorCode):
|
|
self.notify.warning('handleDecompressWriteError: ' + errorToText(errorCode))
|
|
self.miniTaskMgr.remove(task)
|
|
self.handleGenericMultifileError()
|
|
|
|
def handleDecompressZlibError(self, task, errorCode):
|
|
self.notify.warning('handleDecompressZlibError: ' + errorToText(errorCode))
|
|
self.miniTaskMgr.remove(task)
|
|
self.handleGenericMultifileError()
|
|
|
|
def handleExtractFatalError(self, task, errorCode):
|
|
self.notify.warning('handleExtractFatalError: ' + errorToText(errorCode))
|
|
self.miniTaskMgr.remove(task)
|
|
self.handleGenericMultifileError()
|
|
|
|
def handleExtractWriteError(self, task, errorCode):
|
|
self.notify.warning('handleExtractWriteError: ' + errorToText(errorCode))
|
|
self.miniTaskMgr.remove(task)
|
|
self.handleGenericMultifileError()
|
|
|
|
def handlePatchFatalError(self, task, errorCode):
|
|
self.notify.warning('handlePatchFatalError: ' + errorToText(errorCode))
|
|
self.miniTaskMgr.remove(task)
|
|
self.handleGenericMultifileError()
|
|
|
|
def handlePatchWriteError(self, task, errorCode):
|
|
self.notify.warning('handlePatchWriteError: ' + errorToText(errorCode))
|
|
self.miniTaskMgr.remove(task)
|
|
self.handleGenericMultifileError()
|
|
|
|
def handleDownloadFatalError(self, task):
|
|
self.notify.warning('handleDownloadFatalError: status code = %s %s' % (self.httpChannel.getStatusCode(), self.httpChannel.getStatusString()))
|
|
self.miniTaskMgr.remove(task)
|
|
statusCode = self.httpChannel.getStatusCode()
|
|
if statusCode == 404:
|
|
self.setPandaErrorCode(5)
|
|
elif statusCode < 100:
|
|
self.setPandaErrorCode(4)
|
|
else:
|
|
self.setPandaErrorCode(6)
|
|
if not self.getNextDownloadServer():
|
|
sys.exit()
|
|
|
|
def handleDownloadWriteError(self, task):
|
|
self.notify.warning('handleDownloadWriteError.')
|
|
self.miniTaskMgr.remove(task)
|
|
self.setPandaErrorCode(2)
|
|
sys.exit()
|
|
|
|
def handleGenericMultifileError(self):
|
|
if not self.currentMfname:
|
|
sys.exit()
|
|
if self.curMultifileRetry < self.multifileRetries:
|
|
self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
|
|
self.curMultifileRetry += 1
|
|
self.notify.info('downloadPatchDone: Recovering from error.' + ' Deleting files in: ' + self.currentMfname)
|
|
self.dldb.setClientMultifileIncomplete(self.currentMfname)
|
|
self.dldb.setClientMultifileSize(self.currentMfname, 0)
|
|
self.notify.info('downloadPatchDone: Recovering from error.' + ' redownloading: ' + self.currentMfname)
|
|
self.httpChannel.reset()
|
|
self.getMultifile(self.currentMfname)
|
|
else:
|
|
self.setPandaErrorCode(6)
|
|
self.notify.info('handleGenericMultifileError: Failed to download multifile')
|
|
sys.exit()
|
|
|
|
def foregroundSleep(self):
|
|
if not self.backgrounded:
|
|
time.sleep(self.ForegroundSleepTime)
|
|
|
|
def forceSleep(self):
|
|
if not self.backgrounded:
|
|
time.sleep(3.0)
|
|
|
|
def addDownloadVersion(self, serverFilePath):
|
|
url = URLSpec(self.downloadServer)
|
|
origPath = url.getPath()
|
|
if origPath and origPath[-1] == '/':
|
|
origPath = origPath[:-1]
|
|
if self.fromCD:
|
|
url.setPath(self.getCDDownloadPath(origPath, serverFilePath))
|
|
else:
|
|
url.setPath(self.getDownloadPath(origPath, serverFilePath))
|
|
self.notify.info('***' + url.cStr())
|
|
return url
|
|
|
|
def download(self, serverFilePath, localFilename, callback, callbackProgress):
|
|
self.launcherMessage(self.Localizer.LauncherDownloadFile % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases})
|
|
task = MiniTask(self.downloadTask)
|
|
task.downloadRam = 0
|
|
task.serverFilePath = serverFilePath
|
|
task.serverFileURL = self.addDownloadVersion(serverFilePath)
|
|
self.notify.info('Download request: %s' % task.serverFileURL.cStr())
|
|
task.callback = callback
|
|
task.callbackProgress = callbackProgress
|
|
task.lastUpdate = 0
|
|
self.resetBytesPerSecond()
|
|
task.localFilename = localFilename
|
|
self.httpChannel.beginGetDocument(DocumentSpec(task.serverFileURL))
|
|
self.httpChannel.downloadToFile(task.localFilename)
|
|
self.miniTaskMgr.add(task, 'launcher-download')
|
|
|
|
def downloadRam(self, serverFilePath, callback):
|
|
self.ramfile = Ramfile()
|
|
task = MiniTask(self.downloadTask)
|
|
task.downloadRam = 1
|
|
task.serverFilePath = serverFilePath
|
|
task.serverFileURL = self.addDownloadVersion(serverFilePath)
|
|
self.notify.info('Download request: %s' % task.serverFileURL.cStr())
|
|
task.callback = callback
|
|
task.callbackProgress = None
|
|
task.lastUpdate = 0
|
|
self.resetBytesPerSecond()
|
|
self.httpChannel.beginGetDocument(DocumentSpec(task.serverFileURL))
|
|
self.httpChannel.downloadToRam(self.ramfile)
|
|
self.miniTaskMgr.add(task, 'launcher-download')
|
|
return
|
|
|
|
def downloadTask(self, task):
|
|
self.maybeStartGame()
|
|
if self.httpChannel.run():
|
|
now = self.getTime()
|
|
if now - task.lastUpdate >= self.UserUpdateDelay:
|
|
task.lastUpdate = now
|
|
self.testBandwidth()
|
|
if task.callbackProgress:
|
|
task.callbackProgress(task)
|
|
bytesWritten = self.httpChannel.getBytesDownloaded()
|
|
totalBytes = self.httpChannel.getFileSize()
|
|
if totalBytes:
|
|
pct = int(round(bytesWritten / float(totalBytes) * 100))
|
|
self.launcherMessage(self.Localizer.LauncherDownloadFilePercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': pct})
|
|
else:
|
|
self.launcherMessage(self.Localizer.LauncherDownloadFileBytes % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'bytes': bytesWritten})
|
|
self.foregroundSleep()
|
|
return task.cont
|
|
statusCode = self.httpChannel.getStatusCode()
|
|
statusString = self.httpChannel.getStatusString()
|
|
self.notify.info('HTTP status %s: %s' % (statusCode, statusString))
|
|
if self.httpChannel.isValid() and self.httpChannel.isDownloadComplete():
|
|
bytesWritten = self.httpChannel.getBytesDownloaded()
|
|
totalBytes = self.httpChannel.getFileSize()
|
|
if totalBytes:
|
|
pct = int(round(bytesWritten / float(totalBytes) * 100))
|
|
self.launcherMessage(self.Localizer.LauncherDownloadFilePercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': pct})
|
|
else:
|
|
self.launcherMessage(self.Localizer.LauncherDownloadFileBytes % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'bytes': bytesWritten})
|
|
self.notify.info('downloadTask: Download done: %s' % task.serverFileURL.cStr())
|
|
task.callback()
|
|
del task.callback
|
|
return task.done
|
|
else:
|
|
if statusCode == HTTPChannel.SCDownloadOpenError or statusCode == HTTPChannel.SCDownloadWriteError:
|
|
self.handleDownloadWriteError(task)
|
|
elif statusCode == HTTPChannel.SCLostConnection:
|
|
gotBytes = self.httpChannel.getBytesDownloaded()
|
|
self.notify.info('Connection lost while downloading; got %s bytes. Reconnecting.' % gotBytes)
|
|
if task.downloadRam:
|
|
self.downloadRam(task.serverFilePath, task.callback)
|
|
else:
|
|
self.download(task.serverFilePath, task.localFilename, task.callback, None)
|
|
else:
|
|
if self.httpChannel.isValid():
|
|
self.notify.info('Unexpected situation: no error status, but %s incompletely downloaded.' % task.serverFileURL.cStr())
|
|
self.handleDownloadFatalError(task)
|
|
if task.downloadRam:
|
|
self.downloadRam(task.serverFilePath, task.callback)
|
|
else:
|
|
self.download(task.serverFilePath, task.localFilename, task.callback, None)
|
|
return task.done
|
|
return
|
|
|
|
def downloadMultifile(self, serverFilename, localFilename, mfname, callback, totalSize, currentSize, callbackProgress):
|
|
if currentSize != 0 and currentSize == totalSize:
|
|
callback()
|
|
return
|
|
self.launcherMessage(self.Localizer.LauncherDownloadFile % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases})
|
|
task = MiniTask(self.downloadMultifileTask)
|
|
mfURL = self.addDownloadVersion(serverFilename)
|
|
task.mfURL = mfURL
|
|
self.notify.info('downloadMultifile: %s ' % task.mfURL.cStr())
|
|
task.callback = callback
|
|
task.callbackProgress = callbackProgress
|
|
task.lastUpdate = 0
|
|
self.httpChannel.getHeader(DocumentSpec(task.mfURL))
|
|
if self.httpChannel.isFileSizeKnown():
|
|
task.totalSize = self.httpChannel.getFileSize()
|
|
else:
|
|
task.totalSize = totalSize
|
|
self.resetBytesPerSecond()
|
|
task.serverFilename = serverFilename
|
|
task.localFilename = localFilename
|
|
task.mfname = mfname
|
|
if currentSize != 0:
|
|
if task.totalSize == currentSize:
|
|
self.notify.info('already have full file! Skipping download.')
|
|
callback()
|
|
return
|
|
self.httpChannel.beginGetSubdocument(DocumentSpec(task.mfURL), currentSize, task.totalSize)
|
|
self.httpChannel.downloadToFile(task.localFilename, True)
|
|
else:
|
|
self.httpChannel.beginGetDocument(DocumentSpec(task.mfURL))
|
|
self.httpChannel.downloadToFile(task.localFilename)
|
|
self._addMiniTask(task, 'launcher-download-multifile')
|
|
|
|
def downloadPatchSimpleProgress(self, task):
|
|
startingByte = self.httpChannel.getFirstByteDelivered()
|
|
bytesDownloaded = self.httpChannel.getBytesDownloaded()
|
|
bytesWritten = startingByte + bytesDownloaded
|
|
totalBytes = self.httpChannel.getFileSize()
|
|
percentPatchComplete = int(round(bytesWritten / float(totalBytes) * self.downloadPercentage))
|
|
self.setPercentPhaseComplete(self.currentPhase, percentPatchComplete)
|
|
|
|
def getPercentPatchComplete(self, bytesWritten):
|
|
return int(round((self.patchDownloadSoFar + bytesWritten) / float(self.totalPatchDownload) * self.downloadPercentage))
|
|
|
|
def downloadPatchOverallProgress(self, task):
|
|
startingByte = self.httpChannel.getFirstByteDelivered()
|
|
bytesDownloaded = self.httpChannel.getBytesDownloaded()
|
|
bytesWritten = startingByte + bytesDownloaded
|
|
percentPatchComplete = self.getPercentPatchComplete(bytesWritten)
|
|
self.setPercentPhaseComplete(self.currentPhase, percentPatchComplete)
|
|
|
|
def downloadMultifileWriteToDisk(self, task):
|
|
self.maybeStartGame()
|
|
startingByte = self.httpChannel.getFirstByteDelivered()
|
|
bytesDownloaded = self.httpChannel.getBytesDownloaded()
|
|
bytesWritten = startingByte + bytesDownloaded
|
|
if self.dldb:
|
|
self.dldb.setClientMultifileSize(task.mfname, bytesWritten)
|
|
percentComplete = 0
|
|
if task.totalSize != 0:
|
|
percentComplete = int(round(bytesWritten / float(task.totalSize) * self.downloadPercentage))
|
|
self.setPercentPhaseComplete(self.currentPhase, percentComplete)
|
|
|
|
def downloadMultifileTask(self, task):
|
|
task.totalSize = self.httpChannel.getFileSize()
|
|
if self.httpChannel.run():
|
|
now = self.getTime()
|
|
if now - task.lastUpdate >= self.UserUpdateDelay:
|
|
task.lastUpdate = now
|
|
self.testBandwidth()
|
|
if task.callbackProgress:
|
|
task.callbackProgress(task)
|
|
startingByte = self.httpChannel.getFirstByteDelivered()
|
|
bytesDownloaded = self.httpChannel.getBytesDownloaded()
|
|
bytesWritten = startingByte + bytesDownloaded
|
|
percentComplete = 0
|
|
if task.totalSize != 0:
|
|
percentComplete = int(round(100.0 * bytesWritten / float(task.totalSize)))
|
|
self.launcherMessage(self.Localizer.LauncherDownloadFilePercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': percentComplete})
|
|
self.foregroundSleep()
|
|
return task.cont
|
|
statusCode = self.httpChannel.getStatusCode()
|
|
statusString = self.httpChannel.getStatusString()
|
|
self.notify.info('HTTP status %s: %s' % (statusCode, statusString))
|
|
if self.httpChannel.isValid() and self.httpChannel.isDownloadComplete():
|
|
if task.callbackProgress:
|
|
task.callbackProgress(task)
|
|
self.notify.info('done: %s' % task.mfname)
|
|
if self.dldb:
|
|
self.dldb.setClientMultifileComplete(task.mfname)
|
|
task.callback()
|
|
del task.callback
|
|
return task.done
|
|
else:
|
|
if statusCode == HTTPChannel.SCDownloadOpenError or statusCode == HTTPChannel.SCDownloadWriteError:
|
|
self.handleDownloadWriteError(task)
|
|
elif statusCode == HTTPChannel.SCLostConnection:
|
|
startingByte = self.httpChannel.getFirstByteDelivered()
|
|
bytesDownloaded = self.httpChannel.getBytesDownloaded()
|
|
bytesWritten = startingByte + bytesDownloaded
|
|
self.notify.info('Connection lost while downloading; got %s bytes. Reconnecting.' % bytesDownloaded)
|
|
self.downloadMultifile(task.serverFilename, task.localFilename, task.mfname, task.callback, task.totalSize, bytesWritten, task.callbackProgress)
|
|
elif (statusCode == 416 or statusCode == HTTPChannel.SCDownloadInvalidRange) and self.httpChannel.getFirstByteRequested() != 0:
|
|
self.notify.info('Invalid subrange; redownloading entire file.')
|
|
self.downloadMultifile(task.serverFilename, task.localFilename, task.mfname, task.callback, task.totalSize, 0, task.callbackProgress)
|
|
else:
|
|
if self.httpChannel.isValid():
|
|
self.notify.info('Unexpected situation: no error status, but %s incompletely downloaded.' % task.mfname)
|
|
self.handleDownloadFatalError(task)
|
|
self.downloadMultifile(task.serverFilename, task.localFilename, task.mfname, task.callback, task.totalSize, 0, task.callbackProgress)
|
|
return task.done
|
|
|
|
def decompressFile(self, localFilename, callback):
|
|
self.notify.info('decompress: request: ' + localFilename.cStr())
|
|
self.launcherMessage(self.Localizer.LauncherDecompressingFile % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases})
|
|
task = MiniTask(self.decompressFileTask)
|
|
task.localFilename = localFilename
|
|
task.callback = callback
|
|
task.lastUpdate = 0
|
|
task.decompressor = Decompressor()
|
|
errorCode = task.decompressor.initiate(task.localFilename)
|
|
if errorCode > 0:
|
|
self._addMiniTask(task, 'launcher-decompressFile')
|
|
else:
|
|
self.handleInitiateFatalError(errorCode)
|
|
|
|
def decompressFileTask(self, task):
|
|
errorCode = task.decompressor.run()
|
|
if errorCode == EUOk:
|
|
now = self.getTime()
|
|
if now - task.lastUpdate >= self.UserUpdateDelay:
|
|
task.lastUpdate = now
|
|
progress = task.decompressor.getProgress()
|
|
self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': int(round(progress * 100))})
|
|
self.foregroundSleep()
|
|
return task.cont
|
|
elif errorCode == EUSuccess:
|
|
self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': 100})
|
|
self.notify.info('decompressTask: Decompress done: ' + task.localFilename.cStr())
|
|
del task.decompressor
|
|
task.callback()
|
|
del task.callback
|
|
return task.done
|
|
elif errorCode == EUErrorAbort:
|
|
self.handleDecompressFatalError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
|
|
self.handleDecompressWriteError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorZlib:
|
|
self.handleDecompressZlibError(task, errorCode)
|
|
return task.done
|
|
elif errorCode > 0:
|
|
self.notify.warning('decompressMultifileTask: Unknown success return code: ' + errorToText(errorCode))
|
|
return task.cont
|
|
else:
|
|
self.notify.warning('decompressMultifileTask: Unknown return code: ' + errorToText(errorCode))
|
|
self.handleDecompressFatalError(task, errorCode)
|
|
return task.done
|
|
|
|
def decompressMultifile(self, mfname, localFilename, callback):
|
|
self.notify.info('decompressMultifile: request: ' + localFilename.cStr())
|
|
self.launcherMessage(self.Localizer.LauncherDecompressingFile % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases})
|
|
task = MiniTask(self.decompressMultifileTask)
|
|
task.mfname = mfname
|
|
task.localFilename = localFilename
|
|
task.callback = callback
|
|
task.lastUpdate = 0
|
|
task.decompressor = Decompressor()
|
|
errorCode = task.decompressor.initiate(task.localFilename)
|
|
if errorCode > 0:
|
|
self._addMiniTask(task, 'launcher-decompressMultifile')
|
|
else:
|
|
self.handleInitiateFatalError(errorCode)
|
|
|
|
def decompressMultifileTask(self, task):
|
|
errorCode = task.decompressor.run()
|
|
if errorCode == EUOk:
|
|
now = self.getTime()
|
|
if now - task.lastUpdate >= self.UserUpdateDelay:
|
|
task.lastUpdate = now
|
|
progress = task.decompressor.getProgress()
|
|
self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': int(round(progress * 100))})
|
|
percentProgress = int(round(progress * self.decompressPercentage))
|
|
totalPercent = self.downloadPercentage + percentProgress
|
|
self.setPercentPhaseComplete(self.currentPhase, totalPercent)
|
|
self.foregroundSleep()
|
|
return task.cont
|
|
elif errorCode == EUSuccess:
|
|
self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': 100})
|
|
totalPercent = self.downloadPercentage + self.decompressPercentage
|
|
self.setPercentPhaseComplete(self.currentPhase, totalPercent)
|
|
self.notify.info('decompressMultifileTask: Decompress multifile done: ' + task.localFilename.cStr())
|
|
self.dldb.setClientMultifileDecompressed(task.mfname)
|
|
del task.decompressor
|
|
task.callback()
|
|
del task.callback
|
|
return task.done
|
|
elif errorCode == EUErrorAbort:
|
|
self.handleDecompressFatalError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
|
|
self.handleDecompressWriteError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorZlib:
|
|
self.handleDecompressZlibError(task, errorCode)
|
|
return task.done
|
|
elif errorCode > 0:
|
|
self.notify.warning('decompressMultifileTask: Unknown success return code: ' + errorToText(errorCode))
|
|
return task.cont
|
|
else:
|
|
self.notify.warning('decompressMultifileTask: Unknown return code: ' + errorToText(errorCode))
|
|
self.handleDecompressFatalError(task, errorCode)
|
|
return task.done
|
|
|
|
def extract(self, mfname, localFilename, destDir, callback):
|
|
self.notify.info('extract: request: ' + localFilename.cStr() + ' destDir: ' + destDir.cStr())
|
|
self.launcherMessage(self.Localizer.LauncherExtractingFile % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases})
|
|
task = MiniTask(self.extractTask)
|
|
task.mfname = mfname
|
|
task.localFilename = localFilename
|
|
task.destDir = destDir
|
|
task.callback = callback
|
|
task.lastUpdate = 0
|
|
task.extractor = Extractor()
|
|
task.extractor.setExtractDir(task.destDir)
|
|
if not task.extractor.setMultifile(task.localFilename):
|
|
self.setPandaErrorCode(6)
|
|
self.notify.info('extract: Unable to open multifile %s' % task.localFilename.cStr())
|
|
sys.exit()
|
|
numFiles = self.dldb.getServerNumFiles(mfname)
|
|
for i in xrange(numFiles):
|
|
subfile = self.dldb.getServerFileName(mfname, i)
|
|
if not task.extractor.requestSubfile(Filename(subfile)):
|
|
self.setPandaErrorCode(6)
|
|
self.notify.info('extract: Unable to find subfile %s in multifile %s' % (subfile, mfname))
|
|
sys.exit()
|
|
|
|
self.notify.info('Extracting %d subfiles from multifile %s.' % (numFiles, mfname))
|
|
self._addMiniTask(task, 'launcher-extract')
|
|
|
|
def extractTask(self, task):
|
|
errorCode = task.extractor.step()
|
|
if errorCode == EUOk:
|
|
now = self.getTime()
|
|
if now - task.lastUpdate >= self.UserUpdateDelay:
|
|
task.lastUpdate = now
|
|
progress = task.extractor.getProgress()
|
|
self.launcherMessage(self.Localizer.LauncherExtractingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': int(round(progress * 100.0))})
|
|
percentProgress = int(round(progress * self.extractPercentage))
|
|
totalPercent = self.downloadPercentage + self.decompressPercentage + percentProgress
|
|
self.setPercentPhaseComplete(self.currentPhase, totalPercent)
|
|
self.foregroundSleep()
|
|
return task.cont
|
|
elif errorCode == EUSuccess:
|
|
self.launcherMessage(self.Localizer.LauncherExtractingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': 100})
|
|
totalPercent = self.downloadPercentage + self.decompressPercentage + self.extractPercentage
|
|
self.setPercentPhaseComplete(self.currentPhase, totalPercent)
|
|
self.notify.info('extractTask: Extract multifile done: ' + task.localFilename.cStr())
|
|
self.dldb.setClientMultifileExtracted(task.mfname)
|
|
del task.extractor
|
|
task.callback()
|
|
del task.callback
|
|
return task.done
|
|
elif errorCode == EUErrorAbort:
|
|
self.handleExtractFatalError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorFileEmpty:
|
|
self.handleExtractFatalError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
|
|
self.handleExtractWriteError(task, errorCode)
|
|
return task.done
|
|
elif errorCode > 0:
|
|
self.notify.warning('extractTask: Unknown success return code: ' + errorToText(errorCode))
|
|
return task.cont
|
|
else:
|
|
self.notify.warning('extractTask: Unknown error return code: ' + errorToText(errorCode))
|
|
self.handleExtractFatalError(task, errorCode)
|
|
return task.done
|
|
|
|
def patch(self, patchFile, patcheeFile, callback):
|
|
self.notify.info('patch: request: ' + patchFile.cStr() + ' patchee: ' + patcheeFile.cStr())
|
|
self.launcherMessage(self.Localizer.LauncherPatchingFile % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases})
|
|
task = MiniTask(self.patchTask)
|
|
task.patchFile = patchFile
|
|
task.patcheeFile = patcheeFile
|
|
task.callback = callback
|
|
task.lastUpdate = 0
|
|
task.patcher = Patcher()
|
|
errorCode = task.patcher.initiate(task.patchFile, task.patcheeFile)
|
|
if errorCode > 0:
|
|
self._addMiniTask(task, 'launcher-patch')
|
|
else:
|
|
self.handleInitiateFatalError(errorCode)
|
|
|
|
def patchTask(self, task):
|
|
errorCode = task.patcher.run()
|
|
if errorCode == EUOk:
|
|
now = self.getTime()
|
|
if now - task.lastUpdate >= self.UserUpdateDelay:
|
|
task.lastUpdate = now
|
|
progress = task.patcher.getProgress()
|
|
self.launcherMessage(self.Localizer.LauncherPatchingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': int(round(progress * 100.0))})
|
|
self.foregroundSleep()
|
|
return task.cont
|
|
elif errorCode == EUSuccess:
|
|
self.launcherMessage(self.Localizer.LauncherPatchingPercent % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases,
|
|
'percent': 100})
|
|
self.notify.info('patchTask: Patch done: ' + task.patcheeFile.cStr())
|
|
del task.patcher
|
|
task.callback()
|
|
del task.callback
|
|
return task.done
|
|
elif errorCode == EUErrorAbort:
|
|
self.handlePatchFatalError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorFileEmpty:
|
|
self.handlePatchFatalError(task, errorCode)
|
|
return task.done
|
|
elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
|
|
self.handlePatchWriteError(task, errorCode)
|
|
return task.done
|
|
elif errorCode > 0:
|
|
self.notify.warning('patchTask: Unknown success return code: ' + errorToText(errorCode))
|
|
return task.cont
|
|
else:
|
|
self.notify.warning('patchTask: Unknown error return code: ' + errorToText(errorCode))
|
|
self.handlePatchFatalError(task, errorCode)
|
|
return task.done
|
|
|
|
def getProgressSum(self, phase):
|
|
sum = 0
|
|
for i in xrange(0, len(self.linesInProgress)):
|
|
if self.linesInProgress[i].find(phase) > -1:
|
|
nameSizeTuple = self.linesInProgress[i].split()
|
|
numSize = nameSizeTuple[1].split('L')
|
|
sum += numSize[0].atoi()
|
|
|
|
return sum
|
|
|
|
def readProgressFile(self):
|
|
localFilename = Filename(self.dbDir, Filename(self.progressFilename))
|
|
if not localFilename.exists():
|
|
self.notify.warning('File does not exist: %s' % localFilename.cStr())
|
|
self.linesInProgress = []
|
|
else:
|
|
f = open(localFilename.toOsSpecific())
|
|
self.linesInProgress = f.readlines()
|
|
f.close()
|
|
localFilename.unlink()
|
|
self.progressSum = 0
|
|
token = 'phase_'
|
|
self.progressSum = self.getProgressSum(token)
|
|
self.progressSum -= self.getProgressSum(token + '2')
|
|
self.notify.info('total phases to be downloaded = ' + `(self.progressSum)`)
|
|
self.checkClientDbExists()
|
|
|
|
def prepareClient(self):
|
|
self.notify.info('prepareClient: Preparing client for install')
|
|
if not self.topDir.exists():
|
|
self.notify.info('prepareClient: Creating top directory: ' + self.topDir.cStr())
|
|
os.makedirs(self.topDir.toOsSpecific())
|
|
if not self.dbDir.exists():
|
|
self.notify.info('prepareClient: Creating db directory: ' + self.dbDir.cStr())
|
|
os.makedirs(self.dbDir.toOsSpecific())
|
|
if not self.patchDir.exists():
|
|
self.notify.info('prepareClient: Creating patch directory: ' + self.patchDir.cStr())
|
|
os.makedirs(self.patchDir.toOsSpecific())
|
|
if not self.mfDir.exists():
|
|
self.notify.info('prepareClient: Creating mf directory: ' + self.mfDir.cStr())
|
|
os.makedirs(self.mfDir.toOsSpecific())
|
|
|
|
def downloadLauncherFileDb(self):
|
|
self.notify.info('Downloading launcherFileDb')
|
|
self.downloadRam(self.launcherFileDbFilename, self.downloadLauncherFileDbDone)
|
|
|
|
def downloadLauncherFileDbDone(self):
|
|
self.launcherFileDbHash = HashVal()
|
|
self.launcherFileDbHash.hashRamfile(self.ramfile)
|
|
if self.VerifyFiles:
|
|
self.notify.info('Validating Launcher files')
|
|
for fileDesc in self.ramfile.readlines():
|
|
try:
|
|
filename, hashStr = fileDesc.split(' ', 1)
|
|
except:
|
|
self.notify.info('Invalid line: "%s"' % fileDesc)
|
|
self.failLauncherFileDb('No hash in launcherFileDb')
|
|
|
|
serverHash = HashVal()
|
|
if not self.hashIsValid(serverHash, hashStr):
|
|
self.notify.info('Not a valid hash string: "%s"' % hashStr)
|
|
self.failLauncherFileDb('Invalid hash in launcherFileDb')
|
|
localHash = HashVal()
|
|
localFilename = Filename(self.topDir, Filename(filename))
|
|
localHash.hashFile(localFilename)
|
|
if localHash != serverHash:
|
|
self.failLauncherFileDb('%s does not match expected version.' % filename)
|
|
|
|
self.downloadServerDbFile()
|
|
|
|
def failLauncherFileDb(self, string):
|
|
self.notify.info(string)
|
|
self.setPandaErrorCode(15)
|
|
sys.exit()
|
|
|
|
def downloadServerDbFile(self):
|
|
self.notify.info('Downloading server db file')
|
|
self.launcherMessage(self.Localizer.LauncherDownloadServerFileList)
|
|
self.downloadRam(self.serverDbFilePath, self.downloadServerDbFileDone)
|
|
|
|
def downloadServerDbFileDone(self):
|
|
self.serverDbFileHash = HashVal()
|
|
self.serverDbFileHash.hashRamfile(self.ramfile)
|
|
self.readProgressFile()
|
|
|
|
def checkClientDbExists(self):
|
|
clientFilename = Filename(self.dbDir, Filename(self.clientDbFilename))
|
|
if clientFilename.exists():
|
|
self.notify.info('Client Db exists')
|
|
self.createDownloadDb()
|
|
else:
|
|
self.notify.info('Client Db does not exist')
|
|
self.downloadClientDbStarterFile()
|
|
|
|
def downloadClientDbStarterFile(self):
|
|
self.notify.info('Downloading Client Db starter file')
|
|
localFilename = Filename(self.dbDir, Filename(self.compClientDbFilename))
|
|
self.download(self.clientStarterDbFilePath, localFilename, self.downloadClientDbStarterFileDone, None)
|
|
return
|
|
|
|
def downloadClientDbStarterFileDone(self):
|
|
localFilename = Filename(self.dbDir, Filename(self.compClientDbFilename))
|
|
decompressor = Decompressor()
|
|
decompressor.decompress(localFilename)
|
|
self.createDownloadDb()
|
|
|
|
def createDownloadDb(self):
|
|
self.notify.info('Creating downloadDb')
|
|
self.launcherMessage(self.Localizer.LauncherCreatingDownloadDb)
|
|
clientFilename = Filename(self.dbDir, Filename(self.clientDbFilename))
|
|
self.notify.info('Client file name: ' + clientFilename.cStr())
|
|
self.launcherMessage(self.Localizer.LauncherDownloadClientFileList)
|
|
serverFile = self.ramfile
|
|
decompressor = Decompressor()
|
|
decompressor.decompress(serverFile)
|
|
self.notify.info('Finished decompress')
|
|
self.dldb = DownloadDb(serverFile, clientFilename)
|
|
self.notify.info('created download db')
|
|
self.launcherMessage(self.Localizer.LauncherFinishedDownloadDb)
|
|
self.currentPhase = self.LauncherPhases[0]
|
|
self.currentPhaseIndex = 1
|
|
self.currentPhaseName = self.Localizer.LauncherPhaseNames[self.currentPhase]
|
|
self.updatePhase(self.currentPhase)
|
|
|
|
def maybeStartGame(self):
|
|
if not self.started and self.currentPhase >= self.showPhase:
|
|
self.started = True
|
|
self.notify.info('maybeStartGame: starting game')
|
|
self.launcherMessage(self.Localizer.LauncherStartingGame)
|
|
self.background()
|
|
__builtin__.launcher = self
|
|
self.startGame()
|
|
|
|
def _runTaskManager(self):
|
|
if not self.taskMgrStarted:
|
|
self.miniTaskMgr.run()
|
|
self.notify.info('Switching task managers.')
|
|
taskMgr.run()
|
|
|
|
def _stepMiniTaskManager(self, task):
|
|
self.miniTaskMgr.step()
|
|
if self.miniTaskMgr.taskList:
|
|
return task.cont
|
|
self.notify.info('Stopping mini task manager.')
|
|
self.miniTaskMgr = None
|
|
return task.done
|
|
|
|
def _addMiniTask(self, task, name):
|
|
if not self.miniTaskMgr:
|
|
self.notify.info('Restarting mini task manager.')
|
|
self.miniTaskMgr = MiniTaskManager()
|
|
from direct.task.TaskManagerGlobal import taskMgr
|
|
taskMgr.remove('miniTaskManager')
|
|
taskMgr.add(self._stepMiniTaskManager, 'miniTaskManager')
|
|
self.miniTaskMgr.add(task, name)
|
|
|
|
def newTaskManager(self):
|
|
self.taskMgrStarted = True
|
|
if self.miniTaskMgr.running:
|
|
self.miniTaskMgr.stop()
|
|
from direct.task.TaskManagerGlobal import taskMgr
|
|
taskMgr.remove('miniTaskManager')
|
|
taskMgr.add(self._stepMiniTaskManager, 'miniTaskManager')
|
|
|
|
def mainLoop(self):
|
|
try:
|
|
self._runTaskManager()
|
|
except SystemExit:
|
|
if hasattr(__builtin__, 'base'):
|
|
base.destroy()
|
|
self.notify.info('Normal exit.')
|
|
raise
|
|
except:
|
|
self.setPandaErrorCode(12)
|
|
self.notify.warning('Handling Python exception.')
|
|
if hasattr(__builtin__, 'base') and getattr(base, 'cr', None):
|
|
if base.cr.timeManager:
|
|
from otp.otpbase import OTPGlobals
|
|
base.cr.timeManager.setDisconnectReason(OTPGlobals.DisconnectPythonError)
|
|
base.cr.timeManager.setExceptionInfo()
|
|
base.cr.sendDisconnect()
|
|
if hasattr(__builtin__, 'base'):
|
|
base.destroy()
|
|
self.notify.info('Exception exit.\n')
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit()
|
|
|
|
return
|
|
|
|
def updatePhase(self, phase):
|
|
self.notify.info('Updating multifiles in phase: ' + `phase`)
|
|
self.setPercentPhaseComplete(self.currentPhase, 0)
|
|
self.phaseMultifileNames = []
|
|
numfiles = self.dldb.getServerNumMultifiles()
|
|
for i in xrange(self.dldb.getServerNumMultifiles()):
|
|
mfname = self.dldb.getServerMultifileName(i)
|
|
if self.dldb.getServerMultifilePhase(mfname) == phase:
|
|
self.phaseMultifileNames.append(mfname)
|
|
|
|
self.updateNextMultifile()
|
|
|
|
def updateNextMultifile(self):
|
|
if len(self.phaseMultifileNames) > 0:
|
|
self.currentMfname = self.phaseMultifileNames.pop()
|
|
self.curMultifileRetry = 0
|
|
self.getMultifile(self.currentMfname)
|
|
else:
|
|
if self.currentMfname is None:
|
|
self.notify.warning('no multifile found! See below for debug info:')
|
|
for i in xrange(self.dldb.getServerNumMultifiles()):
|
|
mfname = self.dldb.getServerMultifileName(i)
|
|
phase = self.dldb.getServerMultifilePhase(mfname)
|
|
print i, mfname, phase
|
|
|
|
self.handleGenericMultifileError()
|
|
decompressedMfname = os.path.splitext(self.currentMfname)[0]
|
|
localFilename = Filename(self.mfDir, Filename(decompressedMfname))
|
|
nextIndex = self.LauncherPhases.index(self.currentPhase) + 1
|
|
if nextIndex < len(self.LauncherPhases):
|
|
self.MakeNTFSFilesGlobalWriteable(localFilename)
|
|
else:
|
|
self.MakeNTFSFilesGlobalWriteable()
|
|
vfs = VirtualFileSystem.getGlobalPtr()
|
|
vfs.mount(localFilename, '.', VirtualFileSystem.MFReadOnly)
|
|
self.setPercentPhaseComplete(self.currentPhase, 100)
|
|
self.notify.info('Done updating multifiles in phase: ' + `(self.currentPhase)`)
|
|
self.progressSoFar += int(round(self.phaseOverallMap[self.currentPhase] * 100))
|
|
self.notify.info('progress so far ' + `(self.progressSoFar)`)
|
|
messenger.send('phaseComplete-' + `(self.currentPhase)`)
|
|
if nextIndex < len(self.LauncherPhases):
|
|
self.currentPhase = self.LauncherPhases[nextIndex]
|
|
self.currentPhaseIndex = nextIndex + 1
|
|
self.currentPhaseName = self.Localizer.LauncherPhaseNames[self.currentPhase]
|
|
self.updatePhase(self.currentPhase)
|
|
else:
|
|
self.notify.info('ALL PHASES COMPLETE')
|
|
self.maybeStartGame()
|
|
messenger.send('launcherAllPhasesComplete')
|
|
self.cleanup()
|
|
return
|
|
|
|
def isDownloadComplete(self):
|
|
return self._downloadComplete
|
|
|
|
def updateMultifileDone(self):
|
|
self.updateNextMultifile()
|
|
|
|
def downloadMultifileDone(self):
|
|
self.getDecompressMultifile(self.currentMfname)
|
|
|
|
def getMultifile(self, mfname):
|
|
self.notify.info('Downloading multifile: ' + mfname)
|
|
if not self.dldb.clientMultifileExists(mfname):
|
|
self.maybeStartGame()
|
|
self.notify.info('Multifile does not exist in client db,' + 'creating new record: ' + mfname)
|
|
self.dldb.addClientMultifile(mfname)
|
|
curHash = self.dldb.getServerMultifileHash(mfname)
|
|
self.dldb.setClientMultifileHash(mfname, curHash)
|
|
localFilename = Filename(self.mfDir, Filename(mfname))
|
|
if localFilename.exists():
|
|
curSize = localFilename.getFileSize()
|
|
self.dldb.setClientMultifileSize(mfname, curSize)
|
|
if curSize == self.dldb.getServerMultifileSize(mfname):
|
|
self.dldb.setClientMultifileComplete(mfname)
|
|
decompressedMfname = os.path.splitext(mfname)[0]
|
|
decompressedFilename = Filename(self.mfDir, Filename(decompressedMfname))
|
|
if (not self.dldb.clientMultifileComplete(mfname) or not self.dldb.clientMultifileDecompressed(mfname)) and decompressedFilename.exists():
|
|
clientMd5 = HashVal()
|
|
clientMd5.hashFile(decompressedFilename)
|
|
clientVer = self.dldb.getVersion(Filename(decompressedMfname), clientMd5)
|
|
if clientVer != -1:
|
|
self.notify.info('Decompressed multifile is already on disk and correct: %s (version %s)' % (mfname, clientVer))
|
|
self.dldb.setClientMultifileComplete(mfname)
|
|
self.dldb.setClientMultifileDecompressed(mfname)
|
|
compressedFilename = Filename(self.mfDir, Filename(mfname))
|
|
compressedFilename.unlink()
|
|
extractedOk = True
|
|
numFiles = self.dldb.getServerNumFiles(mfname)
|
|
for i in xrange(numFiles):
|
|
subfile = self.dldb.getServerFileName(mfname, i)
|
|
fn = Filename(self.mfDir, Filename(subfile))
|
|
if fn.compareTimestamps(decompressedFilename) <= 0:
|
|
extractedOk = False
|
|
break
|
|
|
|
if extractedOk:
|
|
self.notify.info('Multifile appears to have been extracted already.')
|
|
self.dldb.setClientMultifileExtracted(mfname)
|
|
if not self.dldb.clientMultifileComplete(mfname) or not decompressedFilename.exists():
|
|
self.maybeStartGame()
|
|
currentSize = self.dldb.getClientMultifileSize(mfname)
|
|
totalSize = self.dldb.getServerMultifileSize(mfname)
|
|
localFilename = Filename(self.mfDir, Filename(mfname))
|
|
if not localFilename.exists():
|
|
currentSize = 0
|
|
else:
|
|
currentSize = min(currentSize, localFilename.getFileSize())
|
|
if currentSize == 0:
|
|
self.notify.info('Multifile has not been started, ' + 'downloading new file: ' + mfname)
|
|
curHash = self.dldb.getServerMultifileHash(mfname)
|
|
self.dldb.setClientMultifileHash(mfname, curHash)
|
|
self.phaseNewDownload[self.currentPhase] = 1
|
|
self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, 0, self.downloadMultifileWriteToDisk)
|
|
else:
|
|
clientHash = self.dldb.getClientMultifileHash(mfname)
|
|
serverHash = self.dldb.getServerMultifileHash(mfname)
|
|
if clientHash.eq(serverHash):
|
|
self.notify.info('Multifile is not complete, finishing download for %s, size = %s / %s' % (mfname, currentSize, totalSize))
|
|
self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, currentSize, self.downloadMultifileWriteToDisk)
|
|
elif self.curMultifileRetry < self.multifileRetries:
|
|
self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
|
|
self.curMultifileRetry += 1
|
|
self.notify.info('Multifile is not complete, and is out of date. ' + 'Restarting download with newest multifile')
|
|
self.dldb.setClientMultifileIncomplete(self.currentMfname)
|
|
self.dldb.setClientMultifileSize(self.currentMfname, 0)
|
|
self.dldb.setClientMultifileHash(self.currentMfname, serverHash)
|
|
self.getMultifile(self.currentMfname)
|
|
else:
|
|
self.setPandaErrorCode(6)
|
|
self.notify.info('getMultifile: Failed to download multifile')
|
|
sys.exit()
|
|
else:
|
|
self.notify.info('Multifile already complete: ' + mfname)
|
|
self.downloadMultifileDone()
|
|
|
|
def updateMultifileDone(self):
|
|
self.updateNextMultifile()
|
|
|
|
def downloadMultifileDone(self):
|
|
self.getDecompressMultifile(self.currentMfname)
|
|
|
|
def getMultifile(self, mfname):
|
|
self.notify.info('Downloading multifile: ' + mfname)
|
|
if not self.dldb.clientMultifileExists(mfname):
|
|
self.maybeStartGame()
|
|
self.notify.info('Multifile does not exist in client db,' + 'creating new record: ' + mfname)
|
|
self.dldb.addClientMultifile(mfname)
|
|
if self.DecompressMultifiles:
|
|
curHash = self.dldb.getServerMultifileHash(mfname)
|
|
self.dldb.setClientMultifileHash(mfname, curHash)
|
|
localFilename = Filename(self.mfDir, Filename(mfname))
|
|
if localFilename.exists():
|
|
curSize = localFilename.getFileSize()
|
|
self.dldb.setClientMultifileSize(mfname, curSize)
|
|
if curSize == self.dldb.getServerMultifileSize(mfname):
|
|
self.dldb.setClientMultifileComplete(mfname)
|
|
decompressedMfname = os.path.splitext(mfname)[0]
|
|
decompressedFilename = Filename(self.mfDir, Filename(decompressedMfname))
|
|
if self.DecompressMultifiles:
|
|
if (not self.dldb.clientMultifileComplete(mfname) or not self.dldb.clientMultifileDecompressed(mfname)) and decompressedFilename.exists():
|
|
clientMd5 = HashVal()
|
|
clientMd5.hashFile(decompressedFilename)
|
|
clientVer = self.dldb.getVersion(Filename(decompressedMfname), clientMd5)
|
|
if clientVer != -1:
|
|
self.notify.info('Decompressed multifile is already on disk and correct: %s (version %s)' % (mfname, clientVer))
|
|
self.dldb.setClientMultifileComplete(mfname)
|
|
self.dldb.setClientMultifileDecompressed(mfname)
|
|
compressedFilename = Filename(self.mfDir, Filename(mfname))
|
|
compressedFilename.unlink()
|
|
extractedOk = True
|
|
numFiles = self.dldb.getServerNumFiles(mfname)
|
|
for i in xrange(numFiles):
|
|
subfile = self.dldb.getServerFileName(mfname, i)
|
|
fn = Filename(self.mfDir, Filename(subfile))
|
|
if fn.compareTimestamps(decompressedFilename) <= 0:
|
|
extractedOk = False
|
|
break
|
|
|
|
if extractedOk:
|
|
self.notify.info('Multifile appears to have been extracted already.')
|
|
self.dldb.setClientMultifileExtracted(mfname)
|
|
if not self.dldb.clientMultifileComplete(mfname) or not decompressedFilename.exists():
|
|
self.maybeStartGame()
|
|
currentSize = self.dldb.getClientMultifileSize(mfname)
|
|
totalSize = self.dldb.getServerMultifileSize(mfname)
|
|
localFilename = Filename(self.mfDir, Filename(mfname))
|
|
if not localFilename.exists():
|
|
currentSize = 0
|
|
if currentSize == 0:
|
|
self.notify.info('Multifile has not been started, ' + 'downloading new file: ' + mfname)
|
|
curHash = self.dldb.getServerMultifileHash(mfname)
|
|
self.dldb.setClientMultifileHash(mfname, curHash)
|
|
self.phaseNewDownload[self.currentPhase] = 1
|
|
self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, 0, self.downloadMultifileWriteToDisk)
|
|
else:
|
|
clientHash = self.dldb.getClientMultifileHash(mfname)
|
|
serverHash = self.dldb.getServerMultifileHash(mfname)
|
|
if clientHash.eq(serverHash):
|
|
self.notify.info('Multifile is not complete, finishing download for %s, size = %s / %s' % (mfname, currentSize, totalSize))
|
|
self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, currentSize, self.downloadMultifileWriteToDisk)
|
|
elif self.curMultifileRetry < self.multifileRetries:
|
|
self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
|
|
self.curMultifileRetry += 1
|
|
self.notify.info('Multifile is not complete, and is out of date. ' + 'Restarting download with newest multifile')
|
|
self.dldb.setClientMultifileIncomplete(self.currentMfname)
|
|
self.dldb.setClientMultifileSize(self.currentMfname, 0)
|
|
if self.DecompressMultifiles:
|
|
self.dldb.setClientMultifileHash(self.currentMfname, serverHash)
|
|
self.getMultifile(self.currentMfname)
|
|
else:
|
|
self.setPandaErrorCode(6)
|
|
self.notify.info('getMultifile: Failed to download multifile')
|
|
sys.exit()
|
|
else:
|
|
self.notify.info('Multifile already complete: ' + mfname)
|
|
self.downloadMultifileDone()
|
|
|
|
def getDecompressMultifile(self, mfname):
|
|
if not self.DecompressMultifiles:
|
|
self.decompressMultifileDone()
|
|
elif not self.dldb.clientMultifileDecompressed(mfname):
|
|
self.maybeStartGame()
|
|
self.notify.info('decompressMultifile: Decompressing multifile: ' + mfname)
|
|
localFilename = Filename(self.mfDir, Filename(mfname))
|
|
self.decompressMultifile(mfname, localFilename, self.decompressMultifileDone)
|
|
else:
|
|
self.notify.info('decompressMultifile: Multifile already decompressed: ' + mfname)
|
|
self.decompressMultifileDone()
|
|
|
|
def decompressMultifileDone(self):
|
|
if self.phaseNewDownload[self.currentPhase]:
|
|
self.setPercentPhaseComplete(self.currentPhase, 95)
|
|
self.extractMultifile(self.currentMfname)
|
|
|
|
def extractMultifile(self, mfname):
|
|
if not self.dldb.clientMultifileExtracted(mfname):
|
|
self.maybeStartGame()
|
|
self.notify.info('extractMultifile: Extracting multifile: ' + mfname)
|
|
decompressedMfname = os.path.splitext(mfname)[0]
|
|
localFilename = Filename(self.mfDir, Filename(decompressedMfname))
|
|
destDir = Filename(self.topDir)
|
|
self.notify.info('extractMultifile: Extracting: ' + localFilename.cStr() + ' to: ' + destDir.cStr())
|
|
self.extract(mfname, localFilename, destDir, self.extractMultifileDone)
|
|
else:
|
|
self.notify.info('extractMultifile: Multifile already extracted: ' + mfname)
|
|
self.extractMultifileDone()
|
|
|
|
def extractMultifileDone(self):
|
|
if self.phaseNewDownload[self.currentPhase]:
|
|
self.setPercentPhaseComplete(self.currentPhase, 99)
|
|
self.notify.info('extractMultifileDone: Finished updating multifile: ' + self.currentMfname)
|
|
self.patchMultifile()
|
|
|
|
def getPatchFilename(self, fname, currentVersion):
|
|
return fname + '.v' + `currentVersion` + '.' + self.patchExtension
|
|
|
|
def downloadPatches(self):
|
|
if len(self.patchList) > 0:
|
|
self.currentPatch, self.currentPatchee, self.currentPatchVersion = self.patchList.pop()
|
|
self.notify.info(self.contentDir)
|
|
self.notify.info(self.currentPatch)
|
|
patchFile = self.currentPatch + '.pz'
|
|
serverPatchFilePath = self.contentDir + patchFile
|
|
self.notify.info(serverPatchFilePath)
|
|
localPatchFilename = Filename(self.patchDir, Filename(patchFile))
|
|
if self.currentPhase > 3:
|
|
self.download(serverPatchFilePath, localPatchFilename, self.downloadPatchDone, self.downloadPatchSimpleProgress)
|
|
else:
|
|
self.download(serverPatchFilePath, localPatchFilename, self.downloadPatchDone, self.downloadPatchOverallProgress)
|
|
else:
|
|
self.notify.info('applyNextPatch: Done patching multifile: ' + `(self.currentPhase)`)
|
|
self.patchDone()
|
|
|
|
def downloadPatchDone(self):
|
|
self.patchDownloadSoFar += self.httpChannel.getBytesDownloaded()
|
|
self.notify.info('downloadPatchDone: Decompressing patch file: ' + self.currentPatch + '.pz')
|
|
self.decompressFile(Filename(self.patchDir, Filename(self.currentPatch + '.pz')), self.decompressPatchDone)
|
|
|
|
def decompressPatchDone(self):
|
|
self.notify.info('decompressPatchDone: Patching file: ' + self.currentPatchee + ' from ver: ' + `(self.currentPatchVersion)`)
|
|
patchFile = Filename(self.patchDir, Filename(self.currentPatch))
|
|
patchFile.setBinary()
|
|
patchee = Filename(self.mfDir, Filename(self.currentPatchee))
|
|
patchee.setBinary()
|
|
self.patch(patchFile, patchee, self.downloadPatches)
|
|
|
|
def patchDone(self):
|
|
self.notify.info('patchDone: Patch successful')
|
|
del self.currentPatch
|
|
del self.currentPatchee
|
|
del self.currentPatchVersion
|
|
decompressedMfname = os.path.splitext(self.currentMfname)[0]
|
|
localFilename = Filename(self.mfDir, Filename(decompressedMfname))
|
|
destDir = Filename(self.topDir)
|
|
self.extract(self.currentMfname, localFilename, destDir, self.updateMultifileDone)
|
|
|
|
def startReextractingFiles(self):
|
|
self.notify.info('startReextractingFiles: Reextracting ' + `(len(self.reextractList))` + ' files for multifile: ' + self.currentMfname)
|
|
self.launcherMessage(self.Localizer.LauncherRecoverFiles)
|
|
self.currentMfile = Multifile()
|
|
decompressedMfname = os.path.splitext(self.currentMfname)[0]
|
|
self.currentMfile.openRead(Filename(self.mfDir, Filename(decompressedMfname)))
|
|
self.reextractNextFile()
|
|
|
|
def reextractNextFile(self):
|
|
failure = 0
|
|
while not failure and len(self.reextractList) > 0:
|
|
currentReextractFile = self.reextractList.pop()
|
|
subfileIndex = self.currentMfile.findSubfile(currentReextractFile)
|
|
if subfileIndex >= 0:
|
|
destFilename = Filename(self.topDir, Filename(currentReextractFile))
|
|
result = self.currentMfile.extractSubfile(subfileIndex, destFilename)
|
|
if not result:
|
|
self.notify.warning('reextractNextFile: Failure on reextract.')
|
|
failure = 1
|
|
else:
|
|
self.notify.warning('reextractNextFile: File not found in multifile: ' + `currentReextractFile`)
|
|
failure = 1
|
|
|
|
if failure:
|
|
sys.exit()
|
|
self.notify.info('reextractNextFile: Done reextracting files for multifile: ' + `(self.currentPhase)`)
|
|
del self.currentMfile
|
|
self.updateMultifileDone()
|
|
|
|
def patchMultifile(self):
|
|
self.launcherMessage(self.Localizer.LauncherCheckUpdates % {'name': self.currentPhaseName,
|
|
'current': self.currentPhaseIndex,
|
|
'total': self.numPhases})
|
|
self.notify.info('patchMultifile: Checking for patches on multifile: ' + self.currentMfname)
|
|
self.patchList = []
|
|
clientMd5 = HashVal()
|
|
decompressedMfname = os.path.splitext(self.currentMfname)[0]
|
|
localFilename = Filename(self.mfDir, Filename(decompressedMfname))
|
|
clientMd5.hashFile(localFilename)
|
|
clientVer = self.dldb.getVersion(Filename(decompressedMfname), clientMd5)
|
|
if clientVer == 1:
|
|
self.patchAndHash()
|
|
return
|
|
elif clientVer == -1:
|
|
self.notify.info('patchMultifile: Invalid hash for file: ' + self.currentMfname)
|
|
self.maybeStartGame()
|
|
if self.curMultifileRetry < self.multifileRetries:
|
|
self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
|
|
self.curMultifileRetry += 1
|
|
self.notify.info('patchMultifile: Restarting download with newest multifile')
|
|
self.dldb.setClientMultifileIncomplete(self.currentMfname)
|
|
self.dldb.setClientMultifileSize(self.currentMfname, 0)
|
|
self.getMultifile(self.currentMfname)
|
|
else:
|
|
self.setPandaErrorCode(6)
|
|
self.notify.info('patchMultifile: Failed to download multifile')
|
|
sys.exit()
|
|
return
|
|
elif clientVer > 1:
|
|
self.notify.info('patchMultifile: Old version for multifile: ' + self.currentMfname + ' Client ver: ' + `clientVer`)
|
|
self.maybeStartGame()
|
|
self.totalPatchDownload = 0
|
|
self.patchDownloadSoFar = 0
|
|
for ver in xrange(1, clientVer):
|
|
patch = self.getPatchFilename(decompressedMfname, ver + 1)
|
|
patchee = decompressedMfname
|
|
patchVersion = ver + 1
|
|
self.patchList.append((patch, patchee, patchVersion))
|
|
if self.currentPhase == 3:
|
|
self.totalPatchDownload += self.getProgressSum(patch)
|
|
|
|
self.notify.info('total patch to be downloaded = ' + `(self.totalPatchDownload)`)
|
|
self.downloadPatches()
|
|
return
|
|
|
|
def patchAndHash(self):
|
|
self.reextractList = []
|
|
self.PAHClean = 1
|
|
self.PAHNumFiles = self.dldb.getServerNumFiles(self.currentMfname)
|
|
self.PAHFileCounter = 0
|
|
if self.PAHNumFiles > 0:
|
|
task = MiniTask(self.patchAndHashTask)
|
|
task.cleanCallback = self.updateMultifileDone
|
|
task.uncleanCallback = self.startReextractingFiles
|
|
self._addMiniTask(task, 'patchAndHash')
|
|
else:
|
|
self.updateMultifileDone()
|
|
|
|
def patchAndHashTask(self, task):
|
|
self.launcherMessage(self.Localizer.LauncherVerifyPhase)
|
|
if self.PAHFileCounter == self.PAHNumFiles:
|
|
if self.PAHClean:
|
|
task.cleanCallback()
|
|
else:
|
|
task.uncleanCallback()
|
|
return task.done
|
|
else:
|
|
i = self.PAHFileCounter
|
|
self.PAHFileCounter += 1
|
|
fname = self.dldb.getServerFileName(self.currentMfname, i)
|
|
fnameFilename = Filename(self.topDir, Filename(fname))
|
|
if not os.path.exists(fnameFilename.toOsSpecific()):
|
|
self.notify.info('patchAndHash: File not found: ' + fname)
|
|
self.reextractList.append(fname)
|
|
self.PAHClean = 0
|
|
return task.cont
|
|
if self.VerifyFiles and self.dldb.hasVersion(Filename(fname)):
|
|
clientMd5 = HashVal()
|
|
clientMd5.hashFile(fnameFilename)
|
|
clientVer = self.dldb.getVersion(Filename(fname), clientMd5)
|
|
if clientVer == 1:
|
|
return task.cont
|
|
else:
|
|
self.notify.info('patchAndHash: Invalid hash for file: ' + fname)
|
|
self.reextractList.append(fname)
|
|
self.PAHClean = 0
|
|
return task.cont
|
|
|
|
def launcherMessage(self, msg):
|
|
if msg != self.lastLauncherMsg:
|
|
self.lastLauncherMsg = msg
|
|
self.notify.info(msg)
|
|
|
|
def recordPeriodTimeRemaining(self, secondsRemaining):
|
|
self.setValue(self.PeriodTimeRemainingKey, int(secondsRemaining))
|
|
|
|
def recordPeriodName(self, periodName):
|
|
self.setValue(self.PeriodNameKey, periodName)
|
|
|
|
def recordSwid(self, swid):
|
|
self.setValue(self.SwidKey, swid)
|
|
|
|
def getGoUserName(self):
|
|
return self.goUserName
|
|
|
|
def setGoUserName(self, userName):
|
|
self.goUserName = userName
|
|
|
|
def getInstallDir(self):
|
|
return self.topDir.cStr()
|
|
|
|
def setPandaWindowOpen(self):
|
|
self.setValue(self.PandaWindowOpenKey, 1)
|
|
|
|
def setPandaErrorCode(self, code):
|
|
self.notify.info('setting panda error code to %s' % code)
|
|
self.pandaErrorCode = code
|
|
errorLog = open(self.errorfile, 'w')
|
|
errorLog.write(str(code) + '\n')
|
|
errorLog.flush()
|
|
errorLog.close()
|
|
|
|
def getPandaErrorCode(self):
|
|
return self.pandaErrorCode
|
|
|
|
def setDisconnectDetailsNormal(self):
|
|
self.notify.info('Setting Disconnect Details normal')
|
|
self.disconnectCode = 0
|
|
self.disconnectMsg = 'normal'
|
|
|
|
def setDisconnectDetails(self, newCode, newMsg):
|
|
self.notify.info('New Disconnect Details: %s - %s ' % (newCode, newMsg))
|
|
self.disconnectCode = newCode
|
|
self.disconnectMsg = newMsg
|
|
|
|
def setServerVersion(self, version):
|
|
self.ServerVersion = version
|
|
|
|
def getServerVersion(self):
|
|
return self.ServerVersion
|
|
|
|
def getIsNewInstallation(self):
|
|
result = self.getValue(self.NewInstallationKey, 1)
|
|
result = base.config.GetBool('new-installation', result)
|
|
return result
|
|
|
|
def setIsNotNewInstallation(self):
|
|
self.setValue(self.NewInstallationKey, 0)
|
|
|
|
def getLastLogin(self):
|
|
return self.getValue(self.LastLoginKey, '')
|
|
|
|
def setLastLogin(self, login):
|
|
self.setValue(self.LastLoginKey, login)
|
|
|
|
def setUserLoggedIn(self):
|
|
self.setValue(self.UserLoggedInKey, '1')
|
|
|
|
def setPaidUserLoggedIn(self):
|
|
self.setValue(self.PaidUserLoggedInKey, '1')
|
|
|
|
def getReferrerCode(self):
|
|
return self.getValue(self.ReferrerKey, None)
|
|
|
|
def getPhaseComplete(self, phase):
|
|
percentDone = self.phaseComplete[phase]
|
|
return percentDone == 100
|
|
|
|
def setPercentPhaseComplete(self, phase, percent):
|
|
self.notify.info('phase updating %s, %s' % (phase, percent))
|
|
oldPercent = self.phaseComplete[phase]
|
|
if oldPercent != percent:
|
|
self.phaseComplete[phase] = percent
|
|
messenger.send('launcherPercentPhaseComplete', [phase,
|
|
percent,
|
|
self.getBandwidth(),
|
|
self.byteRate])
|
|
percentPhaseCompleteKey = 'PERCENT_PHASE_COMPLETE_' + `phase`
|
|
self.setRegistry(percentPhaseCompleteKey, percent)
|
|
self.overallComplete = int(round(percent * self.phaseOverallMap[phase])) + self.progressSoFar
|
|
self.setRegistry('PERCENT_OVERALL_COMPLETE', self.overallComplete)
|
|
|
|
def getPercentPhaseComplete(self, phase):
|
|
return self.phaseComplete[phase]
|
|
dr = finalRequested - startRequested
|
|
if dt <= 0.0:
|
|
return -1
|
|
self.byteRate = db / dt
|
|
self.byteRateRequested = dr / dt
|
|
return self.byteRate
|
|
|
|
def addPhasePostProcess(self, phase, func, taskChain = 'default'):
|
|
if self.getPhaseComplete(phase):
|
|
func()
|
|
return
|
|
self.acceptOnce('phaseComplete-%s' % phase, func)
|
|
|
|
def testBandwidth(self):
|
|
self.recordBytesPerSecond()
|
|
byteRate = self.getBytesPerSecond()
|
|
if byteRate < 0:
|
|
return
|
|
if byteRate >= self.getBandwidth() * self.INCREASE_THRESHOLD:
|
|
self.increaseBandwidth(byteRate)
|
|
elif byteRate < self.byteRateRequested * self.DECREASE_THRESHOLD:
|
|
self.decreaseBandwidth(byteRate)
|
|
|
|
def getBandwidth(self):
|
|
if self.backgrounded:
|
|
bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex] - self.TELEMETRY_BANDWIDTH
|
|
else:
|
|
bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex]
|
|
if self.MAX_BANDWIDTH > 0:
|
|
bandwidth = min(bandwidth, self.MAX_BANDWIDTH)
|
|
return bandwidth
|
|
|
|
def increaseBandwidth(self, targetBandwidth = None):
|
|
maxBandwidthIndex = len(self.BANDWIDTH_ARRAY) - 1
|
|
if self.bandwidthIndex == maxBandwidthIndex:
|
|
self.notify.debug('increaseBandwidth: Already at maximum bandwidth')
|
|
return 0
|
|
self.bandwidthIndex += 1
|
|
self.everIncreasedBandwidth = 1
|
|
self.setBandwidth()
|
|
return 1
|
|
|
|
def decreaseBandwidth(self, targetBandwidth = None):
|
|
if not self.DECREASE_BANDWIDTH:
|
|
return 0
|
|
if self.backgrounded and self.everIncreasedBandwidth:
|
|
return 0
|
|
if self.bandwidthIndex == 0:
|
|
return 0
|
|
else:
|
|
self.bandwidthIndex -= 1
|
|
if targetBandwidth:
|
|
while self.bandwidthIndex > 0 and self.BANDWIDTH_ARRAY[self.bandwidthIndex] > targetBandwidth:
|
|
self.bandwidthIndex -= 1
|
|
|
|
self.setBandwidth()
|
|
return 1
|
|
|
|
def setBandwidth(self):
|
|
self.resetBytesPerSecond()
|
|
self.httpChannel.setMaxBytesPerSecond(self.getBandwidth())
|
|
|
|
def resetBytesPerSecond(self):
|
|
self.bpsList = []
|
|
|
|
def recordBytesPerSecond(self):
|
|
bytesDownloaded = self.httpChannel.getBytesDownloaded()
|
|
bytesRequested = self.httpChannel.getBytesRequested()
|
|
t = self.getTime()
|
|
self.bpsList.append((t, bytesDownloaded, bytesRequested))
|
|
while 1:
|
|
if len(self.bpsList) == 0:
|
|
break
|
|
ft, fb, fr = self.bpsList[0]
|
|
if ft < t-self.BPS_WINDOW:
|
|
self.bpsList.pop(0)
|
|
else:
|
|
break
|
|
|
|
def getBytesPerSecond(self):
|
|
if len(self.bpsList) < 2:
|
|
return -1
|
|
startTime, startBytes, startRequested = self.bpsList[0]
|
|
finalTime, finalBytes, finalRequested = self.bpsList[-1]
|
|
dt = finalTime - startTime
|
|
db = finalBytes - startBytes
|
|
dr = finalRequested - startRequested
|
|
if dt <= 0.0:
|
|
return -1
|
|
self.byteRate = db / dt
|
|
self.byteRateRequested = dr / dt
|
|
return self.byteRate
|
|
|
|
def testBandwidth(self):
|
|
self.recordBytesPerSecond()
|
|
byteRate = self.getBytesPerSecond()
|
|
if byteRate < 0:
|
|
return
|
|
if byteRate >= self.getBandwidth() * self.INCREASE_THRESHOLD:
|
|
self.increaseBandwidth(byteRate)
|
|
elif byteRate < self.byteRateRequested * self.DECREASE_THRESHOLD:
|
|
self.decreaseBandwidth(byteRate)
|
|
|
|
def getBandwidth(self):
|
|
if self.backgrounded:
|
|
bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex] - self.TELEMETRY_BANDWIDTH
|
|
else:
|
|
bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex]
|
|
if self.MAX_BANDWIDTH > 0:
|
|
bandwidth = min(bandwidth, self.MAX_BANDWIDTH)
|
|
return bandwidth
|
|
|
|
def increaseBandwidth(self, targetBandwidth = None):
|
|
maxBandwidthIndex = len(self.BANDWIDTH_ARRAY) - 1
|
|
if self.bandwidthIndex == maxBandwidthIndex:
|
|
return 0
|
|
self.bandwidthIndex += 1
|
|
self.everIncreasedBandwidth = 1
|
|
self.setBandwidth()
|
|
return 1
|
|
|
|
def decreaseBandwidth(self, targetBandwidth = None):
|
|
if not self.DECREASE_BANDWIDTH:
|
|
return 0
|
|
if self.backgrounded and self.everIncreasedBandwidth:
|
|
return 0
|
|
if self.bandwidthIndex == 0:
|
|
return 0
|
|
else:
|
|
self.bandwidthIndex -= 1
|
|
if targetBandwidth:
|
|
while self.bandwidthIndex > 0 and self.BANDWIDTH_ARRAY[self.bandwidthIndex] > targetBandwidth:
|
|
self.bandwidthIndex -= 1
|
|
|
|
self.setBandwidth()
|
|
return 1
|
|
|
|
def setBandwidth(self):
|
|
self.resetBytesPerSecond()
|
|
self.httpChannel.setMaxBytesPerSecond(self.getBandwidth())
|
|
|
|
def MakeNTFSFilesGlobalWriteable(self, pathToSet = None):
|
|
if not self.WIN32:
|
|
return
|
|
import win32api
|
|
if pathToSet == None:
|
|
pathToSet = self.getInstallDir()
|
|
else:
|
|
pathToSet = pathToSet.cStr() + '*'
|
|
DrivePath = pathToSet[0:3]
|
|
try:
|
|
volname, volsernum, maxfilenamlen, sysflags, filesystemtype = win32api.GetVolumeInformation(DrivePath)
|
|
except:
|
|
return
|
|
|
|
if self.win32con_FILE_PERSISTENT_ACLS & sysflags:
|
|
self.notify.info('NTFS detected, making files global writeable\n')
|
|
win32dir = win32api.GetWindowsDirectory()
|
|
cmdLine = win32dir + '\\system32\\cacls.exe "' + pathToSet + '" /T /E /C /G Everyone:F > nul'
|
|
os.system(cmdLine)
|
|
return
|
|
|
|
def cleanup(self):
|
|
self.notify.info('cleanup: cleaning up Launcher')
|
|
self.ignoreAll()
|
|
del self.clock
|
|
del self.dldb
|
|
del self.httpChannel
|
|
del self.http
|
|
|
|
def scanForHacks(self):
|
|
if not self.WIN32:
|
|
return
|
|
import _winreg
|
|
hacksInstalled = {}
|
|
hacksRunning = {}
|
|
hackName = ['!xSpeed.net', 'A Speeder', 'Speed Gear']
|
|
knownHacksRegistryKeys = {
|
|
hackName[0] : [
|
|
[_winreg.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\Run\\!xSpeed'],
|
|
[_winreg.HKEY_CURRENT_USER, 'Software\\!xSpeednethy'],
|
|
[_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Start Menu\\Programs\\!xSpeednet'],
|
|
[_winreg.HKEY_LOCAL_MACHINE, 'Software\\Gentee\\Paths\\!xSpeednet'],
|
|
[_winreg.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\!xSpeed.net 2.0']],
|
|
hackName[1] : [
|
|
[_winreg.HKEY_CURRENT_USER, 'Software\\aspeeder'],
|
|
[_winreg.HKEY_LOCAL_MACHINE, 'Software\\aspeeder'],
|
|
[_winreg.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\aspeeder']]
|
|
}
|
|
try:
|
|
for prog in knownHacksRegistryKeys.keys():
|
|
for key in knownHacksRegistryKeys[prog]:
|
|
try:
|
|
h = _winreg.OpenKey(key[0], key[1])
|
|
hacksInstalled[prog] = 1
|
|
_winreg.CloseKey(h)
|
|
break
|
|
except:
|
|
pass
|
|
except:
|
|
pass
|
|
knownHacksMUI = {'!xspeednet': hackName[0], 'aspeeder': hackName[1], 'speed gear': hackName[2]}
|
|
i = 0
|
|
try:
|
|
rh = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache')
|
|
while 1:
|
|
name, value, type = _winreg.EnumValue(rh, i)
|
|
i += 1
|
|
if type == 1:
|
|
val = value.lower()
|
|
for hackprog in knownHacksMUI:
|
|
if val.find(hackprog) != -1:
|
|
hacksInstalled[knownHacksMUI[hackprog]] = 1
|
|
break
|
|
_winreg.CloseKey(rh)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
import otp.launcher.procapi
|
|
except:
|
|
pass
|
|
else:
|
|
knownHacksExe = {'!xspeednet.exe': hackName[0], 'aspeeder.exe': hackName[1], 'speedgear.exe': hackName[2]}
|
|
try:
|
|
for p in procapi.getProcessList():
|
|
pname = p.name
|
|
if pname in knownHacksExe:
|
|
hacksRunning[knownHacksExe[pname]] = 1
|
|
except:
|
|
pass
|
|
|
|
if len(hacksInstalled) > 0:
|
|
self.notify.info("Third party programs installed:")
|
|
for hack in hacksInstalled.keys():
|
|
self.notify.info(hack)
|
|
|
|
if len(hacksRunning) > 0:
|
|
self.notify.info("Third party programs running:")
|
|
for hack in hacksRunning.keys():
|
|
self.notify.info(hack)
|
|
self.setPandaErrorCode(8)
|
|
sys.exit()
|
|
|
|
def getBlue(self):
|
|
return None
|
|
|
|
def getPlayToken(self):
|
|
return None
|
|
|
|
def getDISLToken(self):
|
|
DISLToken = self.getValue(self.DISLTokenKey)
|
|
self.setValue(self.DISLTokenKey, '')
|
|
if DISLToken == 'NO DISLTOKEN':
|
|
DISLToken = None
|
|
return DISLToken
|