diff --git a/Src/Common/sockets.h b/Src/Common/sockets.h index 99fc610..9d975ab 100644 --- a/Src/Common/sockets.h +++ b/Src/Common/sockets.h @@ -62,6 +62,29 @@ #include #include #include + +#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 +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 #include "pthread.h" #include @@ -78,8 +101,8 @@ typedef char SBYTE; typedef unsigned char BYTE; typedef short SWORD; typedef unsigned short WORD; -typedef long SDWORD; -typedef unsigned long DWORD; +typedef int SDWORD; +typedef unsigned int DWORD; typedef long long SQWORD; typedef unsigned long long QWORD; #ifdef _WIN32 diff --git a/Src/World Server/MainProcess.cpp b/Src/World Server/MainProcess.cpp index 6cf46ea..1c2fee7 100644 --- a/Src/World Server/MainProcess.cpp +++ b/Src/World Server/MainProcess.cpp @@ -118,8 +118,8 @@ PVOID MapProcess( PVOID TS ) if(nextmon != 0) { //Log(MSG_DEBUG,"Spawning TD mobs"); - clock_t etime = clock() - map->lastTDSpawnTime; - if(etime >= map->TDMobDelay) // check if elapsed time is greater than spawn delay + time_t etime = time(NULL) - map->lastTDSpawnTime; + 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 //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 //Log(MSG_DEBUG,"moved the pointer"); if(map->TDNextSpawn > 100)map->TDNextSpawn = 0; - map->lastTDSpawnTime = clock(); + map->lastTDSpawnTime = time(NULL); } } //TD spawn end @@ -330,8 +330,8 @@ PVOID MapProcess( PVOID TS ) //Log(MSG_DEBUG,"NPC %i is registered as in battle",monster->thisnpc->id); monster->DoAttack( ); } - //map->DeleteMonster(monster); - //delete monster; + GServer->ClearClientID( monster->clientid ); // temp actor for running the NPC's AIP script; never added to MonsterList + delete monster; npc->lastAiUpdate = clock(); if(AIP == GServer->Config.AIWatch) Log(MSG_DEBUG,"LastAiUpdate clock reset"); @@ -359,7 +359,7 @@ PVOID MapProcess( PVOID TS ) #ifdef _WIN32 Sleep(GServer->Config.MapDelay); #else - usleep(GServer->Config.MapDelay); + usleep(GServer->Config.MapDelay * 1000); // MapDelay is in milliseconds; usleep wants microseconds #endif } pthread_exit( NULL ); @@ -393,7 +393,7 @@ PVOID VisibilityProcess(PVOID TS) #ifdef _WIN32 Sleep(GServer->Config.VisualDelay); #else - usleep(GServer->Config.VisualDelay); + usleep(GServer->Config.VisualDelay * 1000); // VisualDelay is in milliseconds; usleep wants microseconds #endif } pthread_exit(NULL); @@ -424,7 +424,7 @@ PVOID WorldProcess( PVOID TS ) #ifdef _WIN32 Sleep(GServer->Config.WorldDelay); #else - usleep(GServer->Config.WorldDelay); + usleep(GServer->Config.WorldDelay * 1000); // WorldDelay is in milliseconds; usleep wants microseconds #endif } pthread_exit(NULL); @@ -438,7 +438,7 @@ void ShutdownServer(PVOID TS) #ifdef _WIN32 Sleep(minutes*60000); #else - usleep(minutes*60000); + sleep(minutes*60); // minutes*60000 was milliseconds; sleep() wants seconds #endif Log( MSG_INFO, "Saving User Information... " ); GServer->DisconnectAll(); diff --git a/Src/World Server/character.cpp b/Src/World Server/character.cpp index 72d2736..0ba2b43 100644 --- a/Src/World Server/character.cpp +++ b/Src/World Server/character.cpp @@ -58,6 +58,9 @@ CCharacter::CCharacter( ) assert(Battle); ClearBattle( Battle ); Battle->castTime = 0; + Battle->lastAtkTime = clock(); + Battle->NextAtkTime = clock(); + Battle->contatk = false; //STATUS Status = new STATUS; assert(Status); diff --git a/Src/World Server/gmcmds.cpp b/Src/World Server/gmcmds.cpp index 2a8d15b..f654460 100644 --- a/Src/World Server/gmcmds.cpp +++ b/Src/World Server/gmcmds.cpp @@ -2980,7 +2980,7 @@ bool CWorldServer::ReloadMobSpawn( CPlayer* thisclient, int id ) fPoint position = RandInPoly( thisspawn->points, thisspawn->pcount ); map->AddMonster( thisspawn->montype, position, 0, thisspawn->id, 1 ); } - thisspawn->lastRespawnTime=clock(); + thisspawn->lastRespawnTime=time(NULL); return true; } diff --git a/Src/World Server/mapfunctions.cpp b/Src/World Server/mapfunctions.cpp index 376188a..96a8aac 100644 --- a/Src/World Server/mapfunctions.cpp +++ b/Src/World Server/mapfunctions.cpp @@ -306,11 +306,13 @@ void CMap::UpdateTime( ) // Clean Drops void CMap::CleanDrops( ) { - for(UINT j=0;jdroptime >= 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); if (thisspawn->respawntime<2)thisspawn->respawntime=2; - clock_t rtime = clock() - thisspawn->lastRespawnTime; - if((rtime > thisspawn->respawntime*CLOCKS_PER_SEC && thisspawn->amonmax)|| thisspawn->amonmin ) + time_t rtime = time(NULL) - thisspawn->lastRespawnTime; + if((rtime > thisspawn->respawntime && thisspawn->amonmax)|| thisspawn->amonmin ) { fPoint position = GServer->RandInPoly( thisspawn->points, thisspawn->pcount ); AddMonster( thisspawn->montype, position, 0, thisspawn->mobdrop, thisspawn->mapdrop, thisspawn->id ); - thisspawn->lastRespawnTime = clock(); + thisspawn->lastRespawnTime = time(NULL); } }*/ //PY new spawn code for(UINT k=0;klastRespawnTime; + time_t rtime = time(NULL) - thisspawn->lastRespawnTime; if(thisspawn->limit == 1 && IsNight()) //spawn limit is set to day only continue; 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 break; 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 ); UINT aggro = thisspawn->aggressive; 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 thisspawn->triggercount = 0; } break; 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;imax;i++) { @@ -372,6 +374,7 @@ void CMap::RespawnMonster( ) AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters } + thisspawn->lastRespawnTime = time(NULL); } break; case 3: //cascade spawn @@ -380,7 +383,7 @@ void CMap::RespawnMonster( ) fPoint position = GServer->RandInCircle( thisspawn->point, thisspawn->radius ); UINT aggro = thisspawn->aggressive; 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 thisspawn->triggercount = 0; } @@ -395,12 +398,12 @@ void CMap::RespawnMonster( ) AddMonster( thisspawn->montype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters } - thisspawn->lastRespawnTime = clock(); + thisspawn->lastRespawnTime = time(NULL); thisspawn->triggercount = 0; } break; 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 for(UINT j=0;jmontype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters thisspawn->triggercount = 0; - thisspawn->lastRespawnTime = clock(); + thisspawn->lastRespawnTime = time(NULL); } } } break; 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 for(UINT j=0;jmontype, position, 0, thisspawn->id, aggro, false, thisspawn->limit ); UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 1); // update the counters } - thisspawn->lastRespawnTime = clock(); + thisspawn->lastRespawnTime = time(NULL); thisspawn->triggercount = 0; } } } break; 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 ); AddMonster( thisspawn->montype, position, 0, thisspawn->id, 0, true, 0 ); UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 3); // update the counters - thisspawn->lastRespawnTime = clock(); + thisspawn->lastRespawnTime = time(NULL); } break; default: @@ -463,8 +466,8 @@ void CMap::RespawnMonster( ) for (UINT j = 0; j < MobGroupList.size(); j++) { CMobGroup* thisgroup = MobGroupList.at(j); - clock_t rtime = clock() - thisgroup->lastRespawnTime; - if (rtime < thisgroup->respawntime*CLOCKS_PER_SEC || thisgroup->active >= thisgroup->limit) + time_t rtime = time(NULL) - thisgroup->lastRespawnTime; + if (rtime < thisgroup->respawntime || thisgroup->active >= thisgroup->limit) continue; for (UINT k = thisgroup->active; k < thisgroup->limit; k++) { diff --git a/Src/World Server/startup.cpp b/Src/World Server/startup.cpp index d4b655e..1d2749e 100644 --- a/Src/World Server/startup.cpp +++ b/Src/World Server/startup.cpp @@ -331,7 +331,7 @@ bool CWorldServer::LoadMobGroups() thisgroup->tacticalpoints = atoi(row[7]); char* mobList = row[8]; - thisgroup->lastRespawnTime = clock(); + thisgroup->lastRespawnTime = time(NULL); thisgroup->active = 0; thisgroup->basicKills = 0; thisgroup->curTac = 0; @@ -439,7 +439,7 @@ bool CWorldServer::LoadMonsterSpawn( ) if (thisspawn->respawntime == 0) thisspawn->respawntime=5; thisspawn->amon = 0; - thisspawn->lastRespawnTime = clock(); + thisspawn->lastRespawnTime = time(NULL); thisspawn->type = atoi(row[9]); thisspawn->triggertype = atoi(row[10]); thisspawn->triggeramount = atoi(row[11]); @@ -1244,7 +1244,7 @@ bool CWorldServer::LoadZoneData( ) //TD stuff newzone->TDNextMon = 0; newzone->TDNextSpawn = 0; - newzone->lastTDSpawnTime = clock(); + newzone->lastTDSpawnTime = time(NULL); newzone->LastUpdate = clock( ); newzone->CurrentTime = 0; newzone->TDMobDelay = 1500; diff --git a/Src/World Server/worldmap.cpp b/Src/World Server/worldmap.cpp index 8458c74..857dfa8 100644 --- a/Src/World Server/worldmap.cpp +++ b/Src/World Server/worldmap.cpp @@ -271,7 +271,7 @@ bool CMap::DeleteMonster( CMonster* monster, bool clearobject, UINT i ) if(thisspawn != NULL) { if(thisspawn->amon >= thisspawn->max)// reset spawn timer if the spawn is full - thisspawn->lastRespawnTime = clock(); + thisspawn->lastRespawnTime = time(NULL); thisspawn->amon--; UpdateSpawnCounter( thisspawn->montype, thisspawn->id, 2); } @@ -282,7 +282,7 @@ bool CMap::DeleteMonster( CMonster* monster, bool clearobject, UINT i ) if(thisgroup!=NULL) { if(thisgroup->active >= thisgroup->limit)// reset spawn timer if the spawn is full - thisgroup->lastRespawnTime = clock(); + thisgroup->lastRespawnTime = time(NULL); thisgroup->active--; thisgroup->basicKills++; } diff --git a/Src/World Server/worldmap.h b/Src/World Server/worldmap.h index a2e53db..e49b56a 100644 --- a/Src/World Server/worldmap.h +++ b/Src/World Server/worldmap.h @@ -93,7 +93,7 @@ class CMap UINT TDMobList[500]; //List of TD monsters waiting to be spawned UINT TDNextSpawn; //index pointer for next spawn in 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 crystalhealth; // current health of the base crystal UINT portalhealth; // current health of the spawn portal