




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
编写网络程序应该注意的几个问题刘刚2003年9月19日星期五2006年7月14日星期五批注经过一年多来天骄网络游戏的编程工作,有了一些经验和教训,希望在这里跟大家分享。注:后来我和我的同事编写了天骄II,增加了不少经验,在这里补上。以前我们得出的一些结论到了现在发生了变化,在这里也进行了修正和补充。毕竟我离开目标软件和网络游戏行业一年了,有些经验可以结合着具体的游戏表达出来。网络程序与单机程序相比难度是成倍增加的网络程序的编程和单机版相比,难度增加了许多,而且往往是超乎我们的想象的。对稳定性需求增加一台游戏的服务器程序需要持续不断的运行,至少要在24小时内不会发生任何问题。这与单机游戏相比,难度增加了。如果希望这款游戏的规模再大些,要求就更高了。基本上要达到电信级的水平。注:在后来的天骄II里面,我们终于基本上做到无严重BUG,正式运营的游戏服务器可以正常运行一周没有问题,在我们自己的测试服务器上运行时间超过49天。当然这个成绩在我离开目标的时候还没有达到,还存在一个BUG不定期的会导致崩溃,这时候是去年的7月份,天骄II收费2个月。后来在去年年底,这个BUG由接替我工作的同事们找到,值得庆幸的是,导致BUG的代码并不是我们编写的,而是只作网络底层的程序员编写的。这说明我们在防止BUG方面,做得很不错。对效率要求增加一台服务器支持的游戏人数是非常多的,每个用户的操作也是多种多样的。中间会有慢速操作和快速操作。就需要我们分离这些操作,提高快速操作的效率,而不能让这些操作为了等一些无关紧要的东西而降低效率。对这部分内容进行优化势必会增加程序的复杂性和出现问题的可能性。注:一般我们会用多线程来充分利用服务器上的多个CPU。 程序复杂度增加游戏的用户操作比起那些电信的操作来说要复杂的多了。所可能导致的问题也要多很多。服务器结构的扩充,程序内部模块的划分,模块和线程之间的交互都会使得程序更加容易出现BUG,而且这些BUG更加隐蔽。注:服务器程序的复杂度,不在于代码量的多少,而在于代码的BUG数量和优化的程度。版本更新频繁天骄的服务器程序从2003年2月14日至今就更新了123个版本,平均不到每两天就有一个版本。每次更新都可能包含新的BUG,新的问题。长时间的维护,同一段代码由不同的人维护,也会增加代码中的隐患。注:所谓的至今是只截止到我写这篇文字的时候,2003年9月,大约7个月的时间。天骄I至今还在运营,后来更新了多少版我没有统计过。对BUG的监测那么如何加强对如此复杂的程序的监控呢?下面有一些方法。这些方法对于单机程序而言,可能并不重要,但是对于网络程序而言,就显得比较重要了。这个时候我们写代码,其主要作用就不仅仅是实现游戏的功能,更重要的是如何使我们的代码错误更少,出了错之后更容易被找到。注:因为网络游戏的特殊性,我们必须不断地更新游戏,所以游戏基本上不可能出现一个阶段,在这个阶段里是完全BUG Free的。很多修改都要求我们在没有完全进行测试的情况下就必须上线,实际上进行所谓的完全测试也基本上是不可能的。所以,这要求我们必须很快的定位错误,并且在错误一开始发生的时候就尽量的收到报警并找到它。Assert在每一个模块,线程,函数的参数输入部分,增加Assert()进行参数合法性判定。但是要注意,这个函数的作用仅仅限于Debug版,对于查找那些非常隐蔽的错误用处是不大的。所以一般仅仅在程序开发的前期使用。到了后期,基本上要以后后面的解决办法来查找问题。assert( strlen(szMsg) sizeof(szMsg) );注:在天骄II的时候,因为ASSERT的作用实在是太大了,我们又增加了一个编译选项:Final版。也就是说除了普通的Debug和Release编译选项以外,又增加了一个Final。Final才是最终服务器会用到的版本。而我们在Release版里,重新定义了ASSERT(),而不是原来的简单的把代码设置为NULL。重新定义以后,ASSERT()就不再报对话框,而是直接写log文件,这在公测期间尤为重要。可以随时帮助我们防范错误。Log文件因为游戏的服务器经常需要长时间运行,而且大多是Release版的情况下,所以非常有必要对那些出现错误的地方进行随时的记录。对错误的输入要有记录。主要需要记录的内容有:l 正常错误。程序中可能出现的正常错误,比如资源没有找到,读写磁盘失败,登陆失败等等。l 非正常错误。不应该出现的错误,比如人物没有找到,道具属性没有找到,怀疑是作弊的行为等等。对于网络程序,几乎要在任何我们认为可能出现错误的地方写下Log记录。但是因为Log记录的操作非常消耗时间,所以对于那些调用非常频繁的地方不适合用Log来记录,那就需要下面的方法。WriteLogFile( easyrpg.log, Here cannot be a room!n );注:因为log比较慢,所以在天骄I的后期,和天骄II,我们将这些信息输出给另外一个线程,后者是另外一台计算机,然后再保存。这里的用途除了查找BUG以外,另外一个主要的用途是记录玩家的操作,这是为了追查复制等玩家作弊行为,监控游戏的公平性,防止玩家刷钱,洗钱。Try和Catch网络服务器程序经常需要运行较长的时间,而且一旦出现错误,无法得知到底是在哪里出现的。Try和catch就帮助我们解决这个问题。Try的作用就是保证如果这部分代码出现的了异常,程序会自动返回到一个catch中的指令里继续执行。而不会出现非法指令的问题。我们的全部代码都应该被保护在try和catch里,层层嵌套。在现实情况下,错误往往会出现在程序的任何一个地方,可能会在任何时候出现,而且这些错误往往是不能立刻再现的。我们的程序员已经不可能针对每一个错误长时间的进行“再现”尝试和跟踪。这时候,往往需要我们一边用try先把错误跳过去,让程序可以继续运行,另一方面缩小包围圈,逐步找到问题的真正所在。Try和catch的最大作用是当程序发生了错误,我就可以立刻知道在哪个范围里,哪个函数里发生了错误。虽然发生错误的地方和产生错误的地方可能并不一定是一个地方,但是至少可以帮助我们尽快的确定到底是哪里出了错,并且不让这里继续出错。在天骄里经常可以遇到几个月以后才最终找到BUG的情况。往往当一个错误发生了,我们并不能很快定位出错的原因。但是如果我们的程序内部有这样一个由log和try这样的纠错机制所组成的“网”的话,会非常有助于我们尽快解决问题。实际上一般,try和写log都在一起使用,一旦发生了异常,被catch住,就应该写log把相关的信息打印出来。int aaa = 0;#ifndef_DEBUGtry#endif(实际的代码1)aaa = 1;(实际的代码2)aaa = 2;(实际的代码3)aaa = 3;#ifndef_DEBUGcatch(.)char szMsgER_MESSAGE_SIZE;sprintf( szMsg, newpersonex() Error : %d n, aaa, szType, nLevel, nSLevel );WriteLogFile( easyrpg.log, szMsg );#endif这里要说明一下,变量aaa的作用就是帮助我们定位到底是在执行哪段代码,哪个函数的时候发生了异常。以便于我们下次在该函数内部写入这样的try和catch的代码,继续跟踪。注意:对于还没有发布的程序,尤其是单机版游戏,草率的使用try反而是有害的(假如try不能很快定位错误的话)。因为这样会徒然导致许多很奇怪的错误的产生,会掩盖真正出现错误的地方。注:Try和Catch不能滥用,这里再次强调,尤其在客户端程序,我们不建议使用。甚至我们在天骄II里,基本上很少使用Try和Catch了。一个主要原因是,因为吸取了天骄I的经验,所以天骄II崩溃的机会比天骄I要少了很多。我们基本上没有必要随时保证服务器的稳定运行。另外一个主要原因是我们找到了一个更好的办法。以前,我们最头疼的问题是,一旦程序崩溃,我们无法知道它是在什么地方崩溃的,函数调用的嵌套关系是怎样的。但是我们利用了Windows提供的dbghelp.dll的功能,可以使我们随时发现相关的代码,这大大提供了我们查找错误的速度,所以Try和Catch的主要作用都可以被替代了。当然,如果我们遇到的这个BUG,短时间内找不到,利用Try和Catch维持一下服务器的稳定还是必要的。程序注释程序注释的重要性不仅仅是在解决BUG方面,在大家一起维护同一段代码和自己长时间维护同一段代码的过程中,都会帮助我们的记忆。一般我们都会要求:在每修改一个BUG的时候,要写上修改人,日期,BUG的原因以及是怎么改正的。在每增加一项功能的时候,要写上是什么功能,和什么部分配合。/ BUG FIX : Apr.14.2003, 对中毒以后的人,必须记住是谁下的毒。/中毒时间结束以后,要删除这个变量注:这个要求尤其重要。在我们培训新程序员的时候,这是一个基本要求,在写任何代码的时候我们都要求必须这样。因为这是目前我们认为的最有效的程序文档。必须强制执行。相信程序员自己会逐渐认同的,毕竟这对于需要不停阅读代码的他们,有着实际上的好处。消息和指令要有文档服务器和客户端之间,甚至服务器内部不同模块之间的交互,往往是通过消息的形式来实现的。模块之间交互往往牵扯到不同的程序员之间,不同的模块之间的程序。所以这些接口部分的内容我们建议用文档来将其规范化。每增加一个消息,都应当在文档上有所体现。文档上的主要内容是这个消息的作用,消息的参数,以及发出了这个消息后我们希望会收到什么样的消息。如果每两个模块之间的交互都有这样的文档,那么模块与模块之间的接口就清晰了,发生了问题也比较容易分清责任。我们在设计游戏中某一个功能的时候也能够有一个依托。打开赛程安排界面94TDS_PLAN%d此人ID%d场次1-4%d是否可以参加%s比赛信息最大人数,时间信息,比赛方1,比赛方2注:这相当于是制定网络协议。这是软件工程里,拆分模块的最有效的方法,这就相当于是模块之间的接口。这个接口定义的越清晰,模块之间的调用和访问就会越简单。在我们的天骄I和天骄II里,我们甚至把服务器端程序都拆分成两个部分:文字服务器和图形服务器,这样大大简化了单个模块的复杂度。尽管这增加了消息通讯之间的复杂度,有些程序员认为效率不高,但是经过了长时间的使用,我相信他们会觉得,简单的程序对他们来讲更重要。局域网的局限性我们测试一般都是用公司内部的局域网络进行测试。但是,我们必须要知道,有些错误和问题在局域网上是不能检测出来的。比如天骄的卡机问题。还有在内测时候出现的服务器停止响应的问题,一些利用网络反应迟缓作弊的问题等等。都是我们在局域网无法预先得知的。所以在最终的测试之前,一定要经过Internet的实际检验。注:必须要在真正的Internet环境下真实的测试。测试的问题,一直被认为是最让人头疼的问题之一。在盛大,每次服务器更新都是一场恶仗,由陈天桥带队,所有人员(开发、运营)必须在场,更新以后,必须等待一段时间以后,游戏稳定了,没有玩家抱怨了,所有人才可以撤离。但是我更欣赏九城的方法:建立免费测试区。目标软件也采取的同样的方法,我们建立专门的免费测试区,这个区一般是由我们的内测演变而来的这样就不必删除玩家的存档了。新版内容先放在这个区里测试1-3天,确定没有问题了,再扩展到全部服务器。但是在现在免费时代,免费测试区存在的意义似乎就不大了,因为没有了免费的优势。同时,战区的结构需要更复杂些。对内存使用的关注服务器要想稳定运行,其内存和Windows资源的占用也是很重要的。一定要注意内存的泄漏(Memory Leak)。尽管Windows提供了很方便的内存管理方式,但是有时候我们也需要自己来管理动态内存。因为这时候我们更加容易检测到内存使用的非法情况。再提一次天骄II最后的那个BUG,为了查找到底是谁把内存写出界了,我们把所有模块的动态内存分配都放入了一个自己写的内存池(Memory Pool),但是很遗憾的是因为当时我们采用了公司另外一个部门写的网络底层,这部分代码并没有放入pool中,所以这个问题一直没有被发现。后来还是这个底层代码被使用在另外的一个游戏中之后,才被发现的。所以,我们必须要时刻关注服务器程序的内存占用,这是服务器程序想要稳定运行的必不可少的条件。运行超过49天的BUG在Windows系统里,VC提供一个函数timeGetTime(),得到系统启动累计时间的毫秒数。这个数值是32位的,所以基本上每隔49天就会复位一次。而我们游戏的BUG恰巧在这个时候出现了。这种情况按例说是非常难以出现的,因为大多数正式运行的服务器,都会每周重新启动一次。出现异常要立即做些什么尽量要把BUG消灭在刚刚写完的时候。所以一旦发现问题,尽量立即就进行查找。而对于那些一时无法定位的问题,我们就应该增加log和try这样的调试代码。以期望下次这个问题出现的时候,我们距离错误能够更进一步。注:用一句通俗的话讲,这叫做“贼不走空”。每次我们发现了问题,必须写一些代码更详细的跟踪代码。这样这次服务器的“当机”才更有意义。如果你发现你对这样的当机无计可施,这只能说明你应该换程序员了。提升效率要注意的地方服务器程序里有一些操作是必须进行序列化的。比如:对客户端发过来的指令的执行,我们一般都只能采用一条流水线来处理(为了减少程序的复杂度)。而这些操作里往往既有很快就可以执行完的操作,比如修改一个属性,也有需要较长时间才能执行完的,比如读取一个场景。如果这些慢速指令影响到那些快速指令,则会导致服务器的效率降低。读写磁盘的操作这些操作包括:对图片等资源的读取,对角色档案的读取和写入,对log文件的写入等等。磁盘操作所消耗的时间一般是内存操作的1000倍以上,所以宁肯减少一次读写盘的操作,也比增加一些程序代码要好。当然,具体问题需要具体进行测试和分析。如果是读写其他计算机上的磁盘的操作,比如通过网络共享来读写,速度就更慢了,这一点我们必须注意。注:在天骄II,我们用数据库的操作更多的来代替磁盘文件操作,减少了不少出现BUG的可能性,同时性能也有所提升。调用硬件的次数对网络,显示的硬件调用次数,会直接影响到程序效率。消息队列的排队情况模块之间的调用我们一般用消息(指令)的形式实现。这就需要我们时刻关注这些消息队列的排队情况。如果某个队列排队情况比较严重。则说明有效率低下的情况。我们就可以针对这些情况进行优化。注:消息的堆积这个问题在天骄II里非常突出。因为天骄II的服务器程序的效率大幅度的提升,我们内部测试的数据可以达到2000人/服务器。但是,因为人数太多,消息的数量曾经在公测期间造成大面积的堆积。这需要我们花费更多的精力来进行监控的优化。我们制作了专门的代码来检查消息队列的长度,以及他们消长的情况。一般我们会测试出一个消息量的最大值,比如每秒处理5万条消息,一旦超过这个数量,消息就会堆积,这时候我们的服务器必须丢弃一些消息,或者减缓游戏的刷新频率(比如敌人的刷新频率),这样来缓解小溪的压力。同时,我们也获得了一台服务器最大能够支持的用户的数量。帧速率的平稳理想的情况下,服务器的帧速率应该是平稳的,即便是每秒12帧,帧与帧之间的间隔应该也是平稳的。而如果我们不能把慢速操作分离出来,就很难做到这种效果。注:在我们的游戏程序里,为了防止外挂,很多操作是服务器同步模拟的,并且一切都以服务器的为准。所以这时候,服务器一般都会有一个固定的刷新频率。比如刷新敌人,记录人物身上状态的时间,记录技能火球的移动等等。我们在优化的时候应该尽量减少服务器每秒刷新的内容。多线程的使用单机游戏可以不使用多线程,但是网络方面的程序一般就都需要多线程技术了。而多线程技术对程序员的水平提出了更高的要求。最容易出BUG和死锁的情况多线程的问题最主要的就是资源共享的问题。如果一个资源同时被多个线程修改和访问,就会造成错误。而关键区的嵌套则很容易造成死锁。要想避免或者解决这样的问题,一般可以遵循以下的规则:l 搞清楚那些内容是要被其他线程共享的,它们被称作资源。尽量减少这些资源。l 尽量不要用指针来访问那些共享的资源。对于那些可能被释放的动态资源尤其如此,这是非常危险的。l 可以把资源拷贝一份出来,然后释放关键区,减少关键区包含的范围。关键区包含的范围越大,越会降低多线程的效果,也容易不小心包含了不该包含的内容。l 关键区内尽量不要包含慢速操作,因为这样会拖住其它的线程。l 尽量不要嵌套使用关键区。l 把所有同一个线程的操作写在同一个文件里。这样会帮助你理清头绪。写线程最省事的办法一个10分钟就可以为你的程序增加一个线程,却不会出任何关键区错误的方法是这样的:1. 这个线程和其它线程通讯的唯一途径就是消息。通过一个接受消息链表和一个发送消息链表,来进行本线程和其它线程的通讯。2. 在这两个链表的添加消息和删除消息的函数里分别设置关键区。3. 保证只有这两个消息链表才是线程之间的共享资源,其它的所有内容都不是。CDMList gSRBN_RecvList;CCriticalSection gCS_SRBN;int SRBN_AddRecvMsg( int nIDNet, int nKey, LPCTSTR szCmd, LPCTSTR szParam )int nCount = gSRBN_RecvList.GetCount();if( nCount = 1000 & (nCount%1000) = 0) )char szMsgNET_MESSAGE_SIZE;sprintf( szMsg, SRBN_AddRecvMsg() : Too Muchn, nCount );WriteLogFile( easyrpg.log, szMsg );gCS_SRBN.Lock();CSRBN_MsgItem* pmsg = gSRBNPool.NewPtr();if( pmsg )assert( strlen(szParam) m_szMsg) );sprintf( pmsg-m_szMsg, %s %s, szCmd, szParam );pmsg-m_nMsgSize = strlen( pmsg-m_szMsg );assert( pmsg-m_nMsgSize m_szMsg) );pmsg-m_nIDNet = nIDNet;pmsg-m_nKey = nKey;gSRBN_RecvList.AddTail( pmsg );gCS_SRBN.Unlock();gSRBN_Sema.Unlock(1);/ 接收线程可以接收return 1;int SRBN_RemoveRecvMsg( int* pnIDNet, int* pnKey, char* szMsg, int nSize )int ret = 0;gCS_SRBN.Lock();if( !gSRBN_RecvList.IsEmpty() )CSRBN_MsgItem* pmsg = gSRBN_RecvList.RemoveHead();if( nSize pmsg-m_nMsgSize )memcpy( szMsg, pmsg-m_szMsg, pmsg-m_nMsgSize );assert( pmsg-m_nMsgSize m_nMsgSize = 0;*pnIDNet = pmsg-m_nIDNet;*pnKey = pmsg-m_nKey;assert( pmsg-m_nKey != -1 );elseassert( 0 );gSRBNPool.DeletePtr( pmsg );static int nloop = 0;if( nloop % 101 = 0 )gSRBNPool.Fix( 100 );nloop +;ret = 1;elseret = 0;*pnIDNet = -1;*pnKey = -1;gCS_SRBN.Unlock();return ret;当然,这样的写法仅仅对一部分线程有效,对于那些还需要共享其它资源的线程是没有太大用处的。注:要用这里的算法请注意,消息的拷贝将可能是你的程序消耗CPU的罪魁祸首。硬件环境在编写和测试多线程程序的时候,一个双CPU的计算机对你会有非常大的帮助。一个在单CPU计算机运行一宿才可能出现的问题,在一个双CPU的计算机上一个小时就可能出现了。这样可以节省你大量的测试时间。注:值得庆幸的是,在Windows系统下,你不用自己去管你的线程应该会用在哪个CPU上。文件操作中容易出错的地方如果程序对一个文件既有读操作,又有写操作。那么对这个文件和整个程序而言是极为危险的。因为如果一旦写入文件失败,则会影响到读的操作,而读操作的失败则可能导致整个程序的崩溃。如果这个文件是玩家的存档,则很容易造成档案数据的永久性损坏。注:还是那个建议:尽量用数据库来代替文件系统,又快又不容易出错,更好查询和修改。天骄I的问题是因为这是一个从单机游戏改装过来的网络游戏,你总不能让用户在玩一款单机游戏的时候还要再安装一个数据库吧?多个服务器共享文件在天骄中,会出现多台服务器程序读写同一个文件的情况。比如门派系统的存盘文件。读文件无所谓,但是对于写文件,如果出现多个进程同时写入一个文件,则会导致这个文件的数据混乱。这时候我们必须自己建立一个跨计算机的“关键区”。目前我们所采取的方法是建立“文件锁”的方法。在写操作开始前,判断是否有文件锁,如果没有则创建一个文件锁,然后写入,写入结束以后,删除这个文件锁。写盘操作最不容易出错的写法在读写玩家存档这样的操作中。为了防止在写入文件时失败导致的文件不可读。我们一般采取下面的做法:先把文件写入到一个临时文件中。如果最后成功,再把这个文件改名成为正式文件。当然,文件的结构也是非常重要的,如何既可以随时修改和扩充,又可以随时校验读写的正确与否,都是我们要考虑的。最后,我们必须判断所有的读写函数的返回值。如果出现错误必须能够及时发现,及时处理,以确保如果读取了出了错的文件不会对整个系统造成致命的影响。因为这毕竟不同于单机游戏,网络游戏里即使读了错误的文件也不能使系统崩溃。用数据库代替文件操作用数据库来代替文件操作是个不错的想法,既解决了资源共享的问题,又保证不会出现错误。而且,在速度上也会比较高。机器人的使用机器人就是我们用来模拟客户端操作的一种程序。在网络时代,一台服务器支持成百上千的客户端是家常便饭。但是由此而来的测试问题就摆在我们的面前。尽管有内测和公测的阶段,但是,如果我们的程序有太多的问题,会给公众造成非常不好的影响。所以,我们尽量还是应该能够自己进行比较广泛的测试。机器人就是我们最有效的办法。注:这一点在天骄II得到了充分的利用。我们用机器人模拟了尽可能多的用户操作,从简单的聊天说话,到选用指定的技能进行战斗,甚至穿戴装备。用技能战斗和使用穿戴道具,是常见的两类服务器功能性BUG。尤其在机器人制作的过程中,如果能在一个进城里模拟出更多的机器人,也是很重要的。这样我们利用少量的几台机器就可以模拟出多大上千的机器人。我们测试的2000人/服务器的最大负载就是利用机器人实现的。在天骄II的机器人产生程序里,所能产生的机器人的数量取决于Windows所允许的一个进程里同时能打开的线程的数量,和机器的内存。毕竟一个虚拟的客户端至少也需要几M的空间来存放必要的客户端数据。利用海量的机器人长时间测试过的服务器程序,会大大增强你的信心。实现稳定性测试机器人的一大特点,就是可以针对某一类操作进行高频率的测试,以求在最短的时间里达到最好的测试结果。比如我们在天骄里的登录测试,切换场景测试和组队测试等。都是让机器人频繁的发送一些指令,来专门测试这些指令的执行情况。我们在天骄的稳定性测试的时候,一般是以一宿为单位(10小时),如果在一宿的时间里运行正常,则被认定是稳定的。实现压力测试机器人的另外一大好处是占用的资源较少,我们目前天骄使用的机器人占的内存只有6MB,远远小于一个真正的客户端的需求。这样我们可以在一台计算机上同时启动多个机器人,将服务器的人数充满。如果机器人模拟的足够真实,我们在内测以前就可以得知服务器的最大负载将是多少人。但是,很遗憾,天骄里到目前为止还没有真正实现这第二个功能,一般我们仅以几十来模拟几百人的效果。杜绝外挂实践证明,我们是完全有能力杜绝游戏中外挂的出现的。原理其实很简单,就是一个原则:不信任客户端。注:这句话后来很遗憾的被证明是错误的,至少严格来讲是不完全正确的。这样做只能完全杜绝恶性外挂。也就是说,最多能够防止玩家作弊,但是不能防止玩家的“类机器人”外挂。不过现在网游进入了免费时代,那种消耗时间的机器人外挂存在的价值已经不大了,所以外挂的威胁对网络游戏而言,已经逐渐在缩小。对于我们更重要的是防止恶性外挂就足够了。所以从这个层面上来讲,这段文字的意义还存在。所有重要的计算都放在服务器端进行这些最主要的操作可能包括:装备道具,攻击计算,移动,NPC的智能等等。天骄的实现方法是在服务器端也运行着一个游戏世界,而客户端仅仅是接受服务器传来的一些必要消息。当然,这种做法也导致服务器负载过重,一台服务器支持的人数收到限制。注:天骄I就有严重的此类问题。因为游戏的服务器是从单机游戏简单的修改过来的,所以,服务器端程序真实的模拟了所有客户端的操作,比如一个火球在飞行,服务器端也会产生一个火球在一帧帧的飞行。但是,其实因为服务器不是玩家可见的,所以完全没有必要这样,只要做一个近似的模拟就可以了。比如天骄II就进行了此类的优化,服务器性能大幅度提高。审核客户端来的消息参数对任何客户端发过来的消息(指令)都要进行参数的合法化校验。如果发过来的参数不足以证明其合法性,就要在服务器端额外设置变量进行验证。比如,学习技能。客户端可能发送过来任意一个技能。但是,服务器必须临时设置一个变量,来存储此人能不能学习这个技能。/ 判断作弊CERString strToLearn = pPlayer-GetStr(SKL_ToLearn);int nFind = strToLearn.Find( szParam );if( nFind DeleteStr(SKL_ToLearn);注:这是防止黑客攻击的基本方法。与上面的问题结合起来考虑,有这样一条规则:如果你无法审核这条指令的合法性,那么这就是重要的计算,你必须把它放到服务器端来进行。
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 项城市辅警真题2024
- 河南郑州交通技师学院招聘笔试真题2024
- 百色市乐业县人民法院招聘笔试真题2024
- 上海市上海外国语附属外国语学校2026届化学高二第一学期期末联考模拟试题含答案
- 2025年淮安市市直学校教师招聘考试笔试试卷附答案
- 2025年国家公务员考试公共法律基础知识复习题库及答案
- 2025年公文写作考试题库含参考答案
- 2025年公共卫生与健康教育考试卷及答案
- 江苏色彩考试题库及答案
- 安全专业考试题库及答案
- engel恩格尔注塑机机操纵使用说明
- 花卉学 二年生花卉
- 附件1:中国联通动环监控系统B接口技术规范(V3.0)
- 箱变设备台账
- GB/T 1185-2006光学零件表面疵病
- 微课(比喻句)讲课教案课件
- 银行间本币市场业务简介
- 2023年厦门东海职业技术学院辅导员招聘考试笔试题库及答案解析
- 辽阳市出租汽车驾驶员从业资格区域科目考试题库(含答案)
- (完整版)剑桥通用五级PET考试练习题
- DB32- 4385-2022《锅炉大气污染物排放标准》
评论
0/150
提交评论