wow魔兽世界服务端主体结构

服务端主要由三大块组成,数据库、服务端逻辑、脚本。数据库用的MySQL,这里不是很关键暂且不说。脚本有自己的脚本引擎,简单的任务、战斗等都可以通过数据库配置相应条目来完成,复杂的战斗AI等在脚本库中由C++直接写成,这个脚本库是要被编译为机器代码的,执行效率相当高效,例如巫妖王的战斗比较复杂就用C++写,其它简单的就配置在数据库中由脚本引擎来驱动执行。AZ服务端是一个多线程、逻辑单线程的服务端。每个线程内部都采用循环结构,主线程启动后将创建多个工作线程,主要包括负责游戏世界运作的核心线程,具有处理用户请求,执行定时器的能力。其它几个工作线程还有网络Io,该线程启动后其内部将使用线程池进行网络Io操作,不间断地接收数据包,并存储到相关玩家的消息队列中,由世界线程进行处理,其它几个工作线程先不讨论,看mangos的源代码.务端启动后这些线程将永不停息地工作。世界线程是服务器的核心,负责处理所有玩家操作请求,定时器、AI等。以下是世界线程启动后执行的代码:

/// Heartbeat for the Worldvoid WorldRunnable::run()

{

///- Init new SQL thread for the world database

WorldDatabase.ThreadStart(); // let thread do safe mySQL requests (one connection call enough) sWorld.InitResultQueue();

uint32 realCurrTime = 0;

uint32 realPrevTime = WorldTimer::tick();

uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST

///- While we have not World::m_stopEvent, update the world

while (!World::IsStopped())

{

++World::m_worldLoopCounter;

realCurrTime = WorldTimer::getMSTime();

uint32 diff = WorldTimer::tick();

sWorld.Update(diff);

realPrevTime = realCurrTime;

// diff (D0) include time of previous sleep (d0) + tick time (t0)

// we want that next d1 + t1 == WORLD_SLEEP_CONST

// we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement

// d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0

if (diff <= WORLD_SLEEP_CONST + prevSleepTime)

{

prevSleepTime = WORLD_SLEEP_CONST + prevSleepTime - diff;

ACE_Based::Thread::Sleep(prevSleepTime);

}

else

prevSleepTime = 0;

#ifdef WIN32

if (m_ServiceStatus == 0) World::StopNow(SHUTDOWN_EXIT_CODE);

while (m_ServiceStatus == 2) Sleep(1000);#endif

}

sWorld.CleanupsBeforeStop();

sWorldSocketMgr->StopNetwork();

MapManager::Instance().UnloadAll(); // unload all grids (including locked in memory)

///- End the database thread

WorldDatabase.ThreadEnd(); // free mySQL thread resources

}

这里先作一下说明,这是世界线程的根循环结构,在while(!World::IsStopped())内部只有一个核心函数调用,其他都是一些控制更新时间之类的代码,不用太关注:

sWorld.Update(diff);

sWorld是单一实例的World对象,它代表了整个游戏世界,和多数MMORPG一样,启动后进入根循环,在运行内部一直调用更新整个游戏世界的Update函数,服务端不停的Update游戏世界,每次Update能在100毫秒内完成,则客户端会感到非常流畅。在根循环退出后,清理服务器相关资源,线程结束被回收。

到这里仅仅需要关注一个函数了,就是World的Update方法内部到底在干什么?

void World::Update(uint32 diff)

{

///- Update the different timers

for (int i = 0; i < WUPDATE_COUNT; ++i)

{

if (m_timers[i].GetCurrent() >= 0)

m_timers[i].Update(diff);

else

m_timers[i].SetCurrent(0);

}

///- Update the game time and check for shutdown time _UpdateGameTime();

///-Update mass mailer tasks if any sMassMailMgr.Update();

/// Handle daily quests reset time

if (m_gameTime > m_NextDailyQuestReset)

ResetDailyQuests();

/// Handle weekly quests reset time

if (m_gameTime > m_NextWeeklyQuestReset)

ResetWeeklyQuests();

/// Handle monthly quests reset time

if (m_gameTime > m_NextMonthlyQuestReset)

ResetMonthlyQuests();

/// Handle monthly quests reset time

if (m_gameTime > m_NextCurrencyReset)

ResetCurrencyWeekCounts();

/// <ul><li> Handle auctions when the timer has passed

if (m_timers[WUPDATE_AUCTIONS].Passed())

{

m_timers[WUPDATE_AUCTIONS].Reset();

///- Update mails (return old mails with item, or delete them)

//(tested... works on win)

if (++mail_timer > mail_timer_expires)

{

mail_timer = 0;

sObjectMgr.ReturnOrDeleteOldMails(true);

}

///- Handle expired auctions sAuctionMgr.Update();

}

/// <li> Handle AHBot operations

if (m_timers[WUPDATE_AHBOT].Passed())

{

sAuctionBot.Update();

m_timers[WUPDATE_AHBOT].Reset();

}

/// <li> Handle session updates UpdateSessions(diff);

/// <li> Handle weather updates when the timer has passed

if (m_timers[WUPDATE_WEATHERS].Passed())

{

///- Send an update signal to Weather objects

for (WeatherMap::iterator itr = m_weathers.begin(); itr != m_weathers.end();)

{

///- and remove Weather objects for zones with no player

// As interval > WorldTick

if (!itr->second->Update(m_timers[WUPDATE_WEATHERS].GetInterval()))

{

delete itr->second;

m_weathers.erase(itr++);

}

else

++itr;

}

m_timers[WUPDATE_WEATHERS].SetCurrent(0);

}

/// <li> Update uptime table

if (m_timers[WUPDATE_UPTIME].Passed())

{

uint32 tmpDiff = uint32(m_gameTime - m_startTime);

uint32 maxClientsNum = GetMaxActiveSessionCount();

m_timers[WUPDATE_UPTIME].Reset();

LoginDatabase.PExecute("UPDATE uptime SET uptime = %u, maxplayers = %u WHERE realmid = %u AND starttime = " UI64FMTD, tmpDiff, maxClientsNum, realmID, uint64(m_startTime));

}

/// <li> Handle all other objects

///- Update objects (maps, transport, creatures,...) sMapMgr.Update(diff);

sBattleGroundMgr.Update(diff);

sOutdoorPvPMgr.Update(diff);

///- Delete all characters which have been deleted X days before

if (m_timers[WUPDATE_DELETECHARS].Passed())

{

m_timers[WUPDATE_DELETECHARS].Reset();

Player::DeleteOldCharacters();

}

// execute callbacks from sql queries that were queued recently UpdateResultQueue();

///- Erase corpses once every 20 minutes

//每20分钟清除尸体

if (m_timers[WUPDATE_CORPSES].Passed())

{

m_timers[WUPDATE_CORPSES].Reset();

sObjectAccessor.RemoveOldCorpses();

}

///- Process Game events when necessary

//处理游戏事件

if (m_timers[WUPDATE_EVENTS].Passed())

{

m_timers[WUPDATE_EVENTS].Reset(); // to give time for Update() to be processed

uint32 nextGameEvent = sGameEventMgr.Update();

m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);

m_timers[WUPDATE_EVENTS].Reset();

}

/// </ul>

///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove" sMapMgr.RemoveAllObjectsInRemoveList();

// update the instance reset times sMapPersistentStateMgr.Update();

// And last, but not least handle the issued cli commands ProcessCliCommands();

// cleanup unused GridMap objects as well as VMaps sTerrainMgr.Update(diff);

}

这是World::Update函数的全部代码,服务器循环执行这些代码,每一次执行就能更新一次游戏世界。这个函数看似比较长,实际上不算很长,其中的关键之处在于首先是根据定时器来执行特定的任务,而执行这些任务则是通过调用各个模块的Manager来完成,比如游戏世界里面的尸体每20分钟清除一次,就检测相关的定时器是否超时,超时则清理尸体,然后重置定时器。通过这些定时器,来执行游戏中由服务器主动完成的任务,这些任务基本上是通过定时器来启动的。游戏中的天气系统、PvP系统、地形系统等等都根据定时器指定的频率进行更新。除了更新各个模块之外,其中还有个非常重要的调用:

UpdateSessions(diff);

如果翻译过来就是更新所有会话,服务器端为每一个客户端建立一个Session,即会话,它是客户端与服务端沟通的通道,取数据、发数据都得通过这条通道,这样客户端和服务端才能沟通。在mangos的构架中,Session的作用非常重要,但其功能不仅仅取客户端发过来的数据、将服务端数据发给客户端那么简单,后面会继续结束这个Session,很关键的东西,下面是UpdateSessions的具体实现:

void World::UpdateSessions(uint32 diff)

{

///- Add new sessions

WorldSession* sess;

while (addSessQueue.next(sess))

AddSession_(sess);

///- Then send an update signal to remaining ones

for (SessionMap::iterator itr = m_sessions.begin(), next; itr != m_sessions.end(); itr = next)

{

next = itr;

++next;

///- and remove not active sessions from the list

WorldSession* pSession = itr->second;

WorldSessionFilter updater(pSession);

if (!pSession->Update(updater))

{

RemoveQueuedSession(pSession);

m_sessions.erase(itr);

delete pSession;

}

}

}

其内部结构简单,主要遍历所有会话,移除不活动的会话,并调用每个Session的Update函数,达到更新所有Session的目的,有1000玩家在线就会更新1000个会话,前面提到了Session,每个会话的内部都挂载有一个消息队列,这里队列存储着从客户端发过来的数据包,1000个会话就会有1000个数据包队列,队列是由网络模块收到数据包后,将其挂载到相应Sesson的接收队列中,客户端1发来的数据包被挂载到Session1的队列,客户端2的就挂载到Session2的队列中。mangos的架构中Session不止是收发数据的入口,同样也是处理客户端数据的入口,即处理客户端请求的调度中心。每次Update Session的时候,这个Update 函数的内部会取出队列中所有的请求数据,循环地对每一个数据包调用数据包对应的处理代码,即根据数据包的类型(操作码OpCode)调用相应的函数进行处理,而这些“相应的函数”是Session内部的普通成员函数,以HandleXXXXXX开头,为了便于理解,将Session的Update函数主体核心代码写在这里:

bool WorldSession::Update(PacketFilter& updater)

{

///- Retrieve packets from the receive queue and call the appropriate handlers

/// not process packets if socket already closed

WorldPacket* packet = NULL;

while (m_Socket && !m_Socket->IsClosed() && _recvQueue.next(packet, updater))

{

OpcodeHandler const& opHandle = opcodeTable[packet->GetOpcode()];

ExecuteOpcode(opHandle, packet);

}

}

这样看起了比较清楚了,Session在Update的时候,取出所有数据包,每个数据包都有一个操作码,opcode,魔兽模拟器有1600多个操作码,玩家或者服务器的每个操作都有一个对应的操作码,比如攻击某个目标、拾取一件东西、使用某个物品都有操作码,被追加到数据包头部,这样每次取数据包的操作码,就可以查找相应的处理代码来处理这个数据包。

wow魔兽世界服务端主体结构相关推荐

  1. 魔兽世界服务端centos架设

    目前最完美的魔兽世界服务端AzerothCore 60级经典旧世版本 70级燃烧的远征版版本 80级巫妖王之怒版本 85级大地的裂变版本 90级熊猫人之谜版本 100级德拉诺之王版本 R110级军团再 ...

  2. 魔兽世界服务端AzerothCore+Centos系统+docker编译教程

    魔兽世界服务端AzerothCore+Centos系统+docker编译教程 1.1 准备工作 1.1.1 准备 1.1.2 安装软件 1.1.3 下载源码 1.1.4 地图文件 1.2 修改配置文件 ...

  3. 魔兽世界服务端源码各个重要文件详细情况说明——魔兽世界开服

    魔兽服务端开服源文件各文件翻译 很多文件在服务器中我们知道是跟什么有关,但就是不知道其作用是什么.就算我们知道在这些地方中的文件都是有着不小的作用.但是由于不知道各个文件代表的是什么意思所以在面对这些 ...

  4. 基于 azerothcore-wotlk 构建docker wow 335服务端

    介绍 基于 azerothcore-wotlk 构建docker wow 335服务端 软件架构 Ubuntu 20.04 安装教程 1.下载 docker 环境 更换国内apt源,自行选择 清华源 ...

  5. 魔兽世界服务端端新手搭建教程

    明杰也是很久以前开始研究魔兽服务器架设, 主要原因是亚服经常要排队6-7个小时, 去不排除的服和单机没啥区别,以怀旧服玩到10级以后就开始玩335端,一开始也和新入手的人一样云里雾里的, 经过长时间的 ...

  6. Wow魔兽世界服务器搭建详细教程,魔兽世界服务器配置要求

    自从<魔兽世界>国服关服后,很多魔兽老玩家心里都是空落落的,魔兽陪伴了我们十多年,此次关服犹如关上了通往艾泽拉斯大陆的大门. 上帝关上了一扇门,我们也可以自己开扇窗,随着国服关闭,越来越多 ...

  7. 魔兽世界新版本-逍遥魔兽V837-一键服务端

    837新端已经编写了全新的一键端工具,比335版的要好看很多,如果是win10系统,还会有半透明效果.本端架设比较简单,如果采用推荐的目录架设,基本是解压即玩,详细的架设教程请看"架设说明& ...

  8. mysql修改元宝,端游[君·天下]高仿魔兽世界一键启动服务端+配套客户端+元宝金币修改教程等...

    资源说明: 1.本资源为君·天下OL单机版一键启动服务端,此游戏高仿魔兽世界. 2.四大职业:武修.羽箭.仙道.真巫. 3.慈善系统.变身系统.召唤系统.动态副本-单人进入.配对系统.狩猎时装.商城. ...

  9. Windows编译安装AzerothCore魔兽世界开源服务端Lua脚本引擎Eluna和防作弊anticheat模块教程

    Windows编译安装AzerothCore魔兽世界开源服务端Lua脚本引擎Eluna和防作弊anticheat模块教程 大家好,我是艾西今天和大家聊聊魔兽世界游戏内的脚步以及防作弊模块 Eluna是 ...

最新文章

  1. CVPR2020 Oral | 华为开源只有加法的神经网络,实习生领衔,效果不输传统CNN
  2. 关于CENTOS6.X下修改网卡名称的方法
  3. ubuntu 安装spark
  4. PyCharm——运行错误【Process finished with exit code -1073741819 (0xC0000005)】解决方案之一
  5. [BUUCTF-pwn]——wustctf2020_name_your_dog
  6. Ubuntu18.10的chrome免输入密码
  7. ReviewForJob——算法设计技巧(贪婪算法+分治算法+动态规划)
  8. Java接口–历年来Java 9之旅–默认和私有方法
  9. json qbytearray 串 转_JSON数据采集网关,json转Modbus RTU串IO口RS485转4~20mA边缘计算智能终端...
  10. 传入一个MapString,Long 返回它按value排序后的结果
  11. php元素浮动会产生哪些影响,css浮动带来什么问题
  12. 正则表达式 RegularExpressions
  13. java语言程序设计复习重点_java语言程序设计期末复习要点
  14. 用按键精灵2014怎么开发后台自动喊话的游戏脚本
  15. Python数值和偏微分方程解
  16. 王松波 计算机科学,王松波-华南农业大学华南农业大学动物科学学院
  17. 利用metadata-extractor定向获取图片拍摄时间以及宽高
  18. NFT因无意义而美丽
  19. PHP高性能编程-提高PHP速度-加速PHP执行-PHP性能优化实践
  20. 1186: 零起点学算法93——改革春风吹满地(C)

热门文章

  1. 关于switch的用法
  2. 开源技术_开源,超越技术
  3. Android设置/配置页面,androidx.preference的使用
  4. UE4Android聚光灯投影,Unreal Engine 4 高级灯光实战教学V2
  5. 【android】AS中使用Junit单元测试和Android JUnit 单元测试
  6. oledbconnection java_如何使用C#和OleDbConnection读取.xlsx和.xls文件?
  7. lan和adsl是什么信号_宽带(ADSL)和宽带(LAN)都分别是什么意思??
  8. Android app 读取xls和xlsx格式的excel文件
  9. 巴比特 | 元宇宙每日必读:一文起底BAT数藏“发迹史”,第一梯队的竞争优势能否保持下去?...
  10. 【学步者日记】C#反射中NonPublic和Instance需要一起使用