2013-02-15 289 views
2

我是一个MMORPG私人服务器开发人员,我期待为用户的客户端创建一个全能的更新程序,因为使用必须手动下载的修补程序非常烦人和跛脚。游戏客户端更新

我是C#中的新手,但我已经成功地使用我自己的界面和基本游戏开始/选项按钮制作自己的启动程序,并注意从我的网络服务器读取。

现在,我想为此做一个集成的更新函数,我很迷茫,我不知道从哪里开始。这是它会是什么样子,它只是一个概念

enter image description here

这将有主按钮至极用于当你打开程序的按钮,会写,开始游戏和更新,基本上是“UPDATE “并且被禁用(当它搜索新的更新时),并且如果找到任何更新,它将变成可点击的按钮,然后在更新被下载之后它将自己变成”开始游戏“。

进度条用于整体更新,另一个用于查看仅下载的当前文件的进度,所有与基本信息(如百分比)以及需要下载多少文件有关的进度。

我需要找到一种方法让启动程序通过HTTP方法检查网络服务器上的文件,并检查它们是否与客户端或更新的相同,因此它总是重新下载已经相同版本的文件以及方法,更新程序会将更新下载为压缩存档并自动提取并在完成下载时覆盖现有文件。

注:该文件正在更新不.EXE,他们大多是纹理/配置文件/地图/图片/等..

+1

请不要通过HTTP下载可执行文件,然后运行它们。HTTP对MitM来说非常简单。 – SecurityMatt 2013-02-15 03:20:19

+1

不,更新的文件是纹理,三维模型和其他类型的东西,即使有,它也会被压缩。 – Dan 2013-02-15 03:22:09

+2

@ user2030800:被压缩不会防止攻击。你需要加密 - https或公钥加密。 – 2013-02-15 03:29:13

回答

4

我会为这个系统绘制一个可能的架构。这是不完整的,你应该认为它是上半部分的一个详细的伪C#代码的形式,以及第二部分的一组提示和建议。

我相信你可能需要为这个两个应用:

  • A C#的WinForms客户端。
  • C#服务器端应用程序,也许是一个Web服务。

我不会关注这个答案的安全问题,但它们显然非常重要。我希望安全可以在更高层次上实现,可能使用SSL。 Web服务将在IIS中运行,并且实现某种形式的安全性应该主要是配置问题。

服务器端的部分不是严格要求的,特别是如果你不想压缩;可能有一种方法来配置您的服务器,以便在website.com/updater发出HTTP请求时返回一个可轻松解析的文件列表。但是,拥有Web服务会更加灵活,并且可能更容易实现。您可以先看看this MSDN article。如果您确实需要压缩,则可以将服务器配置为透明地压缩单个文件。我会尽力勾画出所有可能的变体。

在单个更新ZIP文件的情况下,基本上更新程序Web服务应该能够回答两个不同的请求;首先,它可以返回与服务器目录website.com/updater相关的所有游戏文件的列表以及它们的最后写入时间戳(在Web服务中的GetUpdateInfo方法)。客户端会将此列表与本地文件进行比较;某些文件可能不再存在于服务器上(可能客户端必须删除本地副本),有些文件可能不存在于客户端(它们是全新的内容),并且客户端和服务器上可能存在其他一些文件,在这种情况下,客户端需要检查最后写入时间以确定是否需要更新版本。客户端将建立这些文件相对于游戏内容目录的路径列表。游戏内容目录应镜像服务器website.com/updater目录。

其次,客户端将此列表发送到服务器(GetUpdateURL在Web服务中)。服务器将创建一个包含更新的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文件。要更新进度条(在此设置中只有一个进度条,请查看我对您问题的评论),您需要创建一个BackgroundWorkerThis articlethis 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这样的服务器可以免费提供这样一个列表,不过通常它可以用于人类消费,但是它仍然可以通过程序轻松解析。我确定必须有一些脚本/扩展名才能让这个列表以更加机器友好的方式进行格式化。

还有另一种方法来获得该列表;您可以在服务器上拥有一个文本文件,对于每个游戏内容文件,都会存储其最后一次写入时间,或者甚至可能更好的是存储渐进版本号。您可以比较版本号而不是日期来检查您需要的文件。这将防止您的时区问题。但是,在这种情况下,您需要维护此列表的本地副本,因为文件没有发行号,但只有名称和一组日期。

+0

伟大的答案,+1。 – Brian 2013-02-15 05:09:01

+0

@Brian:谢谢:-) – damix911 2013-02-15 05:31:30

+0

感谢您的非常详细的解释,这对我来说是一个巨大的帮助,我会详细检查它并尝试通过不同的方法来更新我的更新。谢谢你的时间先生! – Dan 2013-02-15 06:15:21

0

这是一个广泛和多样的问题,有几个答案可以被称为'正确'取决于各种实施要求。这里有一些想法...

  1. 我的方法是使用System.Security.Cryptography.SHA1生成每个游戏资产的散列码列表。然后,更新程序可以下载列表,将其与本地文件系统进行比较(缓存本地生成的哈希以提高效率)并构建要下载的新/更改文件的列表。

  2. 如果游戏数据使用档案,则该过程会更复杂一些,因为当只有一个小文件可能已被更改时,您不想下载大型档案。在这种情况下,您希望对存档中的每个文件进行散列,并提供一种用于下载这些包含文件的方法,然后使用从服务器下载的文件更新存档。

  3. 最后,在可能的情况下通过下载较小的补丁文件来考虑使用二进制Diff/Patch算法来减少带宽需求。在这种情况下,客户端会请求一个从当前版本更新到最新版本文件的补丁,发送本地文件的散列,以便服务器知道要发送哪个补丁。这要求您在服务器上为您希望能够修补的每个先前版本维护一堆修补程序,这可能比您感兴趣的要多。

这里有一些链接,可能是相关的:

哦,并考虑使用mult i-part下载器,以更好地饱和客户端的可用带宽。这会导致服务器负载较高,但可以大大提高客户端体验。

+0

谢谢你这个失落的想法,我会检查他们,并尝试:)非常感谢! – Dan 2013-02-15 06:16:14