/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2015 MaNGOS project <http://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#define WORLD_SLEEP_CONST 50
/// Heartbeat for the World
void WorldRunnable::run()
{
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;
}
}
}
这里只保留了世界轮询的逻辑。总的实现思路是在world.update前打点,计算了上次update和睡眠的耗时总和,今次的update放生在总和之后,决定了那些逻辑可以进行可以继续挂起。
值得讨论是上面睡眠逻辑的实现。
为了保证游戏足够得流畅,每秒一定要保证足够的计算次数。上面代码里World_Sleep_Const值为50,也就是说每次世界逻辑计算加上睡眠耗时应该在50毫秒上下,这样保证每次能计算20次世界逻辑。
如果上次耗时总和小于(World_Sleep_Const+prevSleepTime),那么小于多少,就睡眠多少,并修改prevSleepTime为小于的数值;否则立刻进入下一轮轮询,并修改prevSleepTime为0。
update的耗时不可能稳定在一个值,表现为时大时小。耗时多,不必睡眠,应该立进入下一次轮询,多余的耗时应该在以后的睡眠时间里扣掉。这样才能在总体上保证每秒20次计算。
<pre name="code" class="cpp">
if (diff < WORLD_SLEEP_CONST - prevSleepTime) {
prevSleepTime = WORLD_SLEEP_CONST - prevSleepTime - diff;
ACE_Based::Thread::Sleep(prevSleepTime);
preSleepTime = 0;
} else {
prevSleepTime = preSleepTime + (diff - WORLD_SLEEP_CONST) ;
}