Fix respawn/spawn timers racing ahead under multi-threading
POSIX clock() sums CPU time across every thread in the process, unlike Windows' clock() which tracks wall-clock milliseconds. With several worker threads running, respawn/spawn/AI timers based on clock() raced far ahead of real elapsed time. Switch spawn-timer bookkeeping to time(NULL)/time_t (seconds) throughout the world server, and add a portable clock() shim in sockets.h (backed by CLOCK_MONOTONIC, millisecond resolution) for remaining call sites so CLOCKS_PER_SEC==1000 still holds like it did on Windows. Also: fix MapDelay/VisualDelay/WorldDelay being passed to usleep() (us) without converting from their configured milliseconds; fix the shutdown timer likewise; stop leaking the temporary actor used to run an NPC's AIP script; fix CleanDrops() skipping the element that shifts into position after an erase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1f626c6b93
commit
725620e23c
8 files changed
+66
-37
No files matched your search
+25
-2
@@ -62,6 +62,29 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
// The entire codebase was written against Windows' clock(), which returns a
|
||||||
|
// monotonic MILLISECOND counter (CLOCKS_PER_SEC == 1000) that closely tracks
|
||||||
|
// wall-clock time for a normal single-threaded process. POSIX clock() is
|
||||||
|
// completely different: microsecond resolution (CLOCKS_PER_SEC == 1,000,000)
|
||||||
|
// AND it sums CPU time across every thread in the process. With several
|
||||||
|
// worker threads running, POSIX clock() races far ahead of real elapsed
|
||||||
|
// time, breaking every cooldown/timer/AI-tick comparison in the game logic
|
||||||
|
// (movement, attacks, aggro, respawns, buffs, ...). Redirect clock() to a
|
||||||
|
// real wall-clock millisecond counter so CLOCKS_PER_SEC==1000 holds like it
|
||||||
|
// did on Windows, without having to touch every call site individually.
|
||||||
|
#include <time.h>
|
||||||
|
static inline long __rose_portable_clock()
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return (long)(ts.tv_sec * 1000L + ts.tv_nsec / 1000000L);
|
||||||
|
}
|
||||||
|
#undef CLOCKS_PER_SEC
|
||||||
|
#define CLOCKS_PER_SEC 1000
|
||||||
|
#define clock() __rose_portable_clock()
|
||||||
|
#endif
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "pthread.h"
|
#include "pthread.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -78,8 +101,8 @@ typedef char SBYTE;
|
|||||||
typedef unsigned char BYTE;
|
typedef unsigned char BYTE;
|
||||||
typedef short SWORD;
|
typedef short SWORD;
|
||||||
typedef unsigned short WORD;
|
typedef unsigned short WORD;
|
||||||
typedef long SDWORD;
|
typedef int SDWORD;
|
||||||
typedef unsigned long DWORD;
|
typedef unsigned int DWORD;
|
||||||
typedef long long SQWORD;
|
typedef long long SQWORD;
|
||||||
typedef unsigned long long QWORD;
|
typedef unsigned long long QWORD;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ PVOID MapProcess( PVOID TS )
|
|||||||
if(nextmon != 0)
|
if(nextmon != 0)
|
||||||
{
|
{
|
||||||
//Log(MSG_DEBUG,"Spawning TD mobs");
|
//Log(MSG_DEBUG,"Spawning TD mobs");
|
||||||
clock_t etime = clock() - map->lastTDSpawnTime;
|
time_t etime = time(NULL) - map->lastTDSpawnTime;
|
||||||
if(etime >= map->TDMobDelay) // check if elapsed time is greater than spawn delay
|
if(etime >= (time_t)(map->TDMobDelay/1000)) // check if elapsed time (seconds) is greater than spawn delay (TDMobDelay is in ms)
|
||||||
{
|
{
|
||||||
//spawn the next monster on the list
|
//spawn the next monster on the list
|
||||||
//Log(MSG_DEBUG,"Spawning TD mob of type %i at position %i in the list for map %i",nextmon, map->TDNextSpawn, map->id);
|
//Log(MSG_DEBUG,"Spawning TD mob of type %i at position %i in the list for map %i",nextmon, map->TDNextSpawn, map->id);
|
||||||
@@ -130,7 +130,7 @@ PVOID MapProcess( PVOID TS )
|
|||||||
map->TDNextSpawn++; //move the pointer
|
map->TDNextSpawn++; //move the pointer
|
||||||
//Log(MSG_DEBUG,"moved the pointer");
|
//Log(MSG_DEBUG,"moved the pointer");
|
||||||
if(map->TDNextSpawn > 100)map->TDNextSpawn = 0;
|
if(map->TDNextSpawn > 100)map->TDNextSpawn = 0;
|
||||||
map->lastTDSpawnTime = clock();
|
map->lastTDSpawnTime = time(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TD spawn end
|
//TD spawn end
|
||||||
@@ -330,8 +330,8 @@ PVOID MapProcess( PVOID TS )
|
|||||||
//Log(MSG_DEBUG,"NPC %i is registered as in battle",monster->thisnpc->id);
|
//Log(MSG_DEBUG,"NPC %i is registered as in battle",monster->thisnpc->id);
|
||||||
monster->DoAttack( );
|
monster->DoAttack( );
|
||||||
}
|
}
|
||||||
//map->DeleteMonster(monster);
|
GServer->ClearClientID( monster->clientid ); // temp actor for running the NPC's AIP script; never added to MonsterList
|
||||||
//delete monster;
|
delete monster;
|
||||||
npc->lastAiUpdate = clock();
|
npc->lastAiUpdate = clock();
|
||||||
if(AIP == GServer->Config.AIWatch)
|
if(AIP == GServer->Config.AIWatch)
|
||||||
Log(MSG_DEBUG,"LastAiUpdate clock reset");
|
Log(MSG_DEBUG,"LastAiUpdate clock reset");
|
||||||
@@ -359,7 +359,7 @@ PVOID MapProcess( PVOID TS )
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep(GServer->Config.MapDelay);
|
Sleep(GServer->Config.MapDelay);
|
||||||
#else
|
#else
|
||||||
usleep(GServer->Config.MapDelay);
|
usleep(GServer->Config.MapDelay * 1000); // MapDelay is in milliseconds; usleep wants microseconds
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
pthread_exit( NULL );
|
pthread_exit( NULL );
|
||||||
@@ -393,7 +393,7 @@ PVOID VisibilityProcess(PVOID TS)
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep(GServer->Config.VisualDelay);
|
Sleep(GServer->Config.VisualDelay);
|
||||||
#else
|
#else
|
||||||
usleep(GServer->Config.VisualDelay);
|
usleep(GServer->Config.VisualDelay * 1000); // VisualDelay is in milliseconds; usleep wants microseconds
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
@@ -424,7 +424,7 @@ PVOID WorldProcess( PVOID TS )
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep(GServer->Config.WorldDelay);
|
Sleep(GServer->Config.WorldDelay);
|
||||||
#else
|
#else
|
||||||
usleep(GServer->Config.WorldDelay);
|
usleep(GServer->Config.WorldDelay * 1000); // WorldDelay is in milliseconds; usleep wants microseconds
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
@@ -438,7 +438,7 @@ void ShutdownServer(PVOID TS)
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep(minutes*60000);
|
Sleep(minutes*60000);
|
||||||
#else
|
#else
|
||||||
usleep(minutes*60000);
|
sleep(minutes*60); // minutes*60000 was milliseconds; sleep() wants seconds
|
||||||
#endif
|
#endif
|
||||||
Log( MSG_INFO, "Saving User Information... " );
|
Log( MSG_INFO, "Saving User Information... " );
|
||||||
GServer->DisconnectAll();
|
GServer->DisconnectAll();
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ CCharacter::CCharacter( )
|
|||||||
assert(Battle);
|
assert(Battle);
|
||||||
ClearBattle( Battle );
|
ClearBattle( Battle );
|
||||||
Battle->castTime = 0;
|
Battle->castTime = 0;
|
||||||
|
Battle->lastAtkTime = clock();
|
||||||
|
Battle->NextAtkTime = clock();
|
||||||
|
Battle->contatk = false;
|
||||||
//STATUS
|
//STATUS
|
||||||
Status = new STATUS;
|
Status = new STATUS;
|
||||||
assert(Status);
|
assert(Status);
|
||||||
|
|||||||
@@ -2980,7 +2980,7 @@ bool CWorldServer::ReloadMobSpawn( CPlayer* thisclient, int id )
|
|||||||
fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount );
|
fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount );
|
||||||
map->AddMonster( thisspawn->montype, position, 0, thisspawn->id, 1 );
|
map->AddMonster( thisspawn->montype, position, 0, thisspawn->id, 1 );
|
||||||
}
|
}
|
||||||
thisspawn->lastRespawnTime=clock();
|
thisspawn->lastRespawnTime=time(NULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -306,11 +306,13 @@ void CMap::UpdateTime( )
|
|||||||
// Clean Drops
|
// Clean Drops
|
||||||
void CMap::CleanDrops( )
|
void CMap::CleanDrops( )
|
||||||
{
|
{
|
||||||
for(UINT j=0;j<DropsList.size();j++)
|
for(UINT j=0;j<DropsList.size();)
|
||||||
{
|
{
|
||||||
CDrop* thisdrop = DropsList.at(j);
|
CDrop* thisdrop = DropsList.at(j);
|
||||||
if( time(NULL) - thisdrop->droptime >= 50 )
|
if( time(NULL) - thisdrop->droptime >= 50 )
|
||||||
DeleteDrop( thisdrop );
|
DeleteDrop( thisdrop ); // erases this element, so don't advance j - the next drop just shifted into position j
|
||||||
|
else
|
||||||
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,19 +326,19 @@ void CMap::RespawnMonster( )
|
|||||||
CSpawnArea* thisspawn = MonsterSpawnList.at(k);
|
CSpawnArea* thisspawn = MonsterSpawnList.at(k);
|
||||||
|
|
||||||
if (thisspawn->respawntime<2)thisspawn->respawntime=2;
|
if (thisspawn->respawntime<2)thisspawn->respawntime=2;
|
||||||
clock_t rtime = clock() - thisspawn->lastRespawnTime;
|
time_t rtime = time(NULL) - thisspawn->lastRespawnTime;
|
||||||
if((rtime > thisspawn->respawntime*CLOCKS_PER_SEC && thisspawn->amon<thisspawn->max)|| thisspawn->amon<thisspawn->min )
|
if((rtime > thisspawn->respawntime && thisspawn->amon<thisspawn->max)|| thisspawn->amon<thisspawn->min )
|
||||||
{
|
{
|
||||||
fPoint position = GServer->RandInPoly( thisspawn->points, thisspawn->pcount );
|
fPoint position = GServer->RandInPoly( thisspawn->points, thisspawn->pcount );
|
||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->mobdrop, thisspawn->mapdrop, thisspawn->id );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->mobdrop, thisspawn->mapdrop, thisspawn->id );
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
//PY new spawn code
|
//PY new spawn code
|
||||||
for(UINT k=0;k<MonsterSpawnList.size( );k++)
|
for(UINT k=0;k<MonsterSpawnList.size( );k++)
|
||||||
{
|
{
|
||||||
CSpawnArea* thisspawn = MonsterSpawnList.at(k);
|
CSpawnArea* thisspawn = MonsterSpawnList.at(k);
|
||||||
clock_t rtime = clock() - thisspawn->lastRespawnTime;
|
time_t rtime = time(NULL) - thisspawn->lastRespawnTime;
|
||||||
if(thisspawn->limit == 1 && IsNight()) //spawn limit is set to day only
|
if(thisspawn->limit == 1 && IsNight()) //spawn limit is set to day only
|
||||||
continue;
|
continue;
|
||||||
if(thisspawn->RefVar != 0 && thisspawn->RefVal != 0) //only allow this spawn if the correct ObjVar is set to non zero
|
if(thisspawn->RefVar != 0 && thisspawn->RefVal != 0) //only allow this spawn if the correct ObjVar is set to non zero
|
||||||
@@ -352,18 +354,18 @@ void CMap::RespawnMonster( )
|
|||||||
// no spawn. this spawn area is inactive
|
// no spawn. this spawn area is inactive
|
||||||
break;
|
break;
|
||||||
case 1: //normal spawn
|
case 1: //normal spawn
|
||||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
if(rtime > (thisspawn->respawntime) && thisspawn->amon < thisspawn->max)
|
||||||
{
|
{
|
||||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||||
UINT aggro = thisspawn->aggressive;
|
UINT aggro = thisspawn->aggressive;
|
||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||||
thisspawn->triggercount = 0;
|
thisspawn->triggercount = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2: //normal but fill the spawn
|
case 2: //normal but fill the spawn
|
||||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
if(rtime > (thisspawn->respawntime) && thisspawn->amon < thisspawn->max)
|
||||||
{
|
{
|
||||||
for(int i=1;i<thisspawn->max;i++)
|
for(int i=1;i<thisspawn->max;i++)
|
||||||
{
|
{
|
||||||
@@ -372,6 +374,7 @@ void CMap::RespawnMonster( )
|
|||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||||
}
|
}
|
||||||
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3: //cascade spawn
|
case 3: //cascade spawn
|
||||||
@@ -380,7 +383,7 @@ void CMap::RespawnMonster( )
|
|||||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||||
UINT aggro = thisspawn->aggressive;
|
UINT aggro = thisspawn->aggressive;
|
||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||||
thisspawn->triggercount = 0;
|
thisspawn->triggercount = 0;
|
||||||
}
|
}
|
||||||
@@ -395,12 +398,12 @@ void CMap::RespawnMonster( )
|
|||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||||
}
|
}
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
thisspawn->triggercount = 0;
|
thisspawn->triggercount = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 5: //sub spawns. needs to trigger from a specific montype
|
case 5: //sub spawns. needs to trigger from a specific montype
|
||||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
if(rtime > (thisspawn->respawntime) && thisspawn->amon < thisspawn->max)
|
||||||
{
|
{
|
||||||
//find if any of the trigger mobs exist
|
//find if any of the trigger mobs exist
|
||||||
for(UINT j=0;j<MonsterList.size();j++)
|
for(UINT j=0;j<MonsterList.size();j++)
|
||||||
@@ -415,13 +418,13 @@ void CMap::RespawnMonster( )
|
|||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||||
thisspawn->triggercount = 0;
|
thisspawn->triggercount = 0;
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->amon < thisspawn->max)
|
if(rtime > (thisspawn->respawntime) && thisspawn->amon < thisspawn->max)
|
||||||
{
|
{
|
||||||
//find if any of the trigger mobs exist
|
//find if any of the trigger mobs exist
|
||||||
for(UINT j=0;j<MonsterList.size();j++)
|
for(UINT j=0;j<MonsterList.size();j++)
|
||||||
@@ -438,19 +441,19 @@ void CMap::RespawnMonster( )
|
|||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit );
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters
|
||||||
}
|
}
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
thisspawn->triggercount = 0;
|
thisspawn->triggercount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 7: //TDEF spawn. Spawns thisspawn->max monsters one by one using triggercount to track them
|
case 7: //TDEF spawn. Spawns thisspawn->max monsters one by one using triggercount to track them
|
||||||
if(rtime > (thisspawn->respawntime * CLOCKS_PER_SEC) && thisspawn->triggercount < thisspawn->max)
|
if(rtime > (thisspawn->respawntime) && thisspawn->triggercount < thisspawn->max)
|
||||||
{
|
{
|
||||||
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius );
|
||||||
AddMonster( thisspawn->montype, position, 0, thisspawn->id, 0, true, 0 );
|
AddMonster( thisspawn->montype, position, 0, thisspawn->id, 0, true, 0 );
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 3); // update the counters
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 3); // update the counters
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -463,8 +466,8 @@ void CMap::RespawnMonster( )
|
|||||||
for (UINT j = 0; j < MobGroupList.size(); j++)
|
for (UINT j = 0; j < MobGroupList.size(); j++)
|
||||||
{
|
{
|
||||||
CMobGroup* thisgroup = MobGroupList.at(j);
|
CMobGroup* thisgroup = MobGroupList.at(j);
|
||||||
clock_t rtime = clock() - thisgroup->lastRespawnTime;
|
time_t rtime = time(NULL) - thisgroup->lastRespawnTime;
|
||||||
if (rtime < thisgroup->respawntime*CLOCKS_PER_SEC || thisgroup->active >= thisgroup->limit)
|
if (rtime < thisgroup->respawntime || thisgroup->active >= thisgroup->limit)
|
||||||
continue;
|
continue;
|
||||||
for (UINT k = thisgroup->active; k < thisgroup->limit; k++)
|
for (UINT k = thisgroup->active; k < thisgroup->limit; k++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ bool CWorldServer::LoadMobGroups()
|
|||||||
thisgroup->tacticalpoints = atoi(row[7]);
|
thisgroup->tacticalpoints = atoi(row[7]);
|
||||||
char* mobList = row[8];
|
char* mobList = row[8];
|
||||||
|
|
||||||
thisgroup->lastRespawnTime = clock();
|
thisgroup->lastRespawnTime = time(NULL);
|
||||||
thisgroup->active = 0;
|
thisgroup->active = 0;
|
||||||
thisgroup->basicKills = 0;
|
thisgroup->basicKills = 0;
|
||||||
thisgroup->curTac = 0;
|
thisgroup->curTac = 0;
|
||||||
@@ -439,7 +439,7 @@ bool CWorldServer::LoadMonsterSpawn( )
|
|||||||
if (thisspawn->respawntime == 0)
|
if (thisspawn->respawntime == 0)
|
||||||
thisspawn->respawntime=5;
|
thisspawn->respawntime=5;
|
||||||
thisspawn->amon = 0;
|
thisspawn->amon = 0;
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
thisspawn->type = atoi(row[9]);
|
thisspawn->type = atoi(row[9]);
|
||||||
thisspawn->triggertype = atoi(row[10]);
|
thisspawn->triggertype = atoi(row[10]);
|
||||||
thisspawn->triggeramount = atoi(row[11]);
|
thisspawn->triggeramount = atoi(row[11]);
|
||||||
@@ -1244,7 +1244,7 @@ bool CWorldServer::LoadZoneData( )
|
|||||||
//TD stuff
|
//TD stuff
|
||||||
newzone->TDNextMon = 0;
|
newzone->TDNextMon = 0;
|
||||||
newzone->TDNextSpawn = 0;
|
newzone->TDNextSpawn = 0;
|
||||||
newzone->lastTDSpawnTime = clock();
|
newzone->lastTDSpawnTime = time(NULL);
|
||||||
newzone->LastUpdate = clock( );
|
newzone->LastUpdate = clock( );
|
||||||
newzone->CurrentTime = 0;
|
newzone->CurrentTime = 0;
|
||||||
newzone->TDMobDelay = 1500;
|
newzone->TDMobDelay = 1500;
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ bool CMap::DeleteMonster( CMonster* monster, bool clearobject, UINT i )
|
|||||||
if(thisspawn != NULL)
|
if(thisspawn != NULL)
|
||||||
{
|
{
|
||||||
if(thisspawn->amon >= thisspawn->max)// reset spawn timer if the spawn is full
|
if(thisspawn->amon >= thisspawn->max)// reset spawn timer if the spawn is full
|
||||||
thisspawn->lastRespawnTime = clock();
|
thisspawn->lastRespawnTime = time(NULL);
|
||||||
thisspawn->amon--;
|
thisspawn->amon--;
|
||||||
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 2);
|
UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 2);
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ bool CMap::DeleteMonster( CMonster* monster, bool clearobject, UINT i )
|
|||||||
if(thisgroup!=NULL)
|
if(thisgroup!=NULL)
|
||||||
{
|
{
|
||||||
if(thisgroup->active >= thisgroup->limit)// reset spawn timer if the spawn is full
|
if(thisgroup->active >= thisgroup->limit)// reset spawn timer if the spawn is full
|
||||||
thisgroup->lastRespawnTime = clock();
|
thisgroup->lastRespawnTime = time(NULL);
|
||||||
thisgroup->active--;
|
thisgroup->active--;
|
||||||
thisgroup->basicKills++;
|
thisgroup->basicKills++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class CMap
|
|||||||
UINT TDMobList[500]; //List of TD monsters waiting to be spawned
|
UINT TDMobList[500]; //List of TD monsters waiting to be spawned
|
||||||
UINT TDNextSpawn; //index pointer for next spawn in TDMobList
|
UINT TDNextSpawn; //index pointer for next spawn in TDMobList
|
||||||
UINT TDNextMon; //index pointer for next addition to TDMobList
|
UINT TDNextMon; //index pointer for next addition to TDMobList
|
||||||
clock_t lastTDSpawnTime; //time of the last spawn
|
time_t lastTDSpawnTime; //time of the last spawn
|
||||||
UINT TDMobDelay; // delay between spawns (ms)
|
UINT TDMobDelay; // delay between spawns (ms)
|
||||||
UINT crystalhealth; // current health of the base crystal
|
UINT crystalhealth; // current health of the base crystal
|
||||||
UINT portalhealth; // current health of the spawn portal
|
UINT portalhealth; // current health of the spawn portal
|
||||||
|
|||||||
Reference in new issue
Block a user