<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_Badkeeper‘s Home</title><subtitle type="text">关注网络游戏开发</subtitle><id>http://feed.cnblogs.com/blog/u/9165/rss</id><updated>2012-01-03T14:16:21Z</updated><author><name>BadKeeper</name><uri>http://www.cnblogs.com/badkeeper/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/badkeeper/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/9165/rss"/><entry><id>http://www.cnblogs.com/badkeeper/articles/2311227.html</id><title type="text">利用PythonQT，定制服务器启动工具</title><summary type="text">在开发过程中经常需要修改游戏服务器的各种配置参数，以方便在冒烟环境，开发环境和预发布环境中进行调试。我们以前使用ini文件进行服务器配置，所以经常需要关掉服务器，修改ini文件，然后再重新把服务器拉起来。最近有点时间，就开始考虑做个服务器启动小工具，能方便服务器在不同环境下的部署。 开始想用QT来写这个小工具，但发现建立一个QT工程其实有点小麻烦，后来干脆使用PythonQT来做。一个100多行的小脚本就能满足需求，以后交给其他人扩展功能也很方便。 主要是想做两个功能 1：可以方便的对服务器在不同环境下进行部署 2：可以在服务器启动时设置系统时间，因为经常需要测试一些时间性的业务。比如元旦..</summary><published>2012-01-03T10:54:00Z</published><updated>2012-01-03T10:54:00Z</updated><author><name>BadKeeper</name><uri>http://www.cnblogs.com/badkeeper/</uri></author><link rel="alternate" href="http://www.cnblogs.com/badkeeper/articles/2311227.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/badkeeper/articles/2311227.html"/><content type="html">&lt;p&gt;在开发过程中经常需要修改游戏服务器的各种配置参数，以方便在冒烟环境，开发环境和预发布环境中进行调试。我们以前使用ini文件进行服务器配置，所以经常需要关掉服务器，修改ini文件，然后再重新把服务器拉起来。最近有点时间，就开始考虑做个服务器启动小工具，能方便服务器在不同环境下的部署。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;开始想用QT来写这个小工具，但发现建立一个QT工程其实有点小麻烦，后来干脆使用PythonQT来做。一个100多行的小脚本就能满足需求，以后交给其他人扩展功能也很方便。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;主要是想做两个功能&lt;/p&gt; &lt;p&gt;1：可以方便的对服务器在不同环境下进行部署&lt;/p&gt; &lt;p&gt;2：可以在服务器启动时设置系统时间，因为经常需要测试一些时间性的业务。比如元旦节的活动。以前都是通过改操作系统时间来测的，这样每台机器就只能启动一个服务器了，如果想测两个不同时间的业务，就要多找一台机器。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/badkeeper/201201/201201031927512994.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/badkeeper/201201/201201031927553437.png" width="272" height="361"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;下面是代码，可以很简单的修改成自己需要的工具&lt;/p&gt; &lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&lt;pre&gt;&lt;img id="Code_Closed_Image_655177" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_655177').style.display='none'; document.getElementById('Code_Open_Image_655177').style.display='inline'; document.getElementById('Code_Open_Text_655177').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width="11" height="16"&gt;&lt;img style="display: none" id="Code_Open_Image_655177" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_655177').style.display='none'; getElementById('Code_Closed_Image_655177').style.display='inline'; getElementById('Code_Closed_Text_655177').style.display='inline';" align="top" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width="11" height="16"&gt;&lt;span id="Code_Closed_Text_655177" &gt;&lt;/span&gt;&lt;span style="display: none" id="Code_Open_Text_655177"&gt;#!/usr/bin/python&#xD;
 #-*- coding: utf-8 -*-&#xD;
&#xD;
"&lt;span style="color: #8b0000"&gt;&lt;/span&gt;""&#xD;
ZetCode PyQt4 tutorial &#xD;
&#xD;
In this example, we create a skeleton&#xD;
of a calculator using a QtGui.QGridLayout.&#xD;
&#xD;
author: Jan Bodnar&#xD;
website: zetcode.com &#xD;
last edited: October 2011&#xD;
"&lt;span style="color: #8b0000"&gt;&lt;/span&gt;""&#xD;
&#xD;
&lt;span style="color: #0000ff"&gt;import&lt;/span&gt; &lt;span style="color: #0000ff"&gt;sys&lt;/span&gt;&#xD;
&lt;span style="color: #0000ff"&gt;import&lt;/span&gt; &lt;span style="color: #0000ff"&gt;time&lt;/span&gt;&#xD;
&lt;span style="color: #0000ff"&gt;import&lt;/span&gt; threading&#xD;
&#xD;
from PyQt4 &lt;span style="color: #0000ff"&gt;import&lt;/span&gt; QtGui,QtCore&#xD;
&#xD;
from xml.dom.minidom &lt;span style="color: #0000ff"&gt;import&lt;/span&gt; parse,parseString&#xD;
&lt;span style="color: #0000ff"&gt;import&lt;/span&gt; &lt;span style="color: #0000ff"&gt;os&lt;/span&gt;&#xD;
&#xD;
&lt;span style="color: #0000ff"&gt;version&lt;/span&gt; = u"&lt;span style="color: #8b0000"&gt;服务器启动工具 V0.1&lt;/span&gt;"&#xD;
&#xD;
&lt;span style="color: #0000ff"&gt;class&lt;/span&gt; CommandThread(threading.Thread):&#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; setCommand(self,command):&#xD;
        self.command = command&#xD;
        &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; run(self):&#xD;
        &lt;span style="color: #0000ff"&gt;os&lt;/span&gt;.system(self.command)&#xD;
 &#xD;
&lt;span style="color: #0000ff"&gt;class&lt;/span&gt; Example(QtGui.QWidget):&#xD;
    &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; __init__(self):&#xD;
        super(Example, self).__init__()&#xD;
        &#xD;
        self.initUI()&#xD;
        &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; initUI(self):&#xD;
        label1  = QtGui.QLabel(self)&#xD;
        label1.setText(u'选择服务器执行文件')&#xD;
        label1.move(20,20)&#xD;
&#xD;
        label2  = QtGui.QLabel(self)&#xD;
        label2.setText(u'选择服务器启动配置')&#xD;
        label2.move(20,80) &#xD;
        &#xD;
        label3  = QtGui.QLabel(self)&#xD;
        label3.setText(u'选择服务器启动时间')&#xD;
        label3.move(20,150)  &#xD;
        &#xD;
        self.label4  = QtGui.QLabel(self)&#xD;
        self.label4.setText(u'差异时间')&#xD;
        self.label4.move(120,365) &#xD;
        &#xD;
        self.text_serverbin = u"&lt;span style="color: #8b0000"&gt;未选择文件&lt;/span&gt;"    &#xD;
        self.label_serverfilename = QtGui.QLabel(self)&#xD;
        self.label_serverfilename.setText(self.text_serverbin)&#xD;
        self.label_serverfilename.move(20,50)&#xD;
        &#xD;
        btn_selserverbin = QtGui.QPushButton('...',self)&#xD;
        btn_selserverbin.setGeometry(270,45,20,20)&#xD;
        btn_selserverbin.clicked[bool].connect(self.event_selserverbin)&#xD;
        &#xD;
        self.dlg_selserverbin = QtGui.QFileDialog(self)&#xD;
        &#xD;
        #配置选择&#xD;
        combo_serverConfig = QtGui.QComboBox(self)        &#xD;
        xml_file = "&lt;span style="color: #8b0000"&gt;./Data/Config/ServerConfig.xml&lt;/span&gt;"&#xD;
        doc = parse(xml_file)&#xD;
        serverConfig = doc.getElementsByTagName("&lt;span style="color: #8b0000"&gt;serverConfig&lt;/span&gt;")&#xD;
    &#xD;
        for config in serverConfig:&#xD;
            descript = config.getAttribute("&lt;span style="color: #8b0000"&gt;description&lt;/span&gt;")&#xD;
            &lt;span style="color: #0000ff"&gt;index&lt;/span&gt; = config.getAttribute("&lt;span style="color: #8b0000"&gt;index&lt;/span&gt;")        &#xD;
            combo_serverConfig.addItem(descript,&lt;span style="color: #0000ff"&gt;index&lt;/span&gt;)&#xD;
       &#xD;
        combo_serverConfig.move(20,100)&#xD;
        combo_serverConfig.activated[int].connect(self.event_selserverconfig)&#xD;
        self.serverconfigindex = 0&#xD;
       &#xD;
       #日历&#xD;
        timer=QtCore.QTimer(self)&#xD;
        timer.&lt;span style="color: #0000ff"&gt;start&lt;/span&gt;(60000) &#xD;
        self.connect(timer,QtCore.SIGNAL("&lt;span style="color: #8b0000"&gt;timeout()&lt;/span&gt;"),self.event_update)&#xD;
        &#xD;
        self.cal_serverdate = QtGui.QCalendarWidget(self)&#xD;
        self.cal_serverdate.setGridVisible(True)&#xD;
        self.cal_serverdate.move(20, 180)&#xD;
        self.cal_serverdate.clicked[QtCore.QDate].connect(self.event_selserverdate)&#xD;
        self.seldate = QtCore.QDate.currentDate();&#xD;
        &#xD;
        self.time_servertime = QtGui.QTimeEdit(self)&#xD;
        self.time_servertime.move(20, 360)&#xD;
        self.time_servertime.setTime(QtCore.QTime.currentTime())&#xD;
        self.time_servertime.timeChanged.connect(self.event_selservertime)&#xD;
        self.seltime = QtCore.QTime.currentTime();&#xD;
       &#xD;
    &#xD;
        btn_start = QtGui.QPushButton(u'启动服务器',self);&#xD;
        btn_start.move(180,420)&#xD;
        btn_start.clicked.connect(self.start_server)&#xD;
        btn_close = QtGui.QPushButton(u'退出',self);&#xD;
        btn_close.move(260,420)&#xD;
        btn_close.clicked.connect(self.&lt;span style="color: #0000ff"&gt;close&lt;/span&gt;)&#xD;
        &#xD;
        self.setGeometry(300, 300,350,450)&#xD;
        self.setWindowTitle(&lt;span style="color: #0000ff"&gt;version&lt;/span&gt;)    &#xD;
        self.show()&#xD;
        &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; event_selserverbin(self, clicked):&#xD;
        self.text_serverbin = self.dlg_selserverbin.getOpenFileName(self,&#xD;
                        "&lt;span style="color: #8b0000"&gt;Open EXE&lt;/span&gt;", "&lt;span style="color: #8b0000"&gt;&lt;/span&gt;", "&lt;span style="color: #8b0000"&gt;EXE Files (*.exe)&lt;/span&gt;");&#xD;
        &#xD;
        self.label_serverfilename.setText(self.text_serverbin)&#xD;
        self.label_serverfilename.adjustSize()&#xD;
        &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; event_selserverconfig(self,selindex):&#xD;
        self.serverconfigindex = selindex&#xD;
    &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; event_selserverdate(self,date):&#xD;
        self.seldate = date &#xD;
        self.update_deltaTime()   &#xD;
&#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; event_selservertime(self,&lt;span style="color: #0000ff"&gt;time&lt;/span&gt;):&#xD;
        self.seltime = &lt;span style="color: #0000ff"&gt;time&lt;/span&gt; &#xD;
        self.update_deltaTime()                        &#xD;
   &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; event_update(self):&#xD;
        self.time_servertime.setTime(QtCore.QTime.currentTime())&#xD;
        &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; update_deltaTime(self):&#xD;
        currentDateTime     = QtCore.QDateTime()&#xD;
        currentDateTime.setDate(QtCore.QDate.currentDate());&#xD;
        currentDateTime.setTime(QtCore.QTime.currentTime());&#xD;
        &#xD;
        selDateTime         = QtCore.QDateTime()&#xD;
        selDateTime.setDate(self.seldate)&#xD;
        selDateTime.setTime(self.seltime)&#xD;
        &#xD;
        self.label4.setText(u'差异时间: '  +&#xD;
            str(currentDateTime.secsTo(selDateTime)) + u'秒')&#xD;
        &#xD;
        self.label4.adjustSize()&#xD;
    &#xD;
    &lt;span style="color: #0000ff"&gt;def&lt;/span&gt; start_server(self):&#xD;
        serverfilename      = self.label_serverfilename.text()&#xD;
        serverconfigindex   = self.serverconfigindex&#xD;
        &#xD;
        currentDateTime     = QtCore.QDateTime()&#xD;
        currentDateTime.setDate(QtCore.QDate.currentDate());&#xD;
        currentDateTime.setTime(QtCore.QTime.currentTime());&#xD;
        &#xD;
        selDateTime         = QtCore.QDateTime()&#xD;
        selDateTime.setDate(self.seldate)&#xD;
        selDateTime.setTime(self.seltime)&#xD;
        &#xD;
        command = str(serverfilename)&#xD;
        command += ' -c ' + str(serverconfigindex)   &#xD;
        command += ' -t ' + str(currentDateTime.secsTo(selDateTime))&#xD;
        &#xD;
       # &lt;span style="color: #0000ff"&gt;print&lt;/span&gt; command&#xD;
        commandTD = CommandThread()&#xD;
        commandTD.setDaemon(True)&#xD;
        commandTD.setCommand(command)        &#xD;
        commandTD.&lt;span style="color: #0000ff"&gt;start&lt;/span&gt;()&#xD;
    &#xD;
&lt;span style="color: #0000ff"&gt;def&lt;/span&gt; main():&#xD;
    app = QtGui.QApplication(&lt;span style="color: #0000ff"&gt;sys&lt;/span&gt;.argv)&#xD;
    ex  = Example()    &#xD;
    &lt;span style="color: #0000ff"&gt;sys&lt;/span&gt;.&lt;span style="color: #0000ff"&gt;exit&lt;/span&gt;(app.exec_())&#xD;
&#xD;
&#xD;
&lt;span style="color: #0000ff"&gt;if&lt;/span&gt; __name__ == '&lt;span style="color: #0000ff"&gt;__main__&lt;/span&gt;':&#xD;
    main()&#xD;
&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;&lt;/span&gt;&#xD;
&lt;p&gt;上面的小工具在启动服务器时是阻塞式的，一旦服务器启动，小工具就会处于假死阶段，直到关闭服务器。&lt;/p&gt;&#xD;
&lt;p&gt;可以使用Thread库，建立一个线程来启动服务器即可。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;最后使用py2exe ，把上述脚本编译成EXE后就可以在团队内部发布了。&lt;img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none"  alt="热烈的笑脸" src="http://images.cnblogs.com/cnblogs_com/badkeeper/201201/201201031927555912.png"&gt;&lt;/p&gt; &lt;img src="http://www.cnblogs.com/badkeeper/aggbug/2311227.html?type=2" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/badkeeper/articles/2311227.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/badkeeper/articles/1949114.html</id><title type="text">新的工程模式 完整的数据集中式 2</title><summary type="text">其实在一个MMORPG的项目中，经常会遇到这种问题，服务器作为主控方，掌握了绝大部分数据的修改权利，通常对于不同类型的数据包，需要专门定义协议，然后在数据改变的时候同步给客户端（在《武林至尊》中，我们把这种数据主要分为资产和FOV数据两种）。在客户端的逻辑模块，根据服务器发过来的消息包修改本地数据。而客户端的其他模块（UI，渲染）需要定期扫描客户端逻辑模块的数据变化（也可以由逻辑模块主动通知），然后再将部分数据转换为自己模块内部的本地数据（脚本数据，渲染数据）。 服务器端掌管着数千个客户端需要得到的数据集，如果在上图中，把客户端本身也看出一个控制器，我们可以想象这样一种抽象结构，主要的数据..</summary><published>2011-02-04T02:56:00Z</published><updated>2011-02-04T02:56:00Z</updated><author><name>BadKeeper</name><uri>http://www.cnblogs.com/badkeeper/</uri></author><link rel="alternate" href="http://www.cnblogs.com/badkeeper/articles/1949114.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/badkeeper/articles/1949114.html"/><content type="html">&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;其实在一个MMORPG的项目中，经常会遇到这种问题，服务器作为主控方，掌握了绝大部分数据的修改权利，通常对于不同类型的数据包，需要专门定义协议，然后在数据改变的时候同步给客户端（在《武林至尊》中，我们把这种数据主要分为资产和FOV数据两种）。在客户端的逻辑模块，根据服务器发过来的消息包修改本地数据。而客户端的其他模块（UI，渲染）需要定期扫描客户端逻辑模块的数据变化（也可以由逻辑模块主动通知），然后再将部分数据转换为自己模块内部的本地数据（脚本数据，渲染数据）。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/badkeeper/201102/201102041056253500.png"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font size="3" face="微软雅黑"&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/badkeeper/201102/201102041100454635.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Image" border="0" alt="Image" src="http://images.cnblogs.com/cnblogs_com/badkeeper/201102/201102041100467832.png" width="463" height="272" /&gt;&lt;/a&gt;&lt;/font&gt;&lt;/a&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;服务器端掌管着数千个客户端需要得到的数据集，如果在上图中，把客户端本身也看出一个控制器，我们可以想象这样一种抽象结构，主要的数据都集中在服务器端，上千个客户端就像控制器一样联接在数据中心上，通过网络来存取数据。在客户端本地，它自己又是一个比较小的数据中心，它的每个功能模块连接在数据中心上，通过函数调用来存取数据。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;在一个C/S结构的项目中，数据的变化应该主要包含几种方式：&lt;/font&gt; &#xD;
&lt;ol&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据在A端被修改，B端需要知道这种变化&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据在A端被修改，B端不需要知道这种变化&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据在A端和B端同时被修改（不是严格意义上的同时）。双方互不关心变化&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据在A端和B端同时被修改（不是严格意义上的同时）。以其中一端为主，另外一端需要通过各种机制进行数据纠正。&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据在A端，B端定期或事件型触发对数据的请求。&lt;/font&gt;&lt;/li&gt;&lt;/ol&gt;&#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;无论是哪一种情况，数据都可以归纳为3种类型。&lt;/font&gt; &#xD;
&lt;ol&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据有原稿和拷贝，原稿的拥有者可以修改和读取最终的数据结果，拷贝方的拥有者只能读取结果，无法修改结果。&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据有两份，分别由拥有者读取和修改，双方之间在一定时间内互不影响。&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font size="3" face="微软雅黑"&gt;数据只有一份，拥有者都可以进行修改和读取。&lt;/font&gt;&lt;/li&gt;&lt;/ol&gt;&lt;font size="3" face="微软雅黑"&gt;其中2，3情况在客户端设计时经常会遇到，1情况则在服务器设计时大量出现。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;让我们来考虑一种新的设计模式 - 包含了网络端的数据集中式。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;strong&gt;&lt;font size="3" face="微软雅黑"&gt;首先要定义数据层的定位和功能&lt;/font&gt;&lt;/strong&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;数据层能够定义数据，并且定义数据的类型，是原稿还是拷贝。对所有数据，数据层都应该提供数据在最小粒度上的置脏功能，但对于数据的划分粒度，则应该根据业务逻辑来决定。例如背包数据，可以认为所有数据是一个完整整体（最终会导致每次背包的数据变化是全量更新），也可以认为一个背包格的数据是一个整体（增量更新）。粒度的划分难以建立统一的标准，通常要考虑到数据量的大小和数据本身的各种特性。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 数据层应该为数据的最小粒度单元提供读写功能，当然也应该为最小单元之上的整合单元提供一些辅助型的读写功能，否则最终代码的实现上会非常繁琐。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 数据脏标志的写功能，不应该暴露到数据层之外，避免在上层应用上无意间调用。脏标准的读取功能是否暴露可以根据实际需求来决定。如果认为所有数据的变化都应该由数据层主动通知给其他模块的话，就无需暴露。如果有由外围模块扫描数据的情况，则需要暴露。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果要保留脏标准的读取功能，则应该暴露数据变化的次数。之所以要这样要求，是因为无法保证只有一个模块会关心一个对应的数据，例如A B模块同时关心一个数据，那么数据只要一脏，A B就无法停止更新了，A在读取数据时不能将数据改为不脏，B也同样不能这样做。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于数据层来说，最重要的功能应该是如何管理数据的原稿和拷贝。考虑到原稿和拷贝通常在不同的网络终端上，数据层应该提供从原稿方将数据变化同步到拷贝方的功能。这种同步和解析的执行应该是自动和透明的，无需上层关心。例如可以将不同的数据设置不同的标志，数据层定期扫描原稿方数据的变化标准，如果发现有变化的数据，就将这些数据统一压到协议中，然后一次性同步给拷贝方。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 细化上述设计的话，主要有两点需要扩展。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;首先数据的同步应该分为不同的频道，每个频道有流量控制和频率控制，以免数据的变化过快时导致模型的崩溃。每个数据在定义时应该指定所属的频道，一些控制型的数据可以注册到高频率的频道中（减小网络延时导致的体验感问题），一些数据量大的资产数据，可以注册到低频率的频道中。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;其次数据的同步流应该一种自解释功能，或者说应该提供一个通用协议，可以把任意数量的，不同类型的数据按任意顺序组织起来，一次型同步给数据的拷贝方。类似于一个文件流的概念，先有一个文件头，后面是文件的具体内容。这样可以让数据层在每次扫描时，只对一个拷贝方放送少量的网络包，避免出现大量小包，导致TCP/IP头占用大量流量的问题。&lt;/font&gt; &#xD;
&lt;p&gt;&lt;font size="3" face="微软雅黑"&gt;&lt;/font&gt;&amp;nbsp; &#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;&lt;font color="#4b4b4b" size="3" face="微软雅黑"&gt;--未完待续&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;img src="http://www.cnblogs.com/badkeeper/aggbug/1949114.html?type=2" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/badkeeper/articles/1949114.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/badkeeper/articles/1942486.html</id><title type="text">工程结构的层次图和基本职责</title><summary type="text">个人还是偏爱在设计初第一步使用模块层次图来描述一个工程的基本结构。因为层次图可以在较浅的层面上对整个工程进行全面考虑，比较容易在一开始就确定细化设计的重点，并且通常层次图也可以对整体工程的子工程划分起到启发作用。但模块层次图无法具体体现出单层中各个模块之间的交互和依赖关系，也不能提供程序代码的层次划分关系，不能单独使用层次图对开发进行指导。 工程层次结构如图 图中的分割线表示多平台支持的分界线，通常分割线所在层次越高，开发难度就越高，分割线可以选择在网络协议层之上，或者在业务逻辑层之上不再支持跨平台实现。 具体情况要根据需要多平台支持何种业务来定。例如客户端在Windows平台上，服务器在L.</summary><published>2011-01-23T06:23:00Z</published><updated>2011-01-23T06:23:00Z</updated><author><name>BadKeeper</name><uri>http://www.cnblogs.com/badkeeper/</uri></author><link rel="alternate" href="http://www.cnblogs.com/badkeeper/articles/1942486.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/badkeeper/articles/1942486.html"/><content type="html">&lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;个人还是偏爱在设计初第一步使用模块层次图来描述一个工程的基本结构。因为层次图可以在较浅的层面上对整个工程进行全面考虑，比较容易在一开始就确定细化设计的重点，并且通常层次图也可以对整体工程的子工程划分起到启发作用。但模块层次图无法具体体现出单层中各个模块之间的交互和依赖关系，也不能提供程序代码的层次划分关系，不能单独使用层次图对开发进行指导。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;工程层次结构如图&lt;/font&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/badkeeper/201101/201101231423088807.png"&gt;&lt;font size="4" face="微软雅黑"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image(2)" border="0" alt="image(2)" src="http://images.cnblogs.com/cnblogs_com/badkeeper/201101/201101231423095841.png" width="244" height="242"&gt;&lt;/font&gt;&lt;/a&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;图中的分割线表示多平台支持的分界线，通常分割线所在层次越高，开发难度就越高，分割线可以选择在网络协议层之上，或者在业务逻辑层之上不再支持跨平台实现。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;具体情况要根据需要多平台支持何种业务来定。例如客户端在Windows平台上，服务器在Linux平台上，可以选择业务逻辑层之上不再支持&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;多平台实现，如果客户端在Windows平台上，逻辑服务器在Windows平台上，后端服务器在Linux平台上，也可以选择在网络协议层之上就不再支持&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;多平台实现。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;跨平台支持层：提供跨平台的类型定义和函数定义，例如对int64，int32等的定义，以及对sprintf等函数的定义。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 需要注意的是，由于该层存在的主要目的是使上层开发中，无需关心所面向的平台，因此在其之上的&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 各层开发中，需要调用某些平台的特有API时，应该将该API定义在《跨平台支持层》中，并且对该API&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 提供其他平台实现方案。该法则要保持到上图的分割线处。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;实用工具层：提供内存监管，日志，时钟，Profile等，无需在这层提供多线程工具等平台差异较大的功能。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;网络协议层：提供所有网络协议的定义，压缩，存储，生成，解析等功能&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;业务逻辑层：提供所有业务逻辑，并提供对网络协议的存取接口。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;PS：其实网络协议层和业务逻辑层之间的关系比较难以确定。首先网络协议层在定义协议时是否需要使用到逻辑层中的各种定义和枚举。如果是，则协议层依赖于逻辑层。其次是逻辑层是否应该定义如何存取协议的接口，如果是，逻辑层依赖于协议层。而在部分项目中，逻辑层本身不是跨平台的，如果协议层依赖于逻辑层，则协议层本身无法跨平台。最后我选择了以下方式：协议层在下，逻辑层在上。协议层中不要出现任何逻辑层中的定义和枚举，应该保持其自身的独立性。在协议定义时，可以选择使用int替代枚举，选择比逻辑层长的，通用的字符串长度替代逻辑层中的某种字符串长度。协议层本身应该只关注如何进行内存转储并在网络上传输，无需关心其内容的实际含义，也不需要对其内容的逻辑合法性进行检查。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;引擎层：该层提供渲染，音效，网络等具体功能的封装和实现。无需考虑跨平台需求，只需对每种平台实际需要用到的功能单独提供模块即可。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;应用层：该层实现客户端，服务器，编辑器和其他工具，无需考虑跨平台。但应该尽可能考虑在不同应用程序中，复用下层模块。&lt;/font&gt; &lt;p&gt;&lt;font size="4" face="微软雅黑"&gt;&lt;/font&gt;&lt;/p&gt; &lt;img src="http://www.cnblogs.com/badkeeper/aggbug/1942486.html?type=2" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/badkeeper/articles/1942486.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/badkeeper/articles/1942441.html</id><title type="text">新的工程模式  完整的数据集中式 1</title><summary type="text">通过对一种设计模式的讨论，思考MMORPG中如何简化C/S端间的数据同步，简化业务逻辑的变化对工程结构的影响</summary><published>2011-01-23T05:13:00Z</published><updated>2011-01-23T05:13:00Z</updated><author><name>BadKeeper</name><uri>http://www.cnblogs.com/badkeeper/</uri></author><link rel="alternate" href="http://www.cnblogs.com/badkeeper/articles/1942441.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/badkeeper/articles/1942441.html"/><content type="html">&amp;nbsp;&lt;span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; font: medium Tahoma; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"&gt;&lt;br /&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;数据集中式的想法一开始出现于《RUN》，当时RUN作为一个带有物理逻辑的休闲游戏，有很多对物理，动作模块的过程型数据进行累计，计算的得分逻辑，并且通过得分逻辑对UI和特效进行显示控制。一开始的时候，没有统一的设计方式，只是完全的根据游戏逻辑的需求，将不同的逻辑分成不能的控制单元，然后相互访问。这种交叉式访问的工程结构，导致数据离散在不同的文件中，一个数据可能会被重复记录多次，并且控制单元间相互耦合度很高，一个控制单元的修改和扩展，会导致很多其他单元需要跟着一起变动。鉴于上述问题，RUN 当时做了一次重构。重构的目的是剥离控制单元之间的耦合，解决数据和数据计算的重复问题。重构的方向是定义一个数据中心（data center）。统一保存逻辑数据。所有逻辑数据都拥有存取接口，并且对一部分特殊数据，采用链表方式保存，便于对这些数据的变化历史进行记录和相关逻辑（例如每做一次得分动作都记录在链表中，后面可以统计出Combo）；数据中心本身只提供数据的存取和历史查询接口，不需对数据的来源和使用具体逻辑负责。数据中心之外有可以注册多个控制器，每个控制器负责一个相关完整的游戏业务逻辑。控制器负责修改数据中心的一部分数据，但不需要将数据的变化通知给其他关心的控制器。每个控制器都会定期的访问数据中心，看看自己关心的数据是否有变化，如果有的话，就根据这些变化执行相应的工作，并且将结果重新放回数据中心。&amp;nbsp;&lt;/font&gt;&lt;/span&gt;&lt;/span&gt; &#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&amp;nbsp;&lt;span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; font: medium Tahoma; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"&gt;&lt;span style="font-family: 微软雅黑; font-size: 19px"&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;img style="width: 486px; height: 331px" border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/badkeeper/数据集中模式1.png" width="486" height="331" /&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;这样将系统逻辑拆分成多个相互独立的控制器后，逻辑模块的耦合度被大大降低了，一个模块的改动，通常不会影响到工程的其他部分。这种模型的缺点是当很多控制器对数据的访问频率很高，而控制器间数据的依赖程度不高的情况下，反而会降低整个工程的执行效率。&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;后来这个模型在制作《武林至尊》时没有被继承下来，主要是因为第一次涉及MMORPG类型的项目，无法在一开始预估出逻辑模块间的数据依赖程度，如果数据依赖非常高，将数据集中起来无疑比较好，如果依赖程度不够高，那么上述模型反而是一种过设计。因此《武林至尊》在初始设计的时候，采用了一种将消息集中的方式。结果类似于上图，不同的是，数据没有集中起来，而是放到了每个控制器的内部，便于快速访问。中心处集中的是由每个控制器抛出的消息。&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;img style="width: 561px; height: 382px" border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/badkeeper/数据集中模式2.png" width="561" height="382" /&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;控制器在启动时会在中心点注册自己关心的消息，当中心点有新的消息时，会根据注册信息派发给相应的控制器。&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;消息集中模型当然有自己的优缺点，但这不是本文想讨论的内容了，以后会专门针对这种设计来扩展一些想法。&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;《武林至尊》在制作到后期时，我又突然对数据集中模型开始关注起来，因为这个时候对于MMORPG这种工程有了新的认识。上面讲到的两种设计都是针对客户端如果架构的，但其实网络游戏中，服务器端扮演了一个更为重要的角色。通常在设计时，都习惯将服务器端和客户端分开在思考（很大程度上是由于一般情况下，是由两个人或者两个部门来分别开发的），但在这个项目中，由于对服务器和客户端在设计方面的全权把控，让我得到了很多宝贵的经验。其实在最开始设计消息集中模型的时候，考虑过将服务器端抽象成一个网络远端的控制器，将网络消息和客户端本地消息统一起来进行设计，但在实现过程中发现由于网络消息本数据量和数据复杂度和客户端消息之间有非常大的差异（试想一个Win32消息和一个玩家背包数据的区别），这种设计最终没有被付诸实现。但从数据交互的角度来看，服务器端和客户端的控制器之间的差异就没有那么明显了。例如我们考虑玩家背包数据的变化这样一个案例。&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;br /&gt;&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;服务器端需要对玩家背包数据进行修改，然后将数据通过网络传输到客户端。客户端的逻辑模块（控制器）在得到数据后会对本地数据进行修改，然后UI模块（控制器）通过得到数据变化的通知，重新从客户端内存中取出数据，根据数据进行显示上的变化。在这个过程中，有3个模块对数据进行了修改和访问。让人感兴趣的是，如果忽略掉服务器和客户度是两个独立的进程，数据放在不同的物理内存中（当然正常情况下，也不在一台物理机器上）这个差异，我们可以认为是3个模块在对&lt;strong&gt;同一块&lt;/strong&gt;数据进行修改和访问。&lt;/font&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;/font&gt;&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;&lt;/font&gt;&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&#xD;
&lt;div&gt;&lt;span style="font-size: 19px"&gt;&lt;font face="微软雅黑"&gt;--待续&lt;/font&gt;&lt;/span&gt;&lt;/div&gt; &lt;img src="http://www.cnblogs.com/badkeeper/aggbug/1942441.html?type=2" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/badkeeper/articles/1942441.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/badkeeper/articles/1624067.html</id><title type="text">技能系统设计笔记 7</title><summary type="text">记录时间：2009年12月14日 更依赖于事件的技能设计方案 如果我们将技能事件的定义和功能扩展，会发现其实远程技能，近程技能的共通点。前期设计中，我们只考虑了事件的瞬时性，换句话说，我们认为只有瞬时发生，并只发生一次的逻辑才属于技能事件。如果我们在事件上加入时间，变化性等特点，可以惊喜的发现原来近程/远程技能的逻辑是如此统一。 我们可以尝试用新的事件结构来描述近程和远程技能 就像上面所描绘的一样，近程技能和远程技能在设计上达成了统一。近程/远程技能在技能控制，时序更新，事件触发和结果派发的各个关键点上的逻辑达成了一致，它们之间的差异性被转移到事件本身中。采用这种设计，技能的...</summary><published>2009-12-14T12:54:00Z</published><updated>2009-12-14T12:54:00Z</updated><author><name>BadKeeper</name><uri>http://www.cnblogs.com/badkeeper/</uri></author><link rel="alternate" href="http://www.cnblogs.com/badkeeper/articles/1624067.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/badkeeper/articles/1624067.html"/></entry></feed>
