net7/branches/linux-port/PlayerManager.cpp
2014-05-11 16:54:16 -04:00

802 lines
18 KiB
C++

// PlayerManager.cpp
#include "Net7.h"
#include "PlayerManager.h"
#include "SectorManager.h"
#include "Opcodes.h"
#include "PacketStructures.h"
#include "Connection.h"
#include <float.h>
#include "UDPConnection.h"
#include "MemoryHandler.h"
#include "ObjectClass.h"
#include "PlayerClass.h"
#include "ServerManager.h"
// This helper function is referenced by _beginthread to launch the TCP thread.
#ifdef WIN32
void __cdecl RunMovementThreadAPI(void *arg)
{
PlayerManager::MParam * param = (PlayerManager::MParam *) arg;
PlayerManager * mgr = param->ClassAdd;
//PlayerManager::Player *player = param->player;
delete param;
mgr->RunMovementThread();
_endthread();
}
#else // Linux
void * RunMovementThreadAPI(void *arg)
{
PlayerManager::MParam * param = (PlayerManager::MParam *) arg;
PlayerManager * mgr = param->ClassAdd;
//PlayerManager::Player *player = param->player;
delete param;
mgr->RunMovementThread();
return NULL;
}
#endif
PlayerManager::PlayerManager()
{
m_NextGroup = 0;
m_GroupList = NULL;
m_Movement_thread_running = false;
m_Save_thread_running = false;
memset(m_GlobalPlayerList, 0, sizeof(m_GlobalPlayerList));
// movement thread
if (!m_Movement_thread_running)
{
MParam *param = new MParam;
LogMessage("Creating movement thread\n");
m_Movement_thread_running = true;
if (param)
{
param->ClassAdd = this;
#ifdef WIN32
_beginthread(&RunMovementThreadAPI, 0, param);
#else
pthread_create(&m_PlayerManagerThreadHandle, NULL, &RunMovementThreadAPI, (void *) param);
#endif
}
}
}
PlayerManager::~PlayerManager()
{
//delete groups
Group * g = m_GroupList;
while (g)
{
Group * temp = g->next;
delete g;
g = temp;
}
}
//PM
bool PlayerManager::SetupPlayer(Player *player, long IPaddr)
{
player->ResetPlayer();
player->SetLastAccessTime(GetNet7TickCount());
//add this to the global player list
SetIndex(player->GameID(), m_GlobalPlayerList);
player->SetMVASIndex(player->GameID());
LogMessage("Confirmed MVAS connection for [%x]: MVAS node index %x\n", player->GameID(), player->MVASIndex());
return true;
}
void PlayerManager::DropPlayerFromSector(Player *p)
{
//first remove the player from all the sector lists
p->RemoveFromAllSectorRangeLists();
//now set inactive until player revived at login
p->SetActive(false);
if (p->PlayerIndex()->GetSectorNum() > 0)
{
p->SaveData(true);
p->SaveDatabase();
p->SaveAmmoLevels();
}
LogMessage("Player %s Removed from sector %s.\n", p->Name(), p->PlayerIndex()->GetSectorName());
}
void PlayerManager::DropPlayerFromGalaxy(Player *p)
{
//first drop the player from the sector - this should remove the player ship from everyone's visibility
DropPlayerFromSector(p);
p->SavePosition();
p->SetRemove(); // this ensures the player will be removed the next time the player handling thread processes them
}
//This method is developer only for debugging.
//any players using it should expect grief!
//PM
void PlayerManager::SendPlayerWithoutConnection(long player_id)
{
Player * p = 0;
Player * player_to_send = 0;
p = GetPlayer(player_id);
//first create a new player entry.
player_to_send = m_GlobMemMgr->GetPlayerNode();
if (!p || !player_to_send)
{
LogMessage("*** Error *** NULL Pointer in SendPlayerWithoutConnection AvatarID: %d", player_id | 0x00FFFFFF);
return;
}
long sector_id = p->PlayerIndex()->GetSectorNum();
player_to_send->ResetPlayer();
player_to_send->SetGroupID(-1);
player_to_send->PlayerIndex()->SetSectorNum(sector_id);
player_to_send->AddPlayerToRangeList(player_to_send); //add self to range list
player_to_send->SetDebugPlayer();
player_to_send->SetActive(true);
//load some data
player_to_send->SetCharacterID(p->CharacterID());
memcpy(player_to_send->Database(), p->Database(), sizeof(CharacterDatabase));
memcpy(player_to_send->Database()->avatar.avatar_first_name, "Dropdead Fred\0", 14);
player_to_send->ReadSavedData();
player_to_send->SetCharacterID(0);
player_to_send->ShipIndex()->SetName("Dropdead Fred");
player_to_send->ShipIndex()->SetGameID(player_to_send->GameID());
player_to_send->SetName("Dropdead Fred");
strcpy(player_to_send->Database()->ship_data.ship_name, "Summer Holiday");
//add this to the global player list
m_Mutex.Lock();
SetIndex(player_to_send->GameID(), m_GlobalPlayerList);
SectorManager *SectorMgr = g_ServerMgr->GetSectorManager(sector_id);
SectorMgr->AddPlayerToSectorList(player_to_send);
player_to_send->SetSectorManager(SectorMgr);
player_to_send->SetObjectManager(SectorMgr->GetObjectManager());
m_Mutex.Unlock();
if (player_to_send && p)
{
p->SetMyDebugPlayer(player_to_send);
player_to_send->SetPosition(p->Position());
player_to_send->SetOrientation(p->Orientation());
player_to_send->SetVelocity(0.0f);
}
if (p->PlayerIndex()->GetSectorNum() > 9999)
{
//add to starbase
player_to_send->SetActionFlag(65);
player_to_send->SetOrient(0);
p->DebugPlayerDock(true);
//player_to_send->SendStarbaseAvatarList();
//player_to_send->SetPosition(p->Position());
}
}
// Finally this is OK because all the player info is protected
Player * PlayerManager::GetPlayer(long GameID, bool sector_login)
{
return (m_GlobMemMgr->GetPlayer(GameID, sector_login));
}
Player * PlayerManager::GetPlayer(char * Name)
{
Player * p = (0);
Player * retval = (0);
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
if (_stricmp(p->Name(), Name) == 0)
{
retval = (Player*)p;
break;
}
}
return retval;
}
long PlayerManager::GetGameIDFromName(char * Name)
{
Player * p = GetPlayer(Name);
if (p)
{
return p->GameID();
}
else
{
return -1;
}
}
//PM
void PlayerManager::ListPlayersAndLocations(Player *send_to)
{
Player * p = (0);
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
Object *nearest_nav = p->NearestNav();
if (nearest_nav && p->InSpace())
{
send_to->SendVaMessage("%s[%x] in %d Near to %s", p->Name(), (p->GameID() & 0x00FFFFFF), p->PlayerIndex()->GetSectorNum(),
nearest_nav->Name());
}
else
{
send_to->SendVaMessage("%s[%x] in %d", p->Name(), (p->GameID() & 0x00FFFFFF), p->PlayerIndex()->GetSectorNum());
}
}
}
void PlayerManager::CheckForDuplicatePlayers(Player *player)
{
Player * p = (0);
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
if (player != p &&
p->CharacterID() == player->CharacterID() )
{
//this player is already logged in. Remove the old player
LogMessage("Killing old instance of player '%s' [0x%08x]\n", p->Name(), p->GameID());
DropPlayerFromGalaxy(p);
}
}
}
bool PlayerManager::CheckAccountInUse(char *username)
{
Player * p = (0);
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
if (strncmp(p->AccountUsername(), username, 50) == 0)
{
if (p->Active())
{
//this player's account is already active
//p->ForceLogout();
LogMessage("Account user %s already active, don't allow second account\n", p->AccountUsername());
return true;
}
else
{
LogMessage("Account user %s has dead player on server, remove\n", username);
DropPlayerFromGalaxy(p);
}
}
}
return false;
}
//PM - start at startup
void PlayerManager::RunMovementThread()
{
Player * p = (0);
unsigned long current_tick;
long sleep_time;
LogMessage("Begin move thread cycle\n");
do
{
current_tick = GetNet7TickCount();
// Run through all players sequentially and update each in turn
m_Mutex.Lock();
p = (0);
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
current_tick = GetNet7TickCount();
//Handle player removal here
if (p)
{
if (p->GetLoginStage() == 2)
{
p->SendPacketCache();
p->HandleLoginStage2();
}
else if (p->IsToBeRemoved())
{
m_GlobMemMgr->ReleasePlayerNode(p);
p->SetActive(false);
LogMessage("Player %s Removed from Galaxy.\n", p->Name());
UnSetIndex(p->GameID(), m_GlobalPlayerList);
}
else if ((p->LastAccessTime() + 2*60000) < current_tick )
{
DropPlayerFromGalaxy(p);
}
else if (p->Active())
{
if (p->InSpace())
{
if (p->MovementID() % 5 == 0)
{
p->CalcNewPosition(current_tick); //update the positions
if (!p->DebugPlayer() && (!IS_PLAYER(p->MVASIndex()) || p->WarpDrive() || p->Following() || p->PlayerUpdateSet()) )
{
p->SendLocationAndSpeed(true);
}
else if (IS_PLAYER(p->MVASIndex()))
{
p->SendLocationAndSpeed(false);
}
if (p->ObjectIsMoving())
{
p->UpdateVerbs();
}
if (p->MovementID() % 50)
{
Object *obj = p->NearestNav();
p->SetNearestNav(obj);
}
p->CheckObjectRanges();
p->UpdatePlayerVisibilityList();
p->ResetAttacksThisTick();
}
p->IncrementMovementID(1);
}
p->SendPacketCache();
p->CheckEventTimes(current_tick);
}
else if (p->DebugPlayer())
{
if (p->MovementID() % 5 == 0)
{
p->SendLocationAndSpeed(false);
p->UpdatePlayerVisibilityList();
}
p->IncrementMovementID(1);
}
else
{
p->SendPacketCache();
}
}
}
m_Mutex.Unlock();
sleep_time = current_tick - GetNet7TickCount() + 100;
if (sleep_time < 0) sleep_time = 0;
Sleep(sleep_time);
} while (!g_ServerShutdown);
m_Movement_thread_running = false;
LogMessage("Shutting down movement thread");
}
///////////////////////////
//// Chat System ////
///////////////////////////
void PlayerManager::GetPostFix(Player *p, char *FName, int length)
{
char PostFix[10];
if (!p) return;
switch(p->AdminLevel())
{
case 20:
sprintf(PostFix, "HLP");
break;
case 30:
sprintf(PostFix, "BETA");
break;
case 50:
sprintf(PostFix, "GM");
break;
case 60:
sprintf(PostFix, "DGM");
break;
case 70:
sprintf(PostFix, "HGM");
break;
case 80:
sprintf(PostFix, "DEV");
break;
case 90:
sprintf(PostFix, "SDEV");
break;
case 100:
sprintf(PostFix, "ADMIN");
break;
default:
sprintf(PostFix, "");
break;
}
if (p->Hijackee())
{
_snprintf(FName, length, "%s", p->GetHijackeeName());
}
else if (PostFix[0])
{
_snprintf(FName, length, "%s [%s]", p->Name(), PostFix);
}
else
{
_snprintf(FName, length, "%s", p->Name());
}
}
void PlayerManager::ChatSendEveryone(long GameID, char *message, bool copy_to_originator)
{
if (message)
{
// Find sender's name
Player * s = GetPlayer(GameID);
Player * p = (0);
char FName[40];
if (!s)
{
return;
}
GetPostFix(s, FName, 40);
if (s)
{
char Channel[] = "Broadcast"; // Channel
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
if ((p->GameID() != GameID) || copy_to_originator) //only send to originating player if directed to.
{
p->SendClientChatEvent(0x03, 0x00, FName, FName, Channel, message);
}
}
}
}
}
//PM
void PlayerManager::GMMessage(char *message)
{
Player * p = 0;
if (message)
{
//char broadcast[2048];
m_Mutex.Lock();
char Channel[] = "Admin Broadcast"; // Channel
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
if (p->AdminLevel() >= 50)
{
p->SendClientChatEvent(0x03, 0x00,"GM", "GM", Channel, message);
}
}
m_Mutex.Unlock();
}
}
//PM
void PlayerManager::ChatSendChannel(long GameID, char * Channel, char * Message)
{
if (Message && Channel)
{
// Find sender's name
Player * s = GetPlayer(GameID);
Player * p = (0);
char FName[40];
GetPostFix(s, FName, 40);
if (s)
{
m_Mutex.Lock();
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
if (p->Active())
{
p->SendClientChatEvent(0x03, 0x00, FName, FName, Channel, Message);
}
}
m_Mutex.Unlock();
}
}
}
void PlayerManager::GlobalAdminMessage(const char * Message)
{
Player * p = 0;
if (Message)
{
m_Mutex.Lock();
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
p->SendMessageString(Message, 0x0A, false);
}
m_Mutex.Unlock();
}
}
//PM
void PlayerManager::ChatSendPrivate(long GameID, char * Nick, char * Message)
{
if (Nick && Message)
{
Player * p_list = (0);
bool FoundPlayer = false;
char Channel[] = "Private";
Player * p = GetPlayer(GameID);
char FName[40];
GetPostFix(p, FName, 40);
if (p)
{
while (GetNextPlayerOnList(p_list, m_GlobalPlayerList))
{
if (!_stricmp(p_list->Name(), Nick))
{
p_list->SendClientChatEvent(0x03, 0x00, FName, FName, Channel, Message);
FoundPlayer = true;
break;
}
}
if (!FoundPlayer)
{
p->SendVaMessageC(17, "%s is not currently online", Nick);
}
}
if (FoundPlayer)
{
LogChatMsg("tell> %s \"%s\"\n", Nick, Message);
}
}
}
//PM
char *PlayerManager::WhoHtml(size_t *response_length)
{
// returns a list of who is online
// first estimate how much memory we need
int count = 0;
size_t length = 512; // increase this if we add more formatting and decorations
char *player = NULL;
Player * p = (0);
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
player = p->Name();
count++;
length += 128; // increase this if we display other player stuff in the table
}
char *data = (char*)_alloca(length);
//char *data = new char[length];
/*
if (count == 0)
{
strcpy(data,
"<html><head><title>Net-7: Who is Online</title></head><body>\r\n"
"No players are currently online on Net-7\r\n"
"</body></html>\r\n");
}
else if (count == 1)
{
sprintf(data,
"<html><head><title>Net-7: Who is Online</title></head><body>\r\n"
"The only player currently online on Net-7 is %s\r\n"
"</body></html>\r\n", player);
}
else
{
*/
sprintf(data,
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\r"
"<online number=\"%d\">\r\n"
" <list>\r\n",
count);
struct in_addr in;
p = (0);
while (GetNextPlayerOnList(p, m_GlobalPlayerList))
{
if (p)
{
#ifdef WIN32
in.S_un.S_addr = p->PlayerIPAddr();
#else
in.s_addr = p->PlayerIPAddr();
#endif
}
else
{
#ifdef WIN32
in.S_un.S_addr = 0;
#else
in.s_addr = 0;
#endif
}
sprintf(data, "%s <player name=\"%s\" ip=\"%s\" clevel=\"%ld\" elevel=\"%ld\" tlevel=\"%ld\" race=\"%ld\" profession=\"%ld\"/>\r\n", data, p->Name(),
inet_ntoa(in),
p->Database()->info.combat_level, p->Database()->info.explore_level, p->Database()->info.trade_level,
p->Database()->ship_data.race, p->Database()->ship_data.profession);
//strcat(data, " <player name=\"");
//strcat(data, p->Database()->avatar.avatar_first_name);
//strcat(data, "</td></tr>\r\n");
}
strcat(data, " </list>\r\n</online>\r\n");
//strcat(data, "</table></body></html>\r\n");
//}
// Determine the actual length
length = strlen(data);
char header[256];
sprintf(header,
"HTTP/1.1 200 OK\r\n"
"<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">\r\n"
"Content-Type: text/html\r\n"
"Server: AuthServer/2.5\r\n"
"Content-Length: %d\r\n"
"\r\n",
length);
size_t header_length = strlen(header);
*response_length = header_length + length;
char * response = new char[header_length + length + 1]; // !heap alloc!
if (response)
{
strcpy(response, header);
memcpy(response + header_length, data, length);
response[header_length + length] = 0;
}
// done with data
//delete [] data;
return (response);
}
//PM
void PlayerManager::SetUDPConnection(UDP_Connection *connection)
{
m_UDPConnection = connection;
}
//PM
void PlayerManager::SetGlobalMemoryHandler(GMemoryHandler *MemMgr)
{
m_GlobMemMgr = MemMgr;
}
//Method to iterate along a player list
bool PlayerManager::GetNextPlayerOnList(Player *&p, u32 *player_list)
{
//iterate along the player list
u32 index = 0;
u32 block_index = 0;
u32 *entry = player_list;
bool found = false;
if (p)
{
index = (p->GameID() & 0x0000FFFF) + 1;
block_index = index/32;
entry = (u32*) (player_list + block_index);
}
while (index < MAX_ONLINE_PLAYERS)
{
//is this bit set?
if (*entry & (1 << index%32))
{
p = GetPlayer(index | PLAYER_TAG);
if (p)
{
found = true;
break;
}
else
{
*entry &= (0xFFFFFFFF ^ (1 << index%32)); //unset the bit, as it's invalid
}
}
index++;
if (*entry == 0)
{
//skip to start of next block
block_index++;
index = 32*block_index;
entry = (u32*) (player_list + block_index);
}
else if (index%32 == 0)
{
block_index++;
entry = (u32*) (player_list + block_index);
}
};
return found;
}
//object index methods
void PlayerManager::SetIndex(u32 index, u32 *player_list)
{
index &= 0x00FFFFFF;
u32 *entry = (u32*) (player_list + (index/(sizeof(u32)*8)));
//now set the specific bit
*entry |= (1 << index%32);
}
void PlayerManager::UnSetIndex(u32 index, u32 *player_list)
{
index &= 0x00FFFFFF;
u32 *entry = (u32*) (player_list + (index/(sizeof(u32)*8)));
//now unset the specific bit
*entry &= (0xFFFFFFFF ^ (1 << index%32));
}
bool PlayerManager::GetIndex(u32 index, u32 *player_list)
{
index &= 0x00FFFFFF;
u32 *entry = (u32*) (player_list + (index/(sizeof(u32)*8)));
//now get the specific bit
if (*entry & (1 << index%32))
{
return true;
}
else
{
return false;
}
}
//PC Obsolete
void PlayerManager::SetSector(Player * player, long sector_id)
{
m_Mutex.Lock();
player->PlayerIndex()->SetSectorNum(sector_id);
m_Mutex.Unlock();
}