// Memory.cpp // // Handles allocation of Time Nodes. // #include "Net7.h" #include "MemoryHandler.h" #include "SectorData.h" #include #include "ObjectManager.h" #include "PlayerClass.h" #include "ServerManager.h" #define PLAYER_NODE_AVAILABLE -1 // Start player cleanout thread #ifdef WIN32 void __cdecl RunCleanoutThreadAPI(void *arg) { GMemoryHandler::MParam * param = (GMemoryHandler::MParam *) arg; GMemoryHandler * mgr = param->ClassAdd; delete param; mgr->CheckPlayerNodeThread(); _endthread(); } #else // Linux void * RunCleanoutThreadAPI(void *arg) { GMemoryHandler::MParam * param = (GMemoryHandler::MParam *) arg; GMemoryHandler * mgr = param->ClassAdd; delete param; mgr->CheckPlayerNodeThread(); return NULL; } #endif TimeNode * MemoryHandler::GetBroadcastNodeSlot() { TimeNode *NextNode;// = m_BroadcastQueueBuf; long i = m_CircularIndex; long spincount = 0; //search for unused node while (m_BroadcastQueueNodes[i]->player_id != 0) { i++; if (i==m_NodeSize) { i = 0; spincount++; if (spincount > 2) ExtendBroadcastSlots();//need to allocate some more nodes - add another NODE_EXTEND_SIZE nodes } } NextNode = m_BroadcastQueueNodes[i]; m_CircularIndex = i + 1; if (m_CircularIndex == m_NodeSize) { m_CircularIndex = 0; } if (NextNode->player_id != 0) { NextNode = 0; } return NextNode; } void MemoryHandler::ExtendBroadcastSlots() { TimeNode * BroadcastQueueBuf = new TimeNode[NODE_EXTEND_SIZE]; //allocate another 30 nodes memset(BroadcastQueueBuf, 0, sizeof(TimeNode)*NODE_EXTEND_SIZE); LogMessage(">>>>>\n"); LogMessage(">>>>> ---- Extending broadcast nodes!\n"); TimeNode *Node; for (int i = 0; i < NODE_EXTEND_SIZE; i++) { Node = &BroadcastQueueBuf[i]; m_BroadcastQueueNodes.push_back(Node); //assign new nodes to vector slots on end of list } m_NodeSize += NODE_EXTEND_SIZE; } MemoryHandler::MemoryHandler(long nodes) { TimeNode * BroadcastQueueBuf = new TimeNode[nodes]; memset(BroadcastQueueBuf, 0, sizeof(TimeNode)*nodes); m_NodeSize = nodes; if (!BroadcastQueueBuf) { LogMessage("FATAL ERROR: Unable to initialise memory.\n"); exit(1); } int i; TimeNode *Node; for (i = 0; i < m_NodeSize; i++) { Node = &BroadcastQueueBuf[i]; m_BroadcastQueueNodes.push_back(Node); //assign each node to a vector slot } m_CircularIndex = 0; } MemoryHandler::~MemoryHandler() { //unhook any active calls still on the buffer long i; for (i = 0; i < m_NodeSize; i++) { if (m_BroadcastQueueNodes[i]->player_id != 0) { m_BroadcastQueueNodes[i]->player_id = NODE_NO_LONGER_NEEDED; } } Sleep(100); TimeNode * BroadcastBuff = m_BroadcastQueueNodes[0]; delete[] BroadcastBuff; i = BROADCAST_SLOTS; while (i > m_NodeSize) { BroadcastBuff = m_BroadcastQueueNodes[i]; delete[] BroadcastBuff; i += NODE_EXTEND_SIZE; } m_BroadcastQueueNodes.clear(); } // Global memory handler GMemoryHandler::GMemoryHandler(long player_nodes) { long node_index; m_PlayerQueueBuf = new Player[player_nodes]; //NB - no memset for derived class - you'll obliterate the vptr. m_PlayerBufferSize = player_nodes; m_PlayerCount = 0; m_PlayerCircularIndex = 0; if (!m_PlayerQueueBuf) { LogMessage("FATAL ERROR: Unable to initialise memory for player handling.\n"); } else { for (node_index = 0; node_index < m_PlayerBufferSize; node_index++) //set all players to 'AVAILABLE' { m_PlayerQueueBuf[node_index].SetGameID(PLAYER_NODE_AVAILABLE); m_PlayerQueueBuf[node_index].SetCharacterID(-1); m_PlayerQueueBuf[node_index].SetAccountUsername(0); } //now start the cleanout thread { MParam *param = new MParam; if (param) { param->ClassAdd = this; #ifdef WIN32 _beginthread(&RunCleanoutThreadAPI, 0, param); #else pthread_create(&m_ThreadHandle, NULL, &RunCleanoutThreadAPI, (void *) param); #endif } } } } GMemoryHandler::~GMemoryHandler() { delete[] m_PlayerQueueBuf; } long GMemoryHandler::GetPlayerCount() { return (m_PlayerCount); } void GMemoryHandler::CheckPlayerNodeThread() { Player *node = (0); long node_index; long sleep_time; LogMessage("Starting node check thread.\n"); for (node_index = 0; node_index < m_PlayerBufferSize; node_index++) { node = &m_PlayerQueueBuf[node_index]; node->SetLastAccessTime(0); } do { unsigned long current_time = GetNet7TickCount(); m_PlayerMutex.Lock(); for (node_index = 0; node_index < m_PlayerBufferSize; node_index++) { node = &m_PlayerQueueBuf[node_index]; //See if the player has had any involvement with anything for the past half hour // (if the connection is invalid and we haven't handled them for // more than 2 mins then it is a dead connection - Net7Proxy will issue a keepalive every 30 secs) if (node->GameID() != PLAYER_NODE_AVAILABLE && node->LastAccessTime() != 0 && (node->LastAccessTime() + 60000*2) < current_time ) { //release the player node. /*LogMessage("*** Cleanup thread is removing dead player: %s [0x%08x]\n*** last received time: %d, time now: %d diff: %d\n", node->Name(), node->GameID(), node->LastAccessTime(), current_time, current_time - node->LastAccessTime());*/ g_ServerMgr->m_PlayerMgr.DropPlayerFromGalaxy(node); ReleasePlayerNode(node); } } m_PlayerMutex.Unlock(); sleep_time = current_time - GetNet7TickCount() + 60000*2; //cleanout every 2 mins if (sleep_time < 0) sleep_time = 0; Sleep(sleep_time); } while (!g_ServerShutdown); LogMessage("Shutting down check thread"); } // for New 'Player' class Player * GMemoryHandler::GetPlayerNode(char *account_name) { Player *node = (0); Player *NodeAllocated = (0); long node_index; if (m_PlayerCount >= MAX_ONLINE_PLAYERS) { return (0); } m_PlayerMutex.Lock(); node_index = m_PlayerCircularIndex; //Get first available PlayerNode after the current circular index while (m_PlayerQueueBuf[node_index].GameID() != PLAYER_NODE_AVAILABLE) { node_index++; if (node_index==m_PlayerBufferSize) { node_index = 0; } } NodeAllocated = &m_PlayerQueueBuf[node_index]; m_PlayerCircularIndex = node_index + 1; if (m_PlayerCircularIndex == m_PlayerBufferSize) { m_PlayerCircularIndex = 0; } NodeAllocated->SetGameID(node_index | PLAYER_TAG); NodeAllocated->SetGroupID(-1); NodeAllocated->SetMVASIndex(-1); NodeAllocated->SetLastAccessTime(GetNet7TickCount()); m_PlayerCount++; m_PlayerMutex.Unlock(); return NodeAllocated; } void GMemoryHandler::ReleasePlayerNode(Player *player) { m_PlayerMutex.Lock(); if (player->GameID() != PLAYER_NODE_AVAILABLE && m_PlayerCount > 0) { //LogMessage("Player %s [%08x] released\n", player->Name(), player->GameID()); player->SetGameID(PLAYER_NODE_AVAILABLE); m_PlayerCount--; } player->SetLastAccessTime(0); player->SetAccountUsername(0); player->SetCharacterID(-1); m_PlayerMutex.Unlock(); } Player * GMemoryHandler::GetPlayer(long avatar_id, bool sector_login) { Player *player = 0; long avatar_index = avatar_id & 0x00FFFFFF; if (!IS_PLAYER(avatar_id)) return (0); if (avatar_index > m_PlayerBufferSize) //see if this is in valid player range { //LogMessage(">> Player Not valid. id: %x index = %x\n", avatar_id, avatar_index); return (0); } m_PlayerMutex.Lock(); player = &m_PlayerQueueBuf[avatar_index]; m_PlayerMutex.Unlock(); if (player->GameID() == PLAYER_NODE_AVAILABLE) //make sure there's something there { //LogMessage("GameID for avatarid %x = %x\n",avatar_id,player->GameID()); if (sector_login) { LogMessage("Override invalid player ID for %s\n", player->Name()); player->SetGameID(avatar_id); return player; } return (0); } player->SetLastAccessTime(GetNet7TickCount()); return player; }