从年初就没玩WLK了,不过听说国服的魔兽世界要升级到CTM(国服叫“大地的裂变”)了,还是很早就下载了那个6.25G的升级补丁。期间看到NGA论坛很多人安装那些偷跑的第二部分补丁提前安装客户端。当时也没在意,心想到时候升级应该也没啥问题,升级那么多次了都。
谁想到,原来在这个新的资料片,暴雪把整个游戏文件都重构了,比如把之前很多文件都合为一个文件等。文件结构确实清爽了很多,但是这也加大了整个升级过程的工作量。特别坑爹的是升级的时候还需要联网和服务器验证和下载一些东西。中国的网络环境,再加上那几个升级服务器不给力(据说server2的文件本身就是错的),导致了太多更新失败!
俺也是其中之一。开服当天晚上都没更新成功,然后终于在第二天在一个帖子里面知道了Blizzard Updater的工作原理(以下是我发到知乎上面的):
这次的更新暴雪一个让人痛恨的地方是不能断点续传。如果失败了,之前更新成功的文件(都是.temp结尾的)会全部删除,然后重新开始。所以就有一种偷天 换日的办法,在显示安装完一个文件后马上把它复制出去。然后下次重装的时候,等更新器生成那些.temp文件并在进度到8%之前覆盖它们
这个脚本其实好早就想发出来了。但是实际上当时我完成更新的时候,写的脚本只是做了最简单的一步,就是打印出来Blizzard Updater上面的进度和正在安装的文字信息,这样之后就可以知道哪些需要备份,哪些正在进行中,然后手工备份的(太想早点弄好,所以奉行“够用就行”的原则)。
等待CTM安装更新的时候就粗略写了自动备份的功能,不过相当不成熟。其实我本来想写个完全傻瓜版(自动搜索WoW路径然后启动Blizzard Updater,自动备份,出错后自动重新开始更新并同时使用备份文件覆盖,然后循环直到成功)的发到NGA给大家用的,不过那个太耗时间就放弃了。。。
而且现在发出来的备份功能也没有实际测试过,只是自己检查代码几次后的成果(因为文件都更新掉了……)。
估计现在还没更新好的也是极少数了吧(从游戏人数就能看到,开服第二天深夜我登录的时候服务器上的人寥寥无几,第三天以后就越来越多了),用我这个脚本的可能性不会很大,呵呵。
脚本使用须知:
- 只供学习研究使用,后果自负,特别是备份功能。(实际上只有一处调用到删除文件操作;在cleanCp方法里面,对备份目录的文件进行的操作,可以把那行os.remove开头的删掉)
- 修改最开始的wowPath(魔兽3.3.5游戏目录)和backupPath(备份用的目录,最好找个不容易出错的地方)为自己对应的目录
- 如果只想用到查看更新进度功能,把从#back up files到最后后面的都删掉就行了
- 备份出来的文件都统一放在backupPath里面,需要手动覆盖到游戏目录(wowPath)里面:文件名有zhCN的放到Data\zhCN下面,enCN同理;其它放Data目录
脚本基本思路:
使用pywin32模块得到窗口标题栏及窗口文字内容,然后就是根据这些的扩展功能(备份)。就这么简单,本人也就Python初级水平,见笑了。
Python脚本:
# encoding: utf-8
# author: Sean Wang : weibo.com/fclef
from __future__ import unicode_literals
import win32gui
import pywintypes
import time
import os
import subprocess
from sys import getfilesystemencoding
# change below two paths to your own
wowPath = r'E:\World Of Warcraft' # WoW install path
backupPath = r'E:\Games setup\CTMbackup' # path to back up update files CAUTION:use a safe place to store!
ENCODING=getfilesystemencoding()
def getBUWin():
def callback(hwnd,allWin):
winText=win32gui.GetWindowText(hwnd).decode(ENCODING)
# search for Blizzard Updater, get handler and title text
if winText.find('Blizzard')>0:
allWin.append(hwnd)
allWin.append(winText)
return True
BlizWin = []
try:
win32gui.EnumWindows(callback,BlizWin)
except pywintypes.errors, wte:
print wte
return BlizWin
def getAbsSrcPath(mpqFName):
"""helper method. get absolute path to the mpq file name"""
assert mpqFName.upper.endswith("MPQ"), "%s is not a valid mpq filename"%mpqFName
if mpqFName.find('zhCN') >=0:
return os.path.join(wowPath,'Data','zhCN.temp',mpqFName+'.temp')
elif mpqFName.find('enCN')>=0:
return os.path.join(wowPath,'Data','enCN.temp',mpqFName+'.temp')
else:
return os.path.join(wowPath,'Data',mpqName+'.temp')
def backUp(srcAbsFPath):
"""helper method. return Popen instance of copy command"""
assert os.path.isfile(srcAbsFPath), "source mpq file %r does not exist!"%srcAbsFPath
srcSize=os.stat(srcAbsFPath).st_size
destFile=os.path.join(backupPath,src)
# compare the two files.
# Use file size actually seems to be non-sense in Windows since even if
# copy failed, the size would be also the same
if os.path.exists(existFile) and os.stat(destFile).st_size==srcSize:
print '%s already exists and size is the same.Ignored.'%srcAbsFPath
return None
else:
print 'Start backuping...'
cpCmd="copy /y %s %s"%(src,backupPath)
return subprocess.Popen(cpCmd,shell=True,
stdout=open(os.devnull,'w'),stderr=subprocess.PIPE)
def cleanCp(cpStat):
if cpStat:
for p in cpStat:
if p and p.poll() is None:
p.kill()
#below part is the only place where this script would delete
#your files, use with caution!!! backupPath should be a safe
#place to do deletion task
os.remove(os.path.join(backupPath,cpStat[p]))
elif p.poll() != 0:
print '%s failed to backup'%cpStat[p]
print p.stderr
if __name__ == '__main__':
contents=progress=toBackUp=[]
cpStat={}
lastBak=''
while True:
# get progress status on window title
if getBUWin():
window,title=getBUWin()
else:
cleanCp(cpStat)
print "Error occurred. Could not find Blizzard Update window. Check if you got failed update or you forgot to launch it"
break
if title not in progress:
progress.append(title)
print "%s : %s"%(time.strftime('%H:%M:%S'),progress[-1])
# get file process status displayed on programme
try:
control= win32gui.FindWindowEx(window,0,"static",None)
except pywintypes.errors:
if progress[-1].find('100%') >=0:
print 'Update Done.'
else:
cleanCp(cpStat)
print "Error occurred. Could not find Blizzard Update window. Check if you got failed update or you forgot to launch it"
break
content=win32gui.GetWindowText(control).decode(ENCODING)
if content not in contents:
contents.append(content)
print "%s : %s"%(time.strftime('%H:%M:%S'),contents[-1])
#back up files
#do not backup temppatch-2.MPQ as we need time to copy those backup files after BU updater started
toBackup=[ i for i in contents if i !=contents[-1] and i.find('temppatch-2.MPQ')=0:
lastBak=toBackup[-1]
mpqFile=toBackup[-1].split('"')[1]
if mpqFile.endswith('.MPQ'):
mpqSrcPath= getAbsSrcPath(mpqFile)
cpStat.setdefault(backup(mpqSrcPath),mpqFile+'.temp')
上两张图,当时截的(未使用备份功能):
更新过程的一个截图,这已经是因为出错而重复更新的第2还是第3次了,不过有了备份文件,重新更新的速度会快很多(可以参看第二幅更新完成的图,后来加了时间戳就很明显了)
这个是更新成功以后的图,更新完以后提示我还要下载2G,还好不是杯具的10G党。。。其实只要Launcher上显示第二部分已经完成就可以进游戏了的,当时不知道,在还剩500M左右的时候我终于忍不住试了下,真的可以进去游戏了。

