MaNGOS 下载,编译,配置和运行的基本步骤

http://hi.baidu.com/zyy503950958/home

   下载和安装msysgit,用于代码管理。我使用的是Git-1.6.5.1-preview20091022.exe

  1. 下载和安装tortoisegit,用于代码管理。我使用的是TortoiseGit-1.3.2.0-32bit.msi 
  2. 使用git://github.com/mangos/mangos.git,从github提取mangos代码。
    1. 采用Git GUI工具的Clone Existing Repository,得到 mangos代码。(我的是9560,安装UDB参考
  3. 使用git://github.com/scriptdev2/scriptdev2.git,从github提取scriptdev2代码。
    1. 采用Git GUI工具的Clone Existing Repository,得到 scriptdev2代码.。
  4. 编译mangos。我用的是VC9,打开mangos\win\mangosdVC90.sln进行构造,构造完成后,会得到mangos\bin\Win32_Debug文件夹。
  5. 编译scriptdev2。我用的是VC9,打开mangos\win\scriptdev2VC90.sln进行构造,构造完成后,会得到scriptdev2\bin\Win32_Debug文件夹。也可参考
    pandore
  6. 拷贝mangos\src\mangosd目录下的mangosd.conf.dist.in为mangos\bin\Win32_Debug\mangosd.conf。
  7. 拷贝mangos\src\realmd目录下的realmd.conf.dist.in为mangos\bin\Win32_Debug\realmd.conf。
  8. http://www.wowtaiwan.com.tw/下载和安装台服WOW,并升级到最新版本。我使用的是台服WOW 3.3.2 build 11403。
  9. Maps, VMAPs and DBCs,采用MaNGOS的工具ad从wow的MPQ中抽取map,得到的所有的map数据文件,文件命名规范为map_id(3位) tileY(2位) tileX(2位).map,如文件名为0002035.map,代表的是Azeroth(地图id为000,tile坐标为(35,20). 注:WOW客户端的Tile对应mangos中的grid,WOW客户端的Chunk对应mangos中的cell(1cell = 4 chunk)。
  10. 按照常规流程(包括建立数据库和配置服务器)把服务器跑起来,使用account create zzh1234567 zzh1234567 创建一个账号,使用account set gmlevel zzh1234567 3设置为超级用户。
  11. 配置好客户端后,运行WOW,顺利登陆,呵呵
  12. 通过安装UDB来丰富场景。FULL DB 9560 : HERE,参考,在我安装DB9560的时候,发现Mangos在LoadCreatureAddons的时候,加载creature template addons出错,Mangos只要求creature template addons有7个字段,而creature template addons有9个字段。我现在只是简单地跳过LoadCreatureAddons的调用。

image

14. 如果要用PerfHUB来调试,可以二进制打开wow.exe,把其中的53 52 6A 01 6A 00 50 8B 41 40改成53 52 6A 02 6A 01 50 8B 41 40后另存。具体可以参考用PerfHUD来调试商业游戏。改完后,托exe到PerfHUB的图标上就ok了。

运行Mangos (运行Mangos一节过期,是以前我针对国服3.1.3版本进行的安装配置)

  1. Compile MaNGOS On Windows
    1. Download (MaNGOS 0.14 release)
    2. Compiling MaNGOS using Visual C++ 2008 (.\win\mangosdVC90.sln)
  2. 按照MaNGOS的MySQL推荐进行安装mysql-5.1.40-win32.msi (配置时候打开端口3306)
  3. Setting up MaNGOS
    1. Binaries and Configuration (There are two .conf files, in src/mangosd/mangosd.conf.dist.in and src/realmd/realmd.conf.dist.in. When you copy this to your MaNGOS folder you need to get rid of the .dist.in on the end of both files.)
    2. Maps, VMAPs and DBCs,采用MaNGOS的工具ad从wow的MPQ中抽取map,得到的所有的map数据文件,文件命名规范为map_id(3位) tileY(2位) tileX(2位).map,如文件名为0002035.map,代表的是Azeroth(地图id为000,tile坐标为(35,20). 注:WOW客户端的Tile对应mangos中的grid,WOW客户端的Chunk对应mangos中的cell(1cell = 4 chunk)。
  4. 安装wow (网易的wow 3.1.3 10146版本)
  5. 修改AuthCodes.h #define EXPECTED_MANGOS_CLIENT_BUILD        {10146,9947, 0}
  6. 按照正常流程把服务器跑起来,使用account create zzh1234567 zzh1234567 创建一个账号
  7. 配置好客户端后,运行WOW,顺利登陆,呵呵

image

Mangos代码阅读

Mangos有13个工程。

使用了4个外部工具库,分别是:

  1. 跨平台的网络通讯框架The ADAPTIVE Communication Environment (ACE)
  2. 压缩库zlib
  3. Socket通信库 C++ Sockets Library (使用在realmd工程中,和使用在Mangosd工程中的RASocket,负责处理Remote Administration。其他地方没有使用到这个C++ Sockets Library )。发现在C++ Sockets Library的TcpSocket::Open中存在一个问题,在n = connect(s, ad, ad);语句执行后,如果n=-1,C++ Sockets Library会检测是否ERR为WSAEWOULDBLOCK,否则表示成功,但在动态库中使用TcpSocket的时候,我发现n = connect(s, ad, ad);语句执行后,n=-1,ERR会为0,这个时候连接也是成功了,但TcpSocket::Open会当做不成功处理。我发现这个问题,但没有时间去探究原因,也许并不是一个问题。
  4. C++的并行编程模板库Threading Building Blocks (tbb 和 tbbmalloc)

Mangos的实现分为:登录服务器(realmd)和世界服务器(mangosd+game)。realmd和mangos共用了Mangos公共库(shared)。

工程shared

提供了通用功能,包括了数据库的封装类,实现了对MySql的访问,同样,我们可以编写派生类来支持其他的数据库。

工程script

提供了脚本接口,并实现了简单的几个脚本,封装为DLL,提供给game使用,具体可参考:MaNGOS脚本接口

通过使用不同的脚本DLL来替换Mangos中的AI实现,可以让game具有更强的AI。ScriptDev2 就是一个这样的库。ScriptDev2 is a replacement for the Script Library that comes with MaNGOS( http://www.getmangos.com ) written in C++ and is compatible with Windows and Linux. It provides scripts for NPCs, Boss events, and Items currently. Once ScriptDev2 is compiled it is automatically run by MaNGOS on server startup.

image

工程mangosd

mangos是世界服务器的管理器,负责初始化工作和启动世界服务器各层的线程,这些工作主要是由类Master来实现。具体是:

  1. 使用三个数据库对象WorldDatabase和CharacterDatabase和loginDatabase,初始化三大数据库:World Database和Character Database和login Database,并为每个数据库的访问都启动一个DB delay threads。具体的数据库操作功能都是由Mangos公共库shared来提供。
  2. 调用sWorld.SetInitialWorldSettings,对World进行初始化,包括加载所有的游戏数据和初始化各种更新定时器和邮件定时器,还有些其他的初始化工作。类World的成员函数SetInitialWorldSettings调用成员函数LoadConfigSettings解析mangosd.conf,解析后内容放入uint32 m_configs[CONFIG_VALUE_COUNT]中。
    1. 加载的游戏数据有:
      1. DBC数据
      2. Objects数据
      3. Spells数据
      4. Pooling数据
      5. Game Event数据
      6. loot数据
      7. 技能数据
      8. 所有其他的游戏数据,包括Waypoints和Trainers等等等。
      9. 脚本数据
    2. 初始化的更新定时器有:
      1. WUPDATE_OBJECTS
      2. WUPDATE_SESSIONS
      3. WUPDATE_AUCTIONS
      4. WUPDATE_WEATHERS
      5. WUPDATE_UPTIME
      6. WUPDATE_CORPSES
      7. WUPDATE_EVENTS
    3. 其他的初始化工作有:
      1. 初始化MapManager,启动Map System
      2. 初始化Battlegrounds,启动BattleGround System
      3. 初始化DailyQuestResetTime
      4. 初始化sGameEventMgr,Starting Game Event system
  3. 类Master还实现了mangos server的退出管理
  4. 类Master启动WorldRunnable,开始游戏逻辑。Heartbeat for the World,由Master创建,并设置线程为最高优先级。
  5. 类Master启动CliRunnable:Command Line Interface handling thread,由Master创建。CliRunnable运行时候会生成一个WorldDatabase线程,在接收到输入后会调用sWorld.QueueCliCommand把Cmd放入到World::cliCmdQueue中。
  6. 类Master启动RARunnable:Remote Administration,由Master创建
  7. 如果配置要求,类Master还会启动一个freeze catcher thread
  8. 类Master启动2个网络线程ReactorRunnable(可配置数目)

mangosd的线程总共有(1+3+1+1+1+2 +1 =10)10个线程。

  1. 主线程Master
  2. 2个网络线程ReactorRunnable(可配置数目)(网络层)
  3. 一个World线程(逻辑层)
  4. 三个DB线程(数据层)
  5. 一个CLI线程(输入层),运行时候会生成一个WorldDatabase线程
  6. 一个RA线程(管理层)
  7. 一个freeze catcher 线程(可选)

工程g3dlite:游戏逻辑层的底层库

工程framework:系统框架

工程realm

负责登陆和选择游戏服务器,进行负载均衡。用到了C++ Sockets Library进行登录处理,采用select I/O模型。实现了Wow, Mangos登录时的SRP6认证。客户端作为它的client连接到realm server认证和选择了mangos server就断开。 而mangos server和realm server则不进行连接,只是通过数据库交互数据:mangos server把自己的状态和拥有的角色数放入库中。realm server会读取数据库中的这些信息来获知mangos server的状态。

  1. 数据库realm的realmlist表保存了realm的列表
  2. realm通过如下事件处理函数来负责登陆和选择游戏服务器。

    const AuthHandler table[] =
    {
        { AUTH_LOGON_CHALLENGE,     STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge    },
        { AUTH_LOGON_PROOF,         STATUS_CONNECTED, &AuthSocket::_HandleLogonProof        },
        { AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleReconnectChallenge},
        { AUTH_RECONNECT_PROOF,     STATUS_CONNECTED, &AuthSocket::_HandleReconnectProof    },
        { REALM_LIST,               STATUS_AUTHED,    &AuthSocket::_HandleRealmList         },
        { XFER_ACCEPT,              STATUS_CONNECTED, &AuthSocket::_HandleXferAccept        },
        { XFER_RESUME,              STATUS_CONNECTED, &AuthSocket::_HandleXferResume        },
        { XFER_CANCEL,              STATUS_CONNECTED, &AuthSocket::_HandleXferCancel        }
    };

登录处理:

user登录到realm server进行身份认证,并选择登录上哪个mangos server。user登录到mangos server后,将不再和realm server交互。

参考: Wow 服务器解析

工程game

game:是Mangos的核心代码,网络层和逻辑层代码(采用了ACE反应器(Reactor)模式

网络层:

  1. WorldSocket :负责网络IO,而类WorldSession负责逻辑处理。WorldSocket和WorldSession分别在独立的线程ReactorRunnable和WorldRunnable中运行,使用WorldSession中的消息队列_recvQueue来进行数据缓冲。在WorldSocket接收到网咯输入后,会调用m_Session->QueuePacket (new_pct);把网络包放入WorldSession的_recvQueue。所以,可以看到WorldSocket 是Mangos game的网络层,而WorldSession是逻辑处理层。WorldSocketMgr是网络层的一个管理器,它负责指派WorldSocket归哪个ReactorRunnable管理(Mangos可创建多个ReactorRunnable,缺省是2个)。
  2. WorldSocketMgr管理所有的连接WorldSocket。WorldSocketMgr的WorldSocketMgr::StartNetwork对8085(缺省)端口进行侦听。

逻辑层:

  1. 类World实现了wow的World,所有的逻辑处理。逻辑处理的循环是在World::Update中。循环处理包括:
    1. 刷新更新定时器
    2. 刷新游戏定时器和处理游戏关闭
    3. 处理日常任务
    4. 处理拍卖
    5. 刷新Sessions。World::UpdateSessions会调用所有WorldSession的WorldSession::Update。在WorldSession::Update中进行逻辑处理。
    6. 处理天气
    7. 刷新uptime table
    8. 刷新Objects,包括maps,transport,creatures,,,,
    9. 刷新所有running battlegrounds
    10. 刷新SqlResultQueue, 逻辑层和数据层是通过Queue来进行异步操作的。(用了AsyncPQuery和SqlResultQueue)
    11. 处理尸体移除
    12. 处理游戏事件
    13. 处理 Move all creatures with "delayed move" and remove and delete all objects with "delayed remove"
    14. 处理InstanceSaveManager的刷新
    15. 调用World::ProcessCliCommands,处理CLI。从cliCmdQueue取得cmd进行解析执行。所有有效的Cmd,都可以在ChatHandler::getCommandTable中找到。
  2. 类WorldSession: 类WorldSession负责逻辑处理。
    1. void WorldSession::SendPacket(WorldPacket const* packet) 负责发包给客户端,直接发包,没有输出缓冲队列。
    2. 在WorldSession::Update中进行逻辑处理。World::UpdateSessions会调用所有WorldSession的WorldSession::Update。
      1. 执行语句OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];得到opHandle
      2. 根据得到的opHandle,执行(this->*opHandle.handler)(*packet);
    3. WorldSession::HandlePlayerLogin处理玩家登陆游戏。
      1. 构建Player
      2. Player::LoadFromDB从数据库中加载玩家数据。在Player::LoadFromDB中会调用SetMap(MapManager::Instance().CreateMap(GetMapId(), this));加载当前player所在的map
  3. Player::SetPosition在Player运动的时候,改变位置,保存处理夸区。
  4. 类MapManager
  5. 类Map实现了一个state machine,采用state pattern组织了Gid的4个state object:InvalidState;ActiveState;IdleState;RemovalState。  

game中的定时器有:

/// Timers for different object refresh rates
enum WorldTimers
{
    WUPDATE_OBJECTS     = 0, // 间隔为0
    WUPDATE_SESSIONS    = 1, // 间隔为0
    WUPDATE_AUCTIONS    = 2,// 间隔为60秒
    WUPDATE_WEATHERS    = 3,// 间隔为1秒
    WUPDATE_UPTIME      = 4,
    WUPDATE_CORPSES     = 5,
    WUPDATE_EVENTS      = 6,
    WUPDATE_COUNT       = 7
};

game中的管理器有:

  1. ObjectMgr : MaNGOS 中的Object Management
    1. ObjectMgr::LoadMangosStrings: 把mMangosStringLocaleMap 关联到mangos_string table
    2. ObjectMgr::LoadScriptNames:m_scriptNames 关联到tables: creature_template;gameobject_template;item_template;areatrigger_scripts;instance_template
    3. mCreatureLocaleMap 关联到locales_creature table
    4. mGameObjectLocaleMap关联到locales_gameobject table
    5. mItemLocaleMap关联到locales_item table
    6. mQuestLocaleMap –> locales_quest
    7. mNpcTextLocaleMap –> locales_npc_text
    8. mPageTextLocaleMap –> locales_page_text
    9. mGossipMenuItemsLocaleMap –> locales_gossip_menu_option
    10. mPointOfInterestLocaleMap –> locales_points_of_interest
  2. GameEventMgr
  3. BattleGroundMgr
  4. AccountMgr
  5. InstanceSaveManager
  6. SpellMgr
  7. GMTicketMgr
  8. PoolManager
  9. WaypointManager
  10. CreatureEventAIMgr
  11. AuctionHouseMgr
  12. LootValidatorRefManager


对象类层次

对象的类层次如下,所有的Object都由ObjectMgr进行管理。ObjectMgr以GUID方式,管理了characters,creature,item_instance,gameobject,auctionhouse,mail,item_text,corpse,arena_team,character_equipmentsets。

image

image

// indexes of BattlemasterList.dbc
enum BattleGroundTypeId
{
    BATTLEGROUND_TYPE_NONE     = 0,
    BATTLEGROUND_AV            = 1,
    BATTLEGROUND_WS            = 2,
    BATTLEGROUND_AB            = 3,
    BATTLEGROUND_NA            = 4,
    BATTLEGROUND_BE            = 5,
    BATTLEGROUND_AA            = 6,
    BATTLEGROUND_EY            = 7,
    BATTLEGROUND_RL            = 8,
    BATTLEGROUND_SA            = 9,
    BATTLEGROUND_DS            = 10,
    BATTLEGROUND_RV            = 11,
    BATTLEGROUND_IC            = 30,
    BATTLEGROUND_ABG           = 32
};

战场代码名字解释

AB - Arathi Basin                    ALX
AV - Alterac Valley               AS
Ey - Eye of the Storm             FB
BE - Blade Edge arena      刀锋山竞技场
AA - dunno :]                       
RL - Ruins of Lordareon arena 洛丹伦废墟竞技场
NA - Nagrand Arena 纳格兰竞技场

image

image

Player状态

/// Player state
enum SessionStatus
{
    STATUS_AUTHED = 0,                                      ///< Player authenticated (_player==NULL, m_playerRecentlyLogout = false or will be reset before handler call, m_GUID have garbage)
    STATUS_LOGGEDIN,                                        ///< Player in game (_player!=NULL, m_GUID == _player->GetGUID(), inWorld())
    STATUS_TRANSFER,                                        ///< Player transferring to another map (_player!=NULL, m_GUID == _player->GetGUID(), !inWorld())
    STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT,                    ///< _player!= NULL or _player==NULL && m_playerRecentlyLogout, m_GUID store last _player guid)
    STATUS_NEVER                                            ///< Opcode not accepted from client (deprecated or server side only)
};

生物状态

enum DeathState
{
    ALIVE       = 0,
    JUST_DIED   = 1,
    CORPSE      = 2,
    DEAD        = 3,
    JUST_ALIVED = 4,
    DEAD_FALLING= 5
};

玩家登陆

服务器端在连接打开后,会发SMSG_AUTH_CHALLENGE到客户端。客户端从服务器端发送回来的种子和 SRP6 数据中产生随机种子,生成 SHA1 字符串,用这些数据生成 CMSG_AUITH_SESSION 数据包,发送给服务端。这个过程是没有经过加密的。

image

  1. 客户端发送SMSG_AUTH_SESSION到服务器
  2. 服务器处理SMSG_AUTH_SESSION
  3. 服务器发送SMSG_AUTH_RESPONSE给客户端
  4. 服务器发送SMSG_ADDON_INFO给客户端
  5. 服务器发送SMSG_CLIENTCACHE_VERSION给客户端
  6. 服务器发送SMSG_TUTORIAL_FLAGS给客户端

packet结构

SMSG_AUTH_SESSION 是client packet有一个头部(ClientPktHeader),后面是数据块。

SMSG_AUTH_RESPONSE 是server packet有一个头部(ServicePktHeader),后面是数据块。

SMSG_AUTH_RESPONSE 的包组织

SMSG_AUTH_RESPONSE 的opcode是01EE,ByteBuffer大小为1 + 4 + 1 + 4 + 1=11。一个SMSG_AUTH_RESPONSE 的数据如下:

  • 8:AUTH_OK
  • 32:BillingTimeRemaining
  • 8:BillingPlanFlags
  • 32:BillingTimeRested
  • 8:0 - normal, 1 – TBC

在构造了SMSG_AUTH_RESPONSE packet后,WorldSocket::SendPacket会根据SMSG_AUTH_RESPONSE packet构造出一个ServerPktHeader,并对ServerPktHeader中的数据header进行加密发送。加密采用m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength());

ServerPktHeader
size = 0d 00 00 00 header =00 0d ee 01
header加密后:
size = 0d 00 00 00 header =0a e5 0b 39
m_OutBuffer:
header =0a e5 0b 39 contents = 0c 00 00 00 00 00 00 00 00 00 02

WorldSocket::handle_input_header会对从客户端接收来的数据进行解密,解密采用m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr (), sizeof (ClientPktHeader));

角色枚举

  1. 玩家登上服务器后,从客户端发送SMSG_CHAR_ENUM到服务器。
  2. 在服务器端
    1. 根据{ "CMSG_CHAR_ENUM",                               STATUS_AUTHED,   &WorldSession::HandleCharEnumOpcode            },服务器会调用WorldSession::HandleCharEnumOpcode
    2. WorldSession::HandleCharEnumOpcode会调用CharacterDatabase.AsyncPQuery,进行异步查询后,调用CharacterHandler::HandleCharEnumCallback
    3. CharacterHandler::HandleCharEnumCallback会回头调用session->HandleCharEnum(result);
    4. WorldSession::HandleCharEnum根据characters数据库的查询结果,调用Player::BuildEnumData,加载角色数据,构造SMSG_CHAR_ENUM返回包,然后发送回客户端。

image

2010-05-10 19:53

角色创建

image

角色的初始化装备在CharStartOutfit.dbc

角色的创建和选择的设置都在 ChrRaces.dbc

角色的创建属性都在Playercreateinfo,包括出生地和出生属性。

playercreateinfo_item表是创建一个新人物时,人物默认带的所有Item的表

playercreateinfo_spell表是创建一个新人物时,人物默认带的所有Spell的表

角色删除

image

角色进入游戏

发出的消息有:

SMSG_SET_PROFICIENCY

SMSG_CRITERIA_UPDATE

MSG_DUGEON_DIFFICULTY

SMSG_LOGIN_VERIFY_WORLD

SMSG_ACCOUNT_DATA_TIMES

SMSG_FEATURE_SYSTEM_STATUS

SMSG_MOTD

SMSG_LEARNED_DANCE_MOVES

SMSG_BIND_POINT_UPDATE

SMSG_TALENTS_INFO

SMSG_INSTANCE_DIFFICULTY

SMSG_INITIAL_SPELLS

SMSG_UNLEARN_SPELLS

SMSG_ACTION_BUTTONS

SMSG_ACTION_FACTIONS

SMSG_INIT_WORLD_STATES

SMSG_EQUIPMENT_SET_LIST

SMSG_ALL_ACHIEVEMENT_DATA

SMSG_LOGIN_SETTIMESPEED

SMSG_TRIGGER_CINEMA

SMSG_COMPRESSED_UPDATE_OBJECT: 会发送Monster数据

SMSG_MONSTER_MOVE:会发送Monster的移动数据

SMSG_INIT_WORLD_STATES

SMSG_WEATHER

SMSG_TIME_SYNC_REQUEST

SMSG_WEATHER

SMSG_SPELL_GO

SMSG_AURA_UPDATE_ALL

SMSG_UPDATE_ACCOUNT_COMPLETE

SMSG_PLYAED_TIME

SMSG_NAME_QUERY_RESPONSE

SMSG_RAID_INSTANCE_INFO

SMSG_QUERY_TIME_RESPONSE

SMSG_GMTICKET_GETTICKET

SMSG_QUERY_TIME_RESPONSE

SMSG_QUERY_NEXT_MAIL_TIME

SMSG_CALENDAR_SEND_NUM_PENDING

SMSG_WORLD_STATE_UI_TIMER_UPDATE

SMSG_STANDSTATE_UPDATE // 多个

SMSG_CHANNEL_NOTIFY // 多个

SMSG_USERLIST_ADD // 多个

image

看上面的Mangos的Call Stack,可以明白角色进入游戏场景时,Mangos是如何处理的。具体如下:

WorldSession::HandlePlayerLogin:

  1. 根据Player GUID,创建服务器端的Player对象,并从DB中提取玩家信息,设置Player对象的属性。
  2. 把玩家的地理位置(MapId, X, Y, Z, Orientation)发给客户端。
  3. 提取玩家相关的账号信息,并发送这些账号信息给客户端。
  4. 发送一些信息,包括是否启动voice chat和当日的系统消息(MOTD:Message of The Day)。
  5. 发送一些玩家信息,包括等级什么的。
  6. 调用Player::SendInitialPacketsBeforeAddToMap在玩家加入到地图前,做初始化的工作。
  7. 调用Map::Add(Player *player)把玩家加入到地图中。

Map::Add(Player *player) 非常重要。可以知道Mangos是如何按Cell加载地图数据,包括地图上的物品,生物的。WOW客户端会把一些Cell数据缓冲到World of Warcraft\Cache\WDB中。

image

Map::Add(Player *player)

bool Map::Add(Player *player)
{
    player->GetMapRef().link(this, player);
    player->SetMap(this);

    // update player state for other player and visa-versa
    CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
    Cell cell(p);
    EnsureGridLoadedAtEnter(cell, player);
    player->AddToWorld();

    SendInitSelf(player); // 把Player信息(transport,EquipmentSlots信息,BankBagSlots信息,KeyRingSlots信息和CurrencyTokenSlots等)以SMSG_UPDATE_OBJECT发送到客户端
    SendInitTransports(player);

    UpdatePlayerVisibility(player,cell,p);
    UpdateObjectsVisibilityFor(player,cell,p);

    AddNotifier(player,cell,p);
    return true;
}

函数LoadMapAndVMap:按GRID加载map & vmap。

类ObjectGridLoader:按GRID加载Grid上的Objects。在ObjectGridLoader::LoadN中,会生成GridLoader<Player, AllWorldObjectTypes, AllGridObjectTypes> loader,然后调用loader.Load对gird中的Objects进行加载。最终loader.Load会调用到objmgr.GetCellObjectGuids,根据mapid和cell_id得到CellObjectGuids,然后根据CellObjectGuids加载该cell上的objects。参考:CellObjectGuids const& cell_guids = objmgr.GetCellObjectGuids(i_map->GetId(), i_map->GetSpawnMode(), cell_id);

看CellObjectGuids结构

struct CellObjectGuids
{
    CellGuidSet creatures;
    CellGuidSet gameobjects;
    CellCorpseSet corpses;
};

可以知道在一个cell中管理了该cell中的所有creatures和gameobjects和corpses。ObjectMgr中的MapObjectGuids mMapObjectGuids就是按照Cell管理所有的所有creatures和gameobjects和corpses。

在ObjectMgr中的mMapObjectGuids结构是:<mapid, spawnMode> <cell_id, < creatures, gameobjects, corpses> >

void Map::SendInitSelf( Player * player )
{
    sLog.outDetail("Creating player data for himself %u", player->GetGUIDLow());

    UpdateData data;

    // attach to player data current transport data
    if(Transport* transport = player->GetTransport())
    {
        transport->BuildCreateUpdateBlockForPlayer(&data, player);
    }

    // build data for self presence in world at own client (one time for map)
    player->BuildCreateUpdateBlockForPlayer(&data, player); // 把要传给client的信息:Player的equipment slot中的item,KeyRing中的Item,CurrencyToken中的Item,BankItem中的Item,m_movementInfo,Unit speeds,

    // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
    if(Transport* transport = player->GetTransport())
    {
        for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr)
        {
            if(player!=(*itr) && player->HaveAtClient(*itr))
            {
                (*itr)->BuildCreateUpdateBlockForPlayer(&data, player);
            }
        }
    }

    WorldPacket packet;
    data.BuildPacket(&packet);
    player->GetSession()->SendPacket(&packet);
}

void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpair )
{
    MaNGOS::VisibleNotifier notifier(*player);

    cell.data.Part.reserved = ALL_DISTRICT;
    cell.SetNoCreate();
    TypeContainerVisitor<MaNGOS::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
    TypeContainerVisitor<MaNGOS::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
    cell.Visit(cellpair, world_notifier, *this, *player, GetVisibilityDistance());
    cell.Visit(cellpair, grid_notifier, *this, *player, GetVisibilityDistance());

    // send data
    notifier.Notify(); // 这条语句会调用到Creature::SendMonsterMoveWithSpeedToCurrentDestination
}

void Creature::SendMonsterMoveWithSpeedToCurrentDestination(Player* player)
{
    float x, y, z;
    if(GetMotionMaster()->GetDestination(x, y, z)) // 得到当前移动的目的地
        SendMonsterMoveWithSpeed(x, y, z, 0, player);
}

void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime, Player* player)
{
    if (!transitTime)
    {
        if(GetTypeId()==TYPEID_PLAYER)
        {
            Traveller<Player> traveller(*(Player*)this);
            transitTime = traveller.GetTotalTrevelTimeTo(x, y, z);
        }
        else
        {
            Traveller<Creature> traveller(*(Creature*)this);
            transitTime = traveller.GetTotalTrevelTimeTo(x, y, z); // 根据creature的移动方式,得到当前移动到目的地所需要的时间
        }
    }
    //float orientation = (float)atan2((double)dy, (double)dx);
    SplineFlags flags = GetTypeId() == TYPEID_PLAYER ? SPLINEFLAG_WALKMODE : ((Creature*)this)->GetSplineFlags();
    SendMonsterMove(x, y, z, SPLINETYPE_NORMAL, flags, transitTime, player);// 从server发送creature的移动数据(SMSG_MONSTER_MOVE)给client
}

怪物的移动 SMSG_MONSTER_MOVE

image

InstanceMap:Dungeon Instance Maps

BattleGroundMap:Battleground Instance Maps

image

一个Map的Grid有4个状态,分别是:

  1. InvalidState
  2. ActiveState
  3. IdleState
  4. RemovalState

Map::Update

6.MaNGOS 下载,编译,配置和运行的基本步骤
2010-05-10 19:53

移动

常规的移动处理一般分为3层:

  1. 目标设定层:负责设定目标,如去某个地方
  2. 导航层:为了到达目标点,需要怎么做,如路径和移动速度确定。在Unit类中的成员MotionMaster i_motionMaster实现了导航层,具体的各种导航策略由MovementGenerator的类层次实现。
  3. 移动层:移动的方式,如坐车或者滑雪到达。

image


2010-05-10 19:56

mangos不是一个魔兽私服模拟器,它是一个开源的自由软件项目,是用c++和C#编程语言,实现的一个支持大型多人在线角色扮演游戏服务器的程序框架,在这个框架下,它理论上应该支持任何客户端的网络游戏,由于现在很多人使用魔兽世界来对它进行测试,所以针对魔兽世界的脚本和数据库文件比较完善,很多人就利用这个开源项目来实现魔兽私服。   首先,mangos项目是一个开源的自由软件(如同linux或者firefox),并且遵守其中最为严格的GPL协议,也就是保证源代码绝对的自由,要了解什么是开源软件,自由软件请自己搜索这方面的资料。   其次开发小组一再强调,这是个研究,教育性质的对怎样开发大型网游的服务器端有好处的项目,是一个技术细节毫无保留向公众开放的软件,是一件很有意义的事情,如果你使用它作为盈利目的,那你本身就违反了软件的协议。所以任何利用mangos项目进行私服活动的组织和个人都违反了mangos的宗旨,mangos项目也不会对它们负责。   mangos的技术细节上是这样的,核心部分是个和特定游戏没有关系的核心框架程序,主要是进行进程调度,创造世界,建立心跳机制,处理网络接入等。数据库也是使用的开源数据库软件MySQL,编译器使用的是GCC。至于游戏内容数据库游戏人物,时间,世界脚本,都是由这个核心程序所支持的扩展脚本来实现,所以有一些独立出来的项目专门模拟魔兽世界来开发支持mangos的核心程序。现在mangos的核心程序已经放到著名的协同开发网站sourceforge上开发了,使用的版本控制工具是subversion,(目前已经迁移到GIT)。大家都可以从sourceforge的subversion数据库下载最新的源代码程序。   另外有点要注意的是,开源项目是很容易被利用的,有时候有的公司开发了一个软件说是自己做的其实内核都是别人的开源内核,但是公司又不承认,利用了别人的劳动成果赚钱不说,既不回报社区,也对开源自由软件造成了很大的伤害。所以大家注意了,如果看到一个和魔兽世界很像的什么网络游戏?要想想是不是利用的mangos的核心程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值