我是一个MMORPG私人服务器开发者,我期待为用户的客户创建一个多功能的更新程序,因为使用必须手动下载的补丁非常烦人和蹩脚。
我是C#的新手,但我已经成功使用自己的界面和基本的游戏开始/选项按钮制作我自己的启动器,并注意从我的网络服务器读取。
现在,我想为此制作一个集成的更新功能,我很丢失,我不知道从哪里开始。这就是它的样子,它只是一个概念
它将有用于启动游戏并更新它的主按钮,基本上当你打开程序时按钮会写“更新”并被禁用(当它搜索新的更新时),如果找到任何,它会变成一个可点击的按钮,然后在下载更新之后,它就会变成“开始游戏”。
整体更新的进度条,另一个用于查看当前正在下载的文件的进度,所有这些都包含基本信息,如百分比和需要下载的文件数量。
我需要找到一种方法让启动程序通过HTTP方法检查Web服务器上的文件,并检查它们是否与客户端或更新版本相同,因此它总是重新下载已经相同版本的文件以及方法以便更新程序将下载更新作为压缩存档,并在下载完成后自动提取并覆盖现有文件。
注意:正在更新的文件不是.exe,它们主要是纹理/配置文件/地图/图像等等......
答案 0 :(得分:4)
我将简要介绍该系统的可能架构。它不完整,您应该将其视为上半部分详细的伪C#代码形式,以及第二部分的一组提示和建议。
我相信您可能需要两个申请:
我不会关注这个答案的安全问题,但它们显然非常重要。我希望可以在更高级别实现安全性,可能使用SSL。 Web服务将在IIS中运行,实现某种形式的安全性应主要是配置问题。
服务器端部分并非严格要求,特别是如果您不想要压缩;可能有一种方法可以配置您的服务器,以便在 website.com/updater 上发出HTTP请求时返回一个易于解析的文件列表。但是,拥有Web服务更灵活,并且可能更容易实现。您可以先查看this MSDN article。如果您确实需要压缩,则可以将服务器配置为透明地压缩单个文件。我将尝试绘制所有可能的变体。
对于单个更新ZIP文件,基本上更新程序Web服务应该能够回答两个不同的请求;首先,它可以返回所有游戏文件的列表,相对于服务器目录 website.com/updater ,以及它们在Web服务中的最后写入时间戳( GetUpdateInfo 方法) )。客户端会将此列表与本地文件进行比较;某些文件可能不再存在于服务器上(可能客户端必须删除本地副本),某些文件可能不存在于客户端上(它们完全是新内容),并且客户端和服务器上可能存在其他一些文件,在这种情况下,客户端需要检查上次写入时间以确定它是否需要更新版本。客户端将构建相对于游戏内容目录的这些文件的路径列表。游戏内容目录应该镜像服务器 website.com/updater 目录。
其次,客户端将此列表发送到服务器(Web服务中的 GetUpdateURL )。服务器将创建一个包含更新的ZIP,并使用其URL进行回复。
[ServiceContract]
public interface IUpdater
{
[OperationContract]
public FileModified[] GetUpdateInfo();
[OperationContract]
public string GetUpdateURL();
}
[DataContract]
public class FileModified
{
[DataMember]
public string Path;
[DataMember]
public DateTime Modified;
}
public class Updater : IUpdater
{
public FileModified[] GetUpdateInfo()
{
// Get the physical directory
string updateDir = HostingEnvironment.MapPath("website.com/updater");
IList<FileModified> updateInfo = new List<FileModified>();
foreach (string path in Directory.GetFiles(updateDir))
{
FileModified fm = new FileModified();
fm.Path = // You may need to adjust path so that it is local with respect to updateDir
fm.Modified = new FileInfo(path).LastWriteTime;
updateInfo.Add(fm);
}
return updateInfo.ToArray();
}
[OperationContract]
public string GetUpdateURL(string[] files)
{
// You could use System.IO.Compression.ZipArchive and its
// method CreateEntryFromFile. You create a ZipArchive by
// calling ZipFile.Open. The name of the file should probably
// be unique for the update session, to avoid that two concurrent
// updates from different clients will conflict. You could also
// cache the ZIP packages you create, in a way that if a future
// update requires the same exact file you would return the same
// ZIP.
// You have to return the URL of the ZIP, not its local path on the
// server. There may be several ways to do this, and they tend to
// depend on the server configuration.
return urlOfTheUpdate;
}
}
客户端将使用 HttpWebRequest 和 HttpWebResponse 对象下载ZIP文件。要更新进度条(此设置中只有一个进度条,请检查我对您的问题的评论),您需要创建 BackgroundWorker 。 This article和this other article涵盖了相关方面(遗憾的是,该示例是用VB.NET编写的,但它看起来与C#中的内容非常相似)。要提前进度条,您需要跟踪收到的字节数:
int nTotalRead = 0;
HttpWebRequest theRequest;
HttpWebResponse theResponse;
...
byte[] readBytes = new byte[1024];
int bytesRead = theResponse.GetResponseStream.Read(readBytes, 0, 4096);
nTotalRead += bytesread;
int percent = (int)((nTotalRead * 100.0) / length);
收到文件后,您可以使用 System.IO.Compression.ZipArchive.ExtractToDirectory 来更新游戏。
如果您不想使用.NET显式压缩文件,您仍然可以使用Web服务的第一种方法获取更新文件列表,并使用以下方法复制客户端所需的文件。每个 HttpWebRequest / HttpWebResponse 对。这样你实际上可以有两个进度条。计算文件的那个将简单地设置为百分比,如:
int filesPercent = (int)((nCurrentFile * 100.0) / nTotalFiles);
如果您有其他方式获取列表,您甚至不需要网络服务。
如果您想单独压缩文件,但服务器无法自动实现此功能,则应使用此界面定义Web服务:
[ServiceContract]
public interface IUpdater
{
[OperationContract]
public FileModified[] GetUpdateInfo();
[OperationContract]
public string CompressFileAndGetURL(string path);
}
您可以要求服务器压缩特定文件并返回压缩单文件存档的URL。
修改 - 重要
特别是在您的更新非常频繁的情况下,您需要特别注意时区。
修改 - 替代
我应该重申,这里的一个主要问题是从服务器获取当前版本中的文件列表;此文件应包含每个文件的最后写入时间。像Apache这样的服务器可以免费提供这样的列表,虽然它通常用于人类消费,但它很容易被程序解析。我确定必须有一些脚本/扩展名才能以更加机器友好的方式格式化该列表。
获得该清单还有另一种方法;你可以在服务器上有一个文本文件,对于每个游戏内容文件,它存储它的最后写入时间,或者甚至更好,存储一个渐进版本号。您可以比较版本号而不是日期来检查您需要哪些文件。这会使你自己从时区问题中解脱出来。但是,在这种情况下,您需要维护此列表的本地副本,因为文件没有版本号,只有一个名称和一组日期。
答案 1 :(得分:1)
这是一个广泛而多样的问题,根据各种实施要求,可以将几个答案称为“正确”。以下是一些想法......
我的方法是使用System.Security.Cryptography.SHA1
生成每个游戏资产的哈希码列表。然后,更新程序可以下载列表,将其与本地文件系统进行比较(缓存本地生成的哈希以提高效率)并构建要下载的新/已更改文件的列表。
如果游戏数据使用档案,则该过程会涉及更多,因为当内部只有一个小文件可能已被更改时,您不想下载大型档案。在这种情况下,您需要散列存档中的每个文件,并提供下载这些包含文件的方法,然后使用从服务器下载的文件更新存档。
最后,考虑使用二进制Diff / Patch算法,尽可能通过下载较小的补丁文件来减少带宽需求。在这种情况下,客户端将请求从当前版本更新到文件的最新版本的补丁,发送本地文件的散列,以便服务器知道要发送哪个补丁。这要求您在服务器上为您希望能够修补的每个先前版本维护一堆补丁,这可能超出您的兴趣。
以下是一些可能相关的链接:
哦,考虑使用多部分下载器来更好地使客户端的可用带宽饱和。这会导致服务器上的负载更高,但可以极大地改善客户端体验。