MMORPG大型游戏设计与开发(part6 of net)

上一部分,讲述了一个服务器与服务器之间的通信实例,客户端其实原理大同小异。网络部分准备就暂时讲到这里,不过我们不妨再回头过来想想在这过程中有没有优化和改进的地方。这部分讲解的是以网络包代码作分析,实现自动生成其代码的功能。

网络包代码

/**
 * PAP Engine ( -- )
 * $Id connect.h
 * @link -- for the canonical source repository
 * @copyright Copyright (c) 2013-2013 viticm( [email protected] )
 * @license
 * @user viticm<[email protected]>
 * @date 2014-01-16 17:20:14
 * @uses packet Connect class
 */
#ifndef PAP_SERVER_COMMON_NET_PACKETS_SERVERSERVER_CONNECT_H_
#define PAP_SERVER_COMMON_NET_PACKETS_SERVERSERVER_CONNECT_H_

#include "server/common/net/config.h"
#include "server/common/net/connection/base.h"
#include "common/net/packet/base.h"
#include "common/net/packet/factory.h"
#include "server/common/game/define/all.h"

namespace pap_server_common_net {

namespace packets {

namespace serverserver {

class Connect : public pap_common_net::packet::Base {

 public:
   Connect();
   virtual ~Connect() {};

 public:
   virtual bool read(pap_common_net::socket::InputStream& inputstream);
   virtual bool write(pap_common_net::socket::OutputStream& outputstream) const;
   virtual uint32_t execute(connection::Base* connection);
   virtual uint16_t getid() const;
   virtual uint32_t getsize() const;

 public:
   int16_t get_serverid();
   void set_serverid(int16_t serverid);
   int16_t get_worldid();
   void set_worldid(int16_t worldid);
   int16_t get_zoneid();
   void set_zoneid(int16_t zoneid);

 private:
   int16_t serverid_; //服务器ID
   int16_t worldid_; //世界ID
   int16_t zoneid_; //区域ID

};

class ConnectFactory : public pap_common_net::packet::Factory {

 public:
   pap_common_net::packet::Base* createpacket();
   uint16_t get_packetid() const;
   uint32_t get_packet_maxsize() const;

};

class ConnectHandler {

 public:
   static uint32_t execute(Connect* packet,
                           connection::Base* connection);

};

}; //namespace serverserver

}; //namespace packets

}; //namespace pap_server_common_net

#endif //PAP_SERVER_COMMON_NET_PACKETS_SERVERSERVER_CONNECT_H_

#include "server/common/net/packets/serverserver/connect.h"

namespace pap_server_common_net {

namespace packets {

namespace serverserver {

Connect::Connect() {
  __ENTER_FUNCTION
  __LEAVE_FUNCTION
}

bool Connect::read(pap_common_net::socket::InputStream& inputstream) {
  __ENTER_FUNCTION
    inputstream.read((char*)(&serverid_), sizeof(serverid_));
    inputstream.read((char*)(&worldid_), sizeof(worldid_));
    inputstream.read((char*)(&zoneid_), sizeof(zoneid_));
  __LEAVE_FUNCTION
    return false;
}

bool Connect::write(pap_common_net::socket::OutputStream& outputstream) const {
  __ENTER_FUNCTION
    outputstream.write((char*)(&serverid_), sizeof(serverid_));
    outputstream.write((char*)(&worldid_), sizeof(worldid_));
    outputstream.write((char*)(&zoneid_), sizeof(zoneid_));
  __LEAVE_FUNCTION
    return false;
}

uint32_t Connect::execute(connection::Base* connection) {
  __ENTER_FUNCTION
    uint32_t result = 0;
    result = ConnectHandler::execute(this, connection);
    return result;
  __LEAVE_FUNCTION
    return 0;
}

uint16_t Connect::getid() const {
  using namespace pap_server_common_game::define;
  return id::packet::serverserver::kConnect;
}

uint32_t Connect::getsize() const {
  uint32_t result = sizeof(serverid_) +
                    sizeof(worldid_) +
                    sizeof(zoneid_);
  return result;
}

int16_t Connect::get_serverid() {
  return serverid_;
}
void Connect::set_serverid(int16_t serverid) {
  serverid_ = serverid;
}
int16_t Connect::get_worldid() {
  return worldid_;
}
void Connect::set_worldid(int16_t worldid) {
  worldid_ = worldid;
}
int16_t Connect::get_zoneid() {
  return zoneid_;
}
void Connect::set_zoneid(int16_t zoneid) {
  zoneid_ = zoneid;
}

pap_common_net::packet::Base* ConnectFactory::createpacket() {
  __ENTER_FUNCTION
    return new Connect();
  __LEAVE_FUNCTION
    return NULL;
}

uint16_t ConnectFactory::get_packetid() const {
  using namespace pap_server_common_game::define;
  return id::packet::serverserver::kConnect;
}

uint32_t ConnectFactory::get_packet_maxsize() const {
  uint32_t result = sizeof(int16_t) +
                    sizeof(int16_t) +
                    sizeof(int16_t);
  return result;
}

} //namespace serverserver

} //namespace packets

} //namespace pap_server_common_net

  这个网络包代码,即是上一部分服务器连接服务器的包,其他网络包都大同小异。所以我们不妨实现一个代码自动实现的工具,让大部分的代码通过工具自动实现。因为网络包在编程中可能会根据不同的游戏非常的多,有了这样的工具那么我们开发时间和精力上就不必费事了。

网络包文本

/** 注释行 可以用来描述该文件的作用 **/
/* author: viticm */
/* date: 2014-1-16 10:36:39 */
/* desc: 服务器与服务器之间的连接包 */

ModelType: Server /* Server 服务器专用 Common 客户端与服务器公用 */
ModelName: serverserver /* 模块名 */
PacketName: Connect /* 包名 将作为类名使用 */
FileName: connect /* 文件名 */
/* 0 不用包含 1 服务器与公用 2 服务器 3 公用 --包括定义文件define/all.h
 * 服务器为pap_server_game_common::define 公用为pap_game_common::define
 */
IncludeDefineFile: 2

/* 数据名称将作为变量名,数据类型可用c99所有整型以及字符和数组,不允许使用指针
   不使用指针的原因是在32位与64位之间长度大小有区别
 */
/**
[数据名称] [数据类型] [长度] [描述?]
如果长度为0,对于字符和float等来说就只是单纯的字符或者float,
否则为对应的数组,多维数组以逗号分开。
多维数组的示例:[account] [char] [10,20]
长度可以为数字也可以为相应的宏或者枚举,不过你要确保它的存在
描述将作为注释生成在头文件中如 uint16_t playerid_; //玩家ID,描述可以为空
**/

/*packet define begin {*/
[serverid] [int16_t] [0] [服务器ID]
[worldid] [int16_t] [0] [世界ID]
[zoneid] [int16_t] [0] [区域ID]
/*packet define end }*/

  有了这样的文本,我们就可以用工具实现上面的代码。

工具

<?php
include_once ‘../include/base.php‘;

// $packetcode = new PacketCode();

// $codecontent = file_get_contents(‘example_packetcode.txt‘);
// $packetcode->get_formatcode($codecontent);
// $packetcode->create_codefile();

/**
 * 从目录生成包的代码
 * @param string $indir
 * @param string $outdir
 */
function createcode_fromdir($indir = NULL, $outdir = NULL) {
  if (NULL == $indir || NULL == $outdir) return false;
  $starttime = time();
  $totalfile = 0;
  $successfile = 0;
  $formatcode_files = glob($indir.‘*.txt‘);
  $totalfile = count($formatcode_files);
  if (0 === $totalfile) return false;
  $packetcode = new PacketCode();
  foreach ($formatcode_files as $file) {
    $codecontent = file_get_contents($file);
    $packetcode->get_formatcode($codecontent);
    $result = $packetcode->create_codefile($outdir);
    if (true === $result) ++$successfile;
  }
  $endtime = time();
  echo ‘create code completed, use time: ‘,$endtime - $starttime,‘s‘,LF;
  echo ‘toalfile: ‘,$totalfile,‘ success file: ‘,$successfile,LF;
  unset($packetcode);
}

/**
 * enter function
 * @param void
 * @return void
 */
function main() {
  $argc = $GLOBALS[‘argc‘];
  $argv = $GLOBALS[‘argv‘];
  $outputdir = ‘./packet/code‘;
  $indir = ‘./packet/txt‘;
  if (3 === $argc) {
    list($indir, $outputdir) = $argv;
  }
  createcode_fromdir(complementpath($indir), complementpath($outputdir));
}

main();

  这是主代码,使用PHP实现,大家不妨用其他语言实现也可。

时间: 2024-10-13 12:44:51

MMORPG大型游戏设计与开发(part6 of net)的相关文章

MMORPG大型游戏设计与开发(UI SYSTEM SHOW)

接下来一段时间,这些文件可能不再更新,期间我会学习和掌握一些前端知识.虽然我非常欣赏剑侠网络版叁和九阴真经的画面,但是那是一个庞大的游戏引擎,一般人是无法窥伺的,除非你是天才而且要拥有机器毫无中断的毅力.我也很羡慕国外诸如刺客信条系列.古墓丽影系列,因为在画面和操作方面都做到了世界级水平,这也正是我想研究其实现原理的原因之一.况且那些大型游戏,在中端机器上运行的都比较流畅,也是我很想弄明白的.至于虚幻引擎,这是给那些大型公司,诸如腾讯拿去折腾的,个人根本用不起,那么只能选择开源了.UI我选择了C

MMORPG大型游戏设计与开发(part1 of net)

网络模块的设计,是大型多人在线游戏中比较重要的一部分.我之所以将网络模块放到最前面,是因为许许多多的开发者面对这一块的时候充满了疑惑,而且也觉得很神秘和深奥.这些我们面对到的困难,其实是由于我们对这方面了解的不足以及太过陌生. 本次设计中参考到了天龙八部/武侠世界的网络模块的设计,进行了封装调整,而且天龙八部其实也参考了韩国经典网游的设计,所以在稳定这方面还是有一定的积累. 在前面的构架中,大家可以看到一次交互的大致流程图,玩家登陆.创建角色.删除角色.选择角色等都由登陆服务器(login)进行

MMORPG大型游戏设计与开发(规范)

一件事如果没有规范.章法,那么做这件事起来往往会遇到许多难题,特别是在多人协作的时候,没有到规范通常让每个人多多少少都面临着头疼的困难.举个例子,多个人要做一桌美味的饺子,有买材料.做面皮.弄肉(菜)馅等.如果没有分工,做面皮的人也可以去弄肉馅,买材料的人也可以由弄肉馅去,这样一来可能导致这一桌香喷喷的饺子做的极慢,而且很可能导致这几个人各怀意见.所以如果规定了谁去做某件事,则大家都无异议,效率上也就不言而喻了. 1.目录规范 不同的语言有着不同的目录结构设计,但是一定要记住:区分模块功能.目录

MMORPG大型游戏设计与开发(服务器 AI 概述)

游戏世界中我们拥有许多对象,常见的就是角色自身以及怪物和NPC,我们可以见到怪物和NPC拥有许多的行为,比如说怪物常常见到敌对的玩家就会攻击一样,又如一些NPC来游戏世界中走来走去,又有些怪物和NPC有的时候还会发出一些奇怪的谈论.我们都知道物体是死的,没有生命的,程序其实就是一种物体,它本身是不会进行任何的操作的,比如场景中的角色我们不操作则傻站着一样.但是NPC和怪物似乎有自己的判断力,谁该打谁不该打,还会排队行走,这些不是有生命的才能实现的吗?这就是AI,全称为Actificial Int

MMORPG大型游戏设计与开发(服务器 游戏场景 地图和区域)

地图的数据以及区域的信息是场景的重要组成部分,这些数据同时存在客户端和服务器,而且都是由编辑器生成的.那么保存的文件数据结构是怎样的?一张3D的场景地图又是怎样处理这些数据的?同时告诉大家这里同样只是讲的理论与设计,理论和设计往往都很空洞,但是却很灵活,需要靠每个人怎么运用. 一些图片 区域和格子 从上面的截图可以看出游戏场景其实是由格子来区分的,不管是矩形的格子还是其他形状的格子也好,一张地图不可能只有一个点(即多点组成一张地图).在3D场景中似乎格子的位置总伴随着高度信息,所以让人感觉有些迷

MMORPG大型游戏设计与开发(客户端架构)

首先为所有等待的朋友说一声歉意,实在让大家等的太久.客户端的设计本来就是一个大的工程,而且工作的关系,也没有太多时间在这方面做研究.不过在私下有空的时间,我还是继续着这方面的研究,很遗憾没有用期望的ogre+cegui最新的版本作为开发,这方面原因是新的版本资料实在不多,对于没有什么经验的人来说实在是一大难事,所以最终选择了同天龙八部/武侠世界版本接近的源码作为开发.好了,废话不多说,今天好介绍的是客户端的基本构架,天龙八部/武侠世界的设计模式. CLIENT 功能实现 本次功能实现了vengi

MMORPG大型游戏设计与开发(服务器 AI 基础接口)

一个模块都往往需要统一的接口支持,特别是对于非常大型的模块,基础结构的统一性非常重要,它往往决定了其扩展对象的通用性.昨天说了AI的基本概述以及组成,作为与场景模块中核心一样重要的地位,基础部分的设计尽量的统一.详细.通用.精简. 游戏截图 基础接口(base) 1.管理方法 初始化(init).释放(release).获得NPC队伍指针(get npc team).内部逻辑循环函数(activate). 2.状态方法(ing) 休闲(idle).闲逛(wander).巡逻(partol).警戒

MMORPG大型游戏设计与开发(服务器 游戏场景 搜索机)

双十一注定是忙碌的日子,所以到了现在我才将今天自己学习的内容拿出来跟大家分享.搜索机是我自己暂时取的名字,其实简单的说就是场景里提供搜索的一个工具,负责场景对象的范围搜索和获取.空洞的理论总是让人一头雾水,如果玩过游戏的朋友不妨想一想查看附近的玩家.选择附近的玩家.点击任务怪物名称就可以自动寻路打怪这些功能就大致有个印象了. 一张截图 搜索机 1.数据 1. 状态 typedef enum operator_status_enum { kOperatorStatusContinue, //扫描继

MMORPG大型游戏设计与开发(服务器 游戏场景 聊天管道和寻路器)

又快到双十一,又是不少同仁们出血的日子,首先希望大家玩的开心.我曾经想要仔细的剖析场景的的每个组件,就像这里的聊天管道与寻路器,但是仔细阅读别人代码的时候才发现元件虽小但是实现并不简单,因为有些东西还没有完全想明白我就暂时不说其具体的实现过程,但是我会保证这些文章在后面会不断更新,同时也希望对这方面有兴趣和经验的朋友们能够指正.聊天这个我们都知道,因为它几乎成了游戏或是生活中不可或缺的一部分,那么什么是寻路器?我想未必大家都知道什么是寻路器,不过我可以告诉大家的是你在游戏中自动寻路这个功能就是由