mirror of
https://github.com/therealKyp/Earth-and-Beyond-server
synced 2026-08-13 22:23:03 -04:00
466 lines
14 KiB
C++
466 lines
14 KiB
C++
|
|
// ClientToGlobalServer.cpp
|
||
|
|
|
||
|
|
/***********************************************
|
||
|
|
* /////////////////////////////////////// *
|
||
|
|
* // CLIENT TO GLOBAL SERVER OPCODES // *
|
||
|
|
* /////////////////////////////////////// *
|
||
|
|
***********************************************/
|
||
|
|
|
||
|
|
#include "Net7.h"
|
||
|
|
#include "Connection.h"
|
||
|
|
#include "Opcodes.h"
|
||
|
|
#include "ServerManager.h"
|
||
|
|
#include "PacketStructures.h"
|
||
|
|
#include "PlayerClass.h"
|
||
|
|
#include "PacketMethods.h"
|
||
|
|
|
||
|
|
void Connection::GlobalError(int Error)
|
||
|
|
{
|
||
|
|
static char _ErrorMsg[][200] =
|
||
|
|
{
|
||
|
|
{"Error: You have been temporarily banned."},
|
||
|
|
{"Sorry, that name has already been taken. Please try again."},
|
||
|
|
{"Sorry, your name can only contain the letters a-z. No spaces or other special characters are allowed. Please try again."},
|
||
|
|
{"Sorry, this name is too short. It must contain at least 3 characters. Please try again."},
|
||
|
|
{"Sorry, this name needs enough vowels (a,e,i,o,u & y) to be pronouncable. Please try again."},
|
||
|
|
{"Sorry, there are too many repeating characters in this name. Please try again."},
|
||
|
|
{"Sorry, this name contains a reserved or illegal word. Please try again."},
|
||
|
|
{"Error: Ticket Validation Failed."},
|
||
|
|
{"Error: Authentication Server (AUTHD) is unavailable. Try again in a few minutes."},
|
||
|
|
{"Error: You have not compleated registration."},
|
||
|
|
{"Sorry, that ship name is not allowed. Please try again"},
|
||
|
|
{"Sorry, Net7 experienced an internal error. Please submit a bug report"},
|
||
|
|
{"Sorry, The Stress test is now closed."}
|
||
|
|
};
|
||
|
|
|
||
|
|
//TODO: Recode this to use new packet builder methods
|
||
|
|
char Buffer[1024];
|
||
|
|
char *p = Buffer;
|
||
|
|
|
||
|
|
*((int *) p) = strlen(_ErrorMsg[Error]); p+= 4;
|
||
|
|
*((int *) p) = ntohl(Error+7); p+= 4;
|
||
|
|
|
||
|
|
memcpy(p, _ErrorMsg[Error], strlen(_ErrorMsg[Error]));
|
||
|
|
p+=strlen(_ErrorMsg[Error]);
|
||
|
|
|
||
|
|
SendResponse(ENB_OPCODE_0075_GLOBAL_ERROR, (unsigned char *) &Buffer, (p - Buffer));
|
||
|
|
|
||
|
|
LogDebug("GlobalError: %s\n", _ErrorMsg[Error]);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void Connection::ProcessGlobalServerOpcode(short opcode, short bytes)
|
||
|
|
{
|
||
|
|
switch (opcode)
|
||
|
|
{
|
||
|
|
case ENB_OPCODE_0000_VERSION_REQUEST :
|
||
|
|
HandleVersionRequest();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_0035_MASTER_JOIN :
|
||
|
|
// TODO: figure out why the client sometimes sends a Master Join
|
||
|
|
// packet to the Global Server. The Master Join packet should only
|
||
|
|
// go to the Master Server. Perhaps we should dump this oddball to
|
||
|
|
// a data file so we can study it.
|
||
|
|
//LogMessage("GlobalServer received MasterJoin packet from client\n");
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_006D_GLOBAL_CONNECT :
|
||
|
|
HandleGlobalConnect();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_006E_GLOBAL_TICKET_REQUEST :
|
||
|
|
HandleGlobalTicketRequest();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_0071_GLOBAL_DELETE_CHARACTER :
|
||
|
|
HandleDeleteCharacter();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_0072_GLOBAL_CREATE_CHARACTER :
|
||
|
|
HandleCreateCharacter();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
LogMessage("ProcessGlobalServerOpcode -- UNRECOGNIZED OPCODE 0x%04x\n", opcode);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::HandleVersionRequest()
|
||
|
|
{
|
||
|
|
VersionRequest * request = (VersionRequest *) m_RecvBuffer;
|
||
|
|
|
||
|
|
long major = ntohl(request->Major);
|
||
|
|
long minor = ntohl(request->Minor);
|
||
|
|
|
||
|
|
if (major == 42 && minor == 0)
|
||
|
|
{
|
||
|
|
// Version is good
|
||
|
|
SendVersionResponse(0);
|
||
|
|
}
|
||
|
|
else if (major < 42)
|
||
|
|
{
|
||
|
|
// The client version is old, need to apply the patch
|
||
|
|
SendVersionResponse(1);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// The client version is newer than this server.
|
||
|
|
SendVersionResponse(2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::SendVersionResponse(long status)
|
||
|
|
{
|
||
|
|
SendResponse(ENB_OPCODE_0001_VERSION_RESPONSE, (unsigned char *) &status, sizeof(status));
|
||
|
|
}
|
||
|
|
|
||
|
|
//This gets invoked when a user logs into the account AFTER being validated
|
||
|
|
void Connection::HandleGlobalConnect()
|
||
|
|
{
|
||
|
|
char *ticket = (char *) &m_RecvBuffer[4];
|
||
|
|
|
||
|
|
//This is a pointer assign since GetUsernameFromTicket() uses the string manager
|
||
|
|
m_AccountUsername = g_ServerMgr->m_AccountMgr->GetUsernameFromTicket(ticket);
|
||
|
|
|
||
|
|
if (!m_AccountUsername)
|
||
|
|
{
|
||
|
|
LogDebug("HandleGlobalConnect() - Invalid Username\n");
|
||
|
|
GlobalError(G_ERROR_NET7_INTERNAL);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
long account_id = g_AccountMgr->GetAccountID(m_AccountUsername);
|
||
|
|
long account_status = g_AccountMgr->GetAccountStatus(m_AccountUsername);
|
||
|
|
|
||
|
|
//block people entering while stress server down
|
||
|
|
if (account_status == 0)
|
||
|
|
{
|
||
|
|
GlobalError(G_ERROR_STRESS_TEST_CLOSED);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (account_id == -1)
|
||
|
|
{
|
||
|
|
LogDebug("HandleGlobalConnect() - Invalid AccountID\n");
|
||
|
|
GlobalError(G_ERROR_NET7_INTERNAL);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (account_status == -1) // Banned
|
||
|
|
{
|
||
|
|
LogDebug("HandleGlobalConnect() - Banned Account\n");
|
||
|
|
GlobalError(G_ERROR_BANNED_ACCOUNT);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (account_status == -2) // Complete Registration
|
||
|
|
{
|
||
|
|
LogDebug("HandleGlobalConnect() - Inactive Account\n");
|
||
|
|
GlobalError(G_ERROR_INACTIVE_ACCOUNT);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
SendAvatarList(g_AccountMgr->GetAccountID(m_AccountUsername));
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::HandleDeleteCharacter()
|
||
|
|
{
|
||
|
|
long character_slot = ntohl(*(long *) m_RecvBuffer);
|
||
|
|
long avatar_id = g_AccountMgr->GetAvatarID(m_AccountUsername, character_slot);
|
||
|
|
|
||
|
|
LogMessage("Delete character %d\n", avatar_id);
|
||
|
|
|
||
|
|
g_AccountMgr->DeleteCharacter(avatar_id);
|
||
|
|
|
||
|
|
SendAvatarList(ACCOUNT_ID(avatar_id));
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::HandleCreateCharacter()
|
||
|
|
{
|
||
|
|
GlobalCreateCharacter *create= (GlobalCreateCharacter *) m_RecvBuffer;
|
||
|
|
|
||
|
|
m_AccountUsername = g_StringMgr->GetStr(create->account_username);
|
||
|
|
long ret_val = g_AccountMgr->CreateCharacter(create);
|
||
|
|
|
||
|
|
if (ret_val != -1)
|
||
|
|
{
|
||
|
|
GlobalError(ret_val);
|
||
|
|
return;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
LogDebug("New character `%s` created\n", create->avatar.avatar_first_name);
|
||
|
|
|
||
|
|
SendAvatarList(g_AccountMgr->GetAccountID(m_AccountUsername));
|
||
|
|
}
|
||
|
|
|
||
|
|
//This sends the client the avatar list after: Logging in, deleting character, creating character
|
||
|
|
void Connection::SendAvatarList(long account_id)
|
||
|
|
{
|
||
|
|
GlobalAvatarList avatar_list;
|
||
|
|
g_AccountMgr->BuildAvatarList(&avatar_list, account_id);
|
||
|
|
|
||
|
|
SendResponse(ENB_OPCODE_0070_GLOBAL_AVATAR_LIST, (unsigned char *) &avatar_list, sizeof(GlobalAvatarList));
|
||
|
|
}
|
||
|
|
|
||
|
|
//Occurs when the player selects a charachter
|
||
|
|
void Connection::HandleGlobalTicketRequest()
|
||
|
|
{
|
||
|
|
// The player selected a character
|
||
|
|
long char_slot = ntohl(*(long *) m_RecvBuffer);
|
||
|
|
long avatar_id = g_AccountMgr->GetAvatarID(m_AccountUsername, char_slot);
|
||
|
|
|
||
|
|
if (avatar_id == -1)
|
||
|
|
{
|
||
|
|
LogMessage("GlobalTicketRequest() Error obtaining global character slot id\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
LogDebug("Received GlobalTicketRequest packet -- User: `%s`, Slot: %d\n", m_AccountUsername, char_slot);
|
||
|
|
|
||
|
|
//Now finally we can allocate a player slot, since the player is logging into the game
|
||
|
|
Player *player = g_ServerMgr->m_GlobMemMgr->GetPlayerNode(m_AccountUsername);
|
||
|
|
|
||
|
|
if (player)
|
||
|
|
{
|
||
|
|
player->SetCharacterID(avatar_id);
|
||
|
|
player->SetCharacterSlot(char_slot);
|
||
|
|
g_PlayerMgr->SetupPlayer(player, m_IPaddr);
|
||
|
|
player->SetAccountUsername(m_AccountUsername);
|
||
|
|
player->SetGameID(player->CharacterID() | PLAYER_TAG);
|
||
|
|
|
||
|
|
player->SetPlayerPortIP(0, 0);
|
||
|
|
|
||
|
|
LogMessage("Allocated %s a playerID of 0x%08x [%d]\n", m_AccountUsername, player->GameID(), player->CharacterID());
|
||
|
|
|
||
|
|
//Read the charachter database
|
||
|
|
g_AccountMgr->ReadDatabase(player->Database(), avatar_id);
|
||
|
|
player->SetName(player->Database()->avatar.avatar_first_name);
|
||
|
|
m_AvatarID = player->GameID();
|
||
|
|
|
||
|
|
ProcessGlobalTicket(player);
|
||
|
|
|
||
|
|
//LogMessage("PlayerID: %d[%s] status is: %c\n", slot_id, g_ServerMgr->m_AccountMgr->GetAccountUsername(slot_id), g_ServerMgr->m_AccountMgr->GetAccountStatus(slot_id));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
LogMessage("** Critical Error: Can't get AvatarID [%d]! **\n", avatar_id);
|
||
|
|
SendGlobalTicket(player->GameID(), 0, 1002, false); //galaxy full
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//TODO: Recode this
|
||
|
|
void Connection::SendGlobalTicket(long avatar_id, long sector_id, long level, bool issue)
|
||
|
|
{
|
||
|
|
GlobalTicket ticket;
|
||
|
|
memset(&ticket, 0, sizeof(ticket));
|
||
|
|
unsigned char *ptr_ticket = (unsigned char *)&ticket;
|
||
|
|
int index = 0;
|
||
|
|
|
||
|
|
// 1000 = 'Error: AvatarSelectionTask-User not authorized'
|
||
|
|
// 'Error: You have not been authorized to play this character.'
|
||
|
|
// 1002 = 'Error: AvatarSelectionTask-Shard is full'
|
||
|
|
// 'Error: This galaxy is full, please select another character.'
|
||
|
|
|
||
|
|
if (issue)
|
||
|
|
{
|
||
|
|
AddDataFlip4(ptr_ticket, 0, index);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
AddDataFlip4(ptr_ticket, level, index);
|
||
|
|
}
|
||
|
|
|
||
|
|
index = 20;
|
||
|
|
|
||
|
|
AddDataFlip4(ptr_ticket, avatar_id, index);
|
||
|
|
AddDataFlip4(ptr_ticket, sector_id, index);
|
||
|
|
index = 32;
|
||
|
|
AddData(ptr_ticket, level, index);
|
||
|
|
index = 48;
|
||
|
|
AddDataS(ptr_ticket, "MY_Avatar_Ticket", index);
|
||
|
|
|
||
|
|
SendResponse(ENB_OPCODE_006F_GLOBAL_TICKET, (unsigned char *) &ticket, sizeof(ticket));
|
||
|
|
}
|
||
|
|
|
||
|
|
//TODO: Recode this
|
||
|
|
void Connection::ProcessGlobalTicket(Player *player)
|
||
|
|
{
|
||
|
|
long sector_id = ntohl(player->Database()->info.sector_id);
|
||
|
|
|
||
|
|
if (1)
|
||
|
|
{
|
||
|
|
LogDebug("trying to connect to %d\n",sector_id);
|
||
|
|
SectorManager * mgr = g_ServerMgr->GetSectorManager(sector_id);
|
||
|
|
if (!mgr)
|
||
|
|
{
|
||
|
|
// Oops, Sector Server is offline
|
||
|
|
// Dump the player into the Net-7 SOL station for now
|
||
|
|
sector_id = 10711;
|
||
|
|
mgr = g_ServerMgr->GetSectorManager(sector_id);
|
||
|
|
if (!mgr)
|
||
|
|
{
|
||
|
|
LogMessage("Fatal error: Sector Servers are unreachable\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
player->Database()->info.sector_id = ntohl(sector_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
//long avatar_id = player->CharacterID() | PLAYER_TAG ;
|
||
|
|
|
||
|
|
SendGlobalTicket(player->GameID(), sector_id, g_AccountMgr->GetAccountStatus(player->AccountUsername()), true);
|
||
|
|
|
||
|
|
//LogMessage("Sending GlobalTicket packet, SectorID = %d\n", m_SectorID);
|
||
|
|
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
LogDebug("Unable to load character database for AvatarID %d\n", player->GameID());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::ProcessProxyGlobalOpcode(short opcode, short bytes)
|
||
|
|
{
|
||
|
|
switch (opcode)
|
||
|
|
{
|
||
|
|
case ENB_OPCODE_2000_ACCOUNTDATA:
|
||
|
|
LogMessage("Received account login attempt from Net7Proxy.\n");
|
||
|
|
HandleAccountValid(bytes);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2002_TICKET:
|
||
|
|
LogMessage("Processing TCP Ticket.\n");
|
||
|
|
ProcessTicketInfo(bytes);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2011_SET_PROXY_SECTOR_LINK:
|
||
|
|
SetConnectionToProxyLink();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2004_AVATARLOGIN:
|
||
|
|
LogMessage("Received TCP AvatarLogin confirm (start player)\n");
|
||
|
|
HandleGlobalTicketRequest();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_200B_CREATE_AVATAR:
|
||
|
|
LogMessage("Received TCP Create Avatar packet\n");
|
||
|
|
HandleCreateCharacter();
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_200D_DELETE_AVATAR:
|
||
|
|
LogMessage("Received TCP Delete Avatar packet\n");
|
||
|
|
HandleDeleteCharacter();
|
||
|
|
//HandleAvatarDeleteRequest(msg, hdr, source_addr, source_port);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ENB_OPCODE_2008_MASTER_HANDOFF:
|
||
|
|
LogMessage("Recieved TCP master handoff\n");
|
||
|
|
HandleMasterHandoff();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
LogMessage("Illegal opcode from Server to Global Account connection\n");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::HandleAccountValid(short bytes)
|
||
|
|
{
|
||
|
|
char username[64];
|
||
|
|
char password[64];
|
||
|
|
int index = 0;
|
||
|
|
char info[128] = "Valid=False\r\n";
|
||
|
|
long length = 13;
|
||
|
|
|
||
|
|
ExtractDataLS(m_RecvBuffer, username, index);
|
||
|
|
ExtractDataLS(m_RecvBuffer, password, index);
|
||
|
|
|
||
|
|
LogMessage("Account info received for: %s\n", username);
|
||
|
|
|
||
|
|
if (username && password)
|
||
|
|
{
|
||
|
|
char * ticket = 0;
|
||
|
|
|
||
|
|
unsigned char *ip = (unsigned char *)&m_IPaddr;
|
||
|
|
LogMessage("User '%s' logging in from IP: %u.%u.%u.%u\n", username, ip[0], ip[1], ip[2], ip[3]);
|
||
|
|
|
||
|
|
// TODO: This is a good place to check for "banned" IP addresses.
|
||
|
|
// TODO: we need to create a list of accounts which are logged in successfully, so Net7Proxy create/delete can't be abused
|
||
|
|
|
||
|
|
if (ticket = g_AccountMgr->IssueTicket(username, password))
|
||
|
|
{
|
||
|
|
length = sprintf(info, "Valid=TRUE\r\nTicket=%s\r\n", ticket);
|
||
|
|
LogMessage("Valid ticket %s\n",ticket);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
SendResponse(ENB_OPCODE_2001_ACCOUNTVALID, (unsigned char *) &info, length);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::SetConnectionToProxyLink()
|
||
|
|
{
|
||
|
|
//now set this connection link to be a sector proxy link
|
||
|
|
m_ServerType = CONNECTION_TYPE_SECTOR_SERVER_TO_PROXY;
|
||
|
|
LogMessage("--> Changed TCP link to sector proxy link.\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::ProcessTicketInfo(short bytes)
|
||
|
|
{
|
||
|
|
//ok, we have a new ticket, lets validate the ticket and send the avatarlist
|
||
|
|
char *ticket = (char *) &m_RecvBuffer;
|
||
|
|
|
||
|
|
//This is a pointer assign since GetUsernameFromTicket() uses the string manager
|
||
|
|
//char *account_name = g_ServerMgr->m_AccountMgr->GetUsernameFromTicket(ticket);
|
||
|
|
char *account_name = strtok(ticket, "-");
|
||
|
|
|
||
|
|
if (!account_name)
|
||
|
|
{
|
||
|
|
LogMessage("ProcessTicketInfo() - Invalid Username\n");
|
||
|
|
GlobalError(G_ERROR_NET7_INTERNAL);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_AccountUsername = g_ServerMgr->m_StringMgr->GetStr(account_name);
|
||
|
|
|
||
|
|
long account_id = g_AccountMgr->GetAccountID(account_name);
|
||
|
|
long account_status = g_AccountMgr->GetAccountStatus(account_name);
|
||
|
|
|
||
|
|
//block people entering while stress server down
|
||
|
|
/*if (account_status == 0)
|
||
|
|
{
|
||
|
|
SendGlobalError(G_ERROR_STRESS_TEST_CLOSED);
|
||
|
|
return false;
|
||
|
|
}*/
|
||
|
|
|
||
|
|
if (account_id == -1)
|
||
|
|
{
|
||
|
|
LogMessage("ProcessTicketInfo() - Invalid AccountID\n");
|
||
|
|
GlobalError(G_ERROR_NET7_INTERNAL);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (account_status == -1) // Banned
|
||
|
|
{
|
||
|
|
LogMessage("ProcessTicketInfo() - Banned Account\n");
|
||
|
|
GlobalError(G_ERROR_BANNED_ACCOUNT);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (account_status == -2) // Complete Registration
|
||
|
|
{
|
||
|
|
LogMessage("ProcessTicketInfo() - Inactive Account\n");
|
||
|
|
GlobalError(G_ERROR_INACTIVE_ACCOUNT);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
SendAvatarList(g_AccountMgr->GetAccountID(account_name));
|
||
|
|
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::HandleMasterHandoff()
|
||
|
|
{
|
||
|
|
LogMessage("!!! Unimplemented opcode used - Implement me if TCP master login is what you want.\n");
|
||
|
|
}
|