BadCompany2.MasterServer/source/Framework/GameClient.cpp
2015-10-10 17:08:19 +02:00

2241 lines
78 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "GameClient.h"
#include "Framework.h"
#include "../Base64.h"
#include <boost/filesystem/operations.hpp>
extern Logger* debug;
extern Framework* fw;
GameClient::GameClient(int connectionType, Database* db, string emuIp, bool isLocal)
{
this->connectionType = connectionType;
this->logDatabase = fw->DatabaseLogging();
database = db;
user.data = NULL;
persona.data = NULL;
this->isLocal = isLocal;
this->emuIp = emuIp;
alreadyLoggedIn = false;
stats_data = NULL;
sock_tid = 0;
queue_position = 0;
}
GameClient::~GameClient()
{
if(connectionType == GAME_CLIENT_SSL)
{
if(user.data && (user.data[USER_ONLINE].compare("1") == 0) && !alreadyLoggedIn)
{
user.data[USER_ONLINE] = "0";
if(persona.data)
persona.data[PERSONA_ONLINE] = "0";
}
}
playerList.clear();
if(connectionType == GAME_CLIENT_SSL && stats_data)
delete stats_data;
}
bool GameClient::acct(list<Packet*>* queue, Packet* packet, string txn, const char* ip)
{
Packet* sendPacket = NULL;
if(txn.compare("GetCountryList") == 0) // User wants to create a new account
{
string countryData("");
ifstream statsFile("./templates/countryList", ifstream::in);
if(statsFile.is_open() && statsFile.good())
{
stringstream buffer;
buffer << statsFile.rdbuf();
countryData = buffer.str();
statsFile.close();
}
size_t count = 0, start = 0;
while((start = countryData.find("\n", start)) != string::npos)
{
count++;
start++;
}
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("countryList.[]", count);
start = 0;
for(size_t i = 0; i < count; i++)
{
char buffer[48];
size_t end = countryData.find("=", start);
if(end != string::npos)
{
string data = countryData.substr(start, end-start);
sprintf(buffer, "countryList.%i.ISOCode", i);
sendPacket->SetVar(buffer, data);
start = end+1;
end = countryData.find("\n", start);
if(end == string::npos)
end = countryData.size(); //we are at the last line
data = countryData.substr(start, end-start);
sprintf(buffer, "countryList.%i.description", i);
sendPacket->SetVar(buffer, data);
if(end != countryData.size())
start = end+1;
}
else
{
debug->warning(2, connectionType, "countryData is corrupted, check the file!");
break;
}
}
}
else if(txn.compare("NuGetTos") == 0)
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("version", "20426_20.20426_20");
string copiedData = "";
ifstream sourceFile("./templates/termsOfService", ifstream::in);
if(sourceFile.is_open() && sourceFile.good())
{
stringstream buffer;
buffer << sourceFile.rdbuf();
copiedData = buffer.str();
sourceFile.close();
}
else
debug->warning(2, DEBUG, "NuGetTos, failed to open termsOfService template, check if the directory and the files exist.");
if(copiedData.size() > 1)
{
size_t pos = 0;
while((pos = copiedData.find_first_of('%', pos+1)) != string::npos)
copiedData.replace(pos, 1, "%25");
pos = 0;
while((pos = copiedData.find_first_of('\n', pos+1)) != string::npos)
copiedData.replace(pos, 1, "%0a");
pos = 0;
while((pos = copiedData.find_first_of(':', pos+1)) != string::npos)
copiedData.replace(pos, 1, "%3a");
pos = 0;
while((pos = copiedData.find_first_of('\"', pos+1)) != string::npos)
copiedData.replace(pos, 1, "%22");
pos = 0;
while((pos = copiedData.find_first_of('\t', pos+1)) != string::npos)
copiedData.replace(pos, 1, "%09");
}
copiedData.append("%0a%0a%09MASE%3aBC2 v0.9 made by Triver");
copiedData.insert(copiedData.begin(), '\"');
copiedData.append("\"");
sendPacket->SetVar("tos", copiedData);
}
else if(txn.compare("NuAddAccount") == 0)
{
list_entry tempUser;
tempUser.name = packet->GetVar("nuid");
int realSize = tempUser.name.size();
int pos = -1;
while((pos = tempUser.name.find_first_of('%', pos+1)) != (int)string::npos)
realSize -= 2;
if(realSize > 32 || realSize < 3) // entered user name length is out of bounds
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("errorContainer.[]", "1");
sendPacket->SetVar("errorCode", "21");
sendPacket->SetVar("localizedMessage", "\"The required parameters for this call are missing or invalid\"");
sendPacket->SetVar("errorContainer.0.fieldName", "displayName");
if(realSize > 32)
{
sendPacket->SetVar("errorContainer.0.fieldError", "3");
sendPacket->SetVar("errorContainer.0.value", "TOO_LONG");
}
else
{
sendPacket->SetVar("errorContainer.0.fieldError", "2");
sendPacket->SetVar("errorContainer.0.value", "TOO_SHORT");
}
} //forbidden chars due to file creation incompatibilities
else if(tempUser.name.find_first_of('/') != string::npos || tempUser.name.find_first_of('\\') != string::npos || tempUser.name.find_first_of('?') != string::npos ||
tempUser.name.find_first_of('*') != string::npos || tempUser.name.find_first_of('\"') != string::npos || tempUser.name.find_first_of('|') != string::npos ||
tempUser.name.find_first_of(':') != string::npos || tempUser.name.find_first_of('<') != string::npos || tempUser.name.find_first_of('>') != string::npos)
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("errorContainer.[]", "1");
sendPacket->SetVar("errorCode", "21");
sendPacket->SetVar("localizedMessage", "\"The required parameters for this call are missing or invalid\"");
sendPacket->SetVar("errorContainer.0.fieldName", "displayName");
sendPacket->SetVar("errorContainer.0.fieldError", "6");
sendPacket->SetVar("errorContainer.0.value", "NOT_ALLOWED");
}
else //first check the age then do database stuff
{
time_t rawtime;
struct tm* lt;
time (&rawtime);
lt = localtime(&rawtime);
int bd_year = lexical_cast<int>(packet->GetVar("DOBYear"));
int bd_month = lexical_cast<int>(packet->GetVar("DOBMonth"));
int bd_day = lexical_cast<int>(packet->GetVar("DOBDay"));
bool isValid = false;
//have to do age check manually because boost::posix_time is too painful to use
if((lt->tm_year+1900)-bd_year > AGE_LIMIT)
isValid = true;
else if((lt->tm_year+1900)-bd_year == AGE_LIMIT)
{
if((lt->tm_mon+1) > bd_month)
isValid = true;
else if((lt->tm_mon+1) == bd_month)
{
if(lt->tm_mday >= bd_day)
isValid = true;
}
}
if(isValid)
{
tempUser.data = new string[USER_SIZE];
tempUser.data[PASSWORD] = packet->GetVar("password");
tempUser.data[COUNTRY] = packet->GetVar("country");
char buffer[12];
sprintf(buffer, "%i-%i-%i", bd_year, bd_month, bd_day);
tempUser.data[BIRTHDAY] = buffer;
tempUser.data[USER_ONLINE] = "0";
if((tempUser.id = database->addUser(tempUser)) < 0)
{
delete[] tempUser.data;
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("localizedMessage", "\"That account name is already taken\"");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "160");
}
else // returned id is valid so create folder for user
{
string directory = "./database/";
directory.append(tempUser.name);
filesystem::path user_dir(directory);
if(filesystem::create_directory(user_dir))
{
if(logDatabase)
debug->simpleNotification(3, DATABASE, database->listEntryToString(&tempUser, "added", true).c_str());
}
else
debug->notification(2, connectionType, "Failed to create user directory %s, check if you have access permissions for your database folder", directory.c_str());
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
}
}
else // new user is not old enough
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("localizedMessage", "\"The required parameters for this call are missing or invalid\"");
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("errorContainer.[]", "1");
sendPacket->SetVar("errorContainer.0.fieldName", "dob");
sendPacket->SetVar("errorContainer.0.fieldError", "15");
sendPacket->SetVar("errorCode", "21");
}
}
}
else if(txn.compare("NuLogin") == 0) // User is logging in with email and password
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
string buffer = packet->GetVar("nuid"), password;
if(!buffer.empty())
{
database->getUser(buffer, &user);
if(user.id > 0)
password = packet->GetVar("password");
}
else
{
string decryptedInfo = packet->GetVar("encryptedInfo");
if(!decryptedInfo.empty() && decryptedInfo.find("Ciyvab0tregdVsBtboIpeChe4G6uzC1v5_-SIxmvSL") != string::npos)
{
decryptedInfo = decryptedInfo.substr(42, decryptedInfo.size()-42); //42 is the size of that first chunk
size_t pos = 0;
while( (pos = decryptedInfo.find('-', decryptedInfo.size()-3)) != string::npos ||
(pos = decryptedInfo.find('_', decryptedInfo.size()-3)) != string::npos) // bring string into proper format again
decryptedInfo.at(pos) = '=';
decryptedInfo = base64_decode(decryptedInfo);
if((pos = decryptedInfo.find_first_of('\f')) != string::npos)
{
buffer = decryptedInfo.substr(0, pos);
password = decryptedInfo.substr(pos+1, decryptedInfo.size()-1); //skip the '\f' and dont go outside string
database->getUser(buffer, &user);
}
else
user.id = -1;
}
else
user.id = -1;
}
if(user.id > 0)
{
if(user.data[PASSWORD].compare(password) == 0)
{
if(user.data[USER_ONLINE].compare("0") == 0)
{
user_lkey = fw->randomString(27, SEED_DEFAULT);
user_lkey.append(".");
user.data[USER_ONLINE] = "1";
sendPacket->SetVar("lkey", user_lkey);
sendPacket->SetVar("nuid", user.name);
string encryptedInfo = packet->GetVar("returnEncryptedInfo");
if(!encryptedInfo.empty() && (encryptedInfo.compare("1") == 0)) //client wants to store login information
{
//Ciyvab0tregdVsBtboIpeChe4G6uzC1v5_-SIxmvSL
//Ciyvab0tregdVsBtboIpeChe4G6uzC1v5_-SIxmvSLIJIU9Vh4C77cH52ArVStN4rKMtWGByi2mOVDuM05Wj31Vit91nAbmg23z2IV4GfubCi9Owt1eBqxmXS6KrceaR
// store the user name and password as a base64 encoded string and put the chunk in front of it (so we have a similar format to the original one)
string encrypted("Ciyvab0tregdVsBtboIpeChe4G6uzC1v5_-SIxmvSL");
size_t pos = 0;
buffer.append("\f");
buffer.append(password);
buffer = base64_encode(reinterpret_cast<const unsigned char*>(buffer.c_str()), buffer.size());
pos = buffer.find('=', buffer.size()-3);
if(pos != string::npos)
{
buffer.at(pos) = '-';
pos = buffer.find('=', buffer.size()-3);
if(pos != string::npos)
buffer.at(pos) = '_';
}
encrypted.append(buffer);
sendPacket->SetVar("encryptedLoginInfo", encrypted);
//sendPacket->SetVar("encryptedLoginInfo", "Ciyvab0tregdVsBtboIpeChe4G6uzC1v5_-SIxmvSLJubxCaMWOxD1iabTkdIjA6nFyrA9nvKvtbovnovBD99T8hIHEGPkl-I9xeEXAXN9ICCmuaWcMHP6q33WjvASgI"); // is this required?
}
sendPacket->SetVar("profileId", user.id);
sendPacket->SetVar("userId", user.id);
if(logDatabase)
debug->simpleNotification(3, DATABASE, database->listEntryToString(&user, "logged in", true).c_str());
}
else // user is already online so prevent login (yes, fsys is correct here)
{
debug->notification(3, connectionType, "User %s with ID %i is already logged in, disconnecting this user...", user.name.c_str(), user.id);
delete sendPacket;
alreadyLoggedIn = true;
sendPacket = new Packet("fsys", 0x00000000);
sendPacket->SetVar("TXN", "Goodbye");
sendPacket->SetVar("reason", "2");
}
}
else
{
sendPacket->SetVar("localizedMessage", "\"The password the user specified is incorrect\"");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "122");
/*sendPacket->SetVar("localizedMessage", "\"The user is not entitled to access this game\"");
sendPacket->SetVar("errorCode", "120");*/
}
}
else
{
sendPacket->SetVar("localizedMessage", "\"The user was not found\"");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "101");
}
}
else if(txn.compare("NuGetPersonas") == 0) // Display all Personas to the User
{
list<list_entry> personas;
database->listMatchingPersonas(USER_ID, lexical_cast<string>(user.id), &personas);
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("personas.[]", (int)personas.size());
int i = 0;
while(!personas.empty())
{
string buffer = "personas.";
buffer.append(lexical_cast<string>(i));
sendPacket->SetVar(buffer, personas.front().name);
personas.pop_front();
i++;
}
}
else if(txn.compare("NuLoginPersona") == 0) // User logs in with selected Persona
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
if(!database->getPersona(packet->GetVar("name"), &persona))
{
sendPacket->SetVar("localizedMessage", "\"The user was not found\"");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "101");
}
else
{
persona_lkey = fw->randomString(27, SEED_DEFAULT);
persona_lkey.append(".");
persona.data[PERSONA_ONLINE] = "1";
sendPacket->SetVar("lkey", persona_lkey);
sendPacket->SetVar("profileId", persona.id);
sendPacket->SetVar("userId", persona.id);
linked_key lkey_id = {persona.id, persona_lkey};
database->personaLogin(lkey_id);
if(logDatabase)
debug->simpleNotification(3, DATABASE, database->listEntryToString(&persona, "logged in", false).c_str());
}
}
else if(txn.compare("NuAddPersona") == 0) // User wants to add a Persona
{
string name = packet->GetVar("name");
int realSize = name.size();
int pos = -1;
while((pos = name.find_first_of('%', pos+1)) != (int)string::npos)
realSize -= 2;
if(realSize > 16 || realSize < 3) // entered persona name length is out of bounds (old: 4 <= name <= 16)
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("errorContainer.[]", "1");
sendPacket->SetVar("errorCode", "21");
sendPacket->SetVar("localizedMessage", "\"The required parameters for this call are missing or invalid\"");
sendPacket->SetVar("errorContainer.0.fieldName", "displayName");
if(realSize > 16)
{
sendPacket->SetVar("errorContainer.0.fieldError", "3");
sendPacket->SetVar("errorContainer.0.value", "TOO_LONG");
}
else
{
sendPacket->SetVar("errorContainer.0.fieldError", "2");
sendPacket->SetVar("errorContainer.0.value", "TOO_SHORT");
}
} //forbidden chars due to file creation incompatibilities
else if(name.find_first_of('/') != string::npos || name.find_first_of('\\') != string::npos || name.find_first_of('?') != string::npos ||
name.find_first_of('*') != string::npos || name.find_first_of('\"') != string::npos || name.find_first_of('|') != string::npos ||
name.find_first_of(':') != string::npos ||name.find_first_of('<') != string::npos || name.find_first_of('>') != string::npos)
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("errorContainer.[]", "1");
sendPacket->SetVar("errorCode", "21");
sendPacket->SetVar("localizedMessage", "\"The required parameters for this call are missing or invalid\"");
sendPacket->SetVar("errorContainer.0.fieldName", "displayName");
sendPacket->SetVar("errorContainer.0.fieldError", "6");
sendPacket->SetVar("errorContainer.0.value", "NOT_ALLOWED");
}
else //valid data, check if name is unique
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
list_entry tempPersona;
tempPersona.name = name;
tempPersona.data = new string[PERSONA_SIZE];
tempPersona.data[USER_ID] = lexical_cast<string>(user.id);
tempPersona.data[USER_EMAIL] = user.name;
tempPersona.data[PLAYER_ID] = "0";
tempPersona.data[PERSONA_ONLINE] = "0";
if((tempPersona.id = database->addPersona(tempPersona)) < 0) //name already exists
{
delete[] tempPersona.data;
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("localizedMessage", "\"That account name is already taken\"");
sendPacket->SetVar("errorCode", "160");
}
else // persona added, create a default stats file for it
{
// will tempStats be "deleted" when we leave this function? I sure hope so!
Stats tempStats = Stats(user.name, name, true);
if(logDatabase)
debug->simpleNotification(3, DATABASE, database->listEntryToString(&tempPersona, "added", false).c_str());
}
}
}
else if(txn.compare("NuDisablePersona") == 0) // User wants to remove a Persona
{
list_entry temp;
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
bool valid = false;
string name = packet->GetVar("name");
if(!database->getPersona(name, &temp))
{
sendPacket->SetVar("localizedMessage", "\"The data necessary for this transaction was not found\"");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "104");
}
else
{
if(user.id != lexical_cast<int>(temp.data[USER_ID]))
{
sendPacket->SetVar("localizedMessage", "\"The data necessary for this transaction was not found\"");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "104");
}
else
valid = true;
}
if(valid)
{
database->removePersona(name);
Stats tempStats = Stats(user.name, name, false, true);
tempStats.deleteStats();
}
}
else if(txn.compare("GetTelemetryToken") == 0)
{
//TODO: make the telemetry token database
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
//sendPacket->SetVar("telemetryToken", "MTU5LjE1My4yMzUuMjYsOTk0NixlblVTLF7ZmajcnLfGpKSJk53K/4WQj7LRw9asjLHvxLGhgoaMsrDE3bGWhsyb4e6woYKGjJiw4MCBg4bMsrnKibuDppiWxYKditSp0amvhJmStMiMlrHk4IGzhoyYsO7A4dLM26rTgAo%3d");
//changed ip to ip of the emulator, port remains the same (9946?)
size_t pos = 0;
string token = emuIp;
if(lexical_cast<int>(fw->portsCfg().emulator_port) <= 0)
token = "127.0.0.1";
token.append(",");
token.append(fw->portsCfg().emulator_port);
token.append(",enUS,^ٙ<>ܜ<EFBFBD>Ƥ<EFBFBD><C6A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֬<EFBFBD><D6AC><EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݱ<EFBFBD><DDB1>̛<EFBFBD><EFBFBD><EEB0A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̲<EFBFBD>ʉ<EFBFBD><CA89><EFBFBD><EFBFBD><EFBFBD>ł<EFBFBD><C582>ԩѩ<D4A9><D1A9><EFBFBD><EFBFBD><EFBFBD>Ȍ<EFBFBD><C88C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>۪Ӏ");
token = base64_encode(reinterpret_cast<const unsigned char*>(token.c_str()), token.size());
while((pos = token.find('=', token.size()-9)) != string::npos) // bring string into proper format again
token.replace(pos, 1, "%3d");
sendPacket->SetVar("telemetryToken", token);
sendPacket->SetVar("enabled", "CA,MX,PR,US,VI,AD,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AW,AX,AZ,BA,BB,BD,BF,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CC,CD,CF,CG,CI,CK,CL,CM,CN,CO,CR,CU,CV,CX,DJ,DM,DO,DZ,EC,EG,EH,ER,ET,FJ,FK,FM,FO,GA,GD,GE,GF,GG,GH,GI,GL,GM,GN,GP,GQ,GS,GT,GU,GW,GY,HM,HN,HT,ID,IL,IM,IN,IO,IQ,IR,IS,JE,JM,JO,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,LY,MA,MC,MD,ME,MG,MH,ML,MM,MN,MO,MP,MQ,MR,MS,MU,MV,MW,MY,MZ,NA,NC,NE,NF,NG,NI,NP,NR,NU,OM,PA,PE,PF,PG,PH,PK,PM,PN,PS,PW,PY,QA,RE,RS,RW,SA,SB,SC,clntSock,SG,SH,SJ,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TL,TM,TN,TO,TT,TV,TZ,UA,UG,UM,UY,UZ,VA,VC,VE,VG,VN,VU,WF,WS,YE,YT,ZM,ZW,ZZ");
sendPacket->SetVar("filters", "");
sendPacket->SetVar("disabled", "");
}
else if(txn.compare("NuGetEntitlements") == 0)
{
string groupName = packet->GetVar("groupName");
if(groupName.compare("BFBC2PC") == 0)
{
//TODO: make the BFBC2 entitlements database
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
int count = 0;
char buffer[36];
if(fw->emuCfg().vietnam_for_all)
{
sendPacket->SetVar("entitlements.0.grantDate", "2010-12-21T22%3a34Z");
sendPacket->SetVar("entitlements.0.groupName", groupName);
sendPacket->SetVar("entitlements.0.userId", user.id);
sendPacket->SetVar("entitlements.0.entitlementTag", "BFBC2%3aPC%3aVIETNAM_ACCESS");
sendPacket->SetVar("entitlements.0.version", "0");
sendPacket->SetVar("entitlements.0.terminationDate", "");
sendPacket->SetVar("entitlements.0.productId", "DR%3a219316800");
sendPacket->SetVar("entitlements.0.entitlementId", "1");
sendPacket->SetVar("entitlements.0.status", "ACTIVE");
sendPacket->SetVar("entitlements.1.grantDate", "2010-12-21T22%3a34Z");
sendPacket->SetVar("entitlements.1.statusReasonCode", "");
sendPacket->SetVar("entitlements.1.groupName", groupName);
sendPacket->SetVar("entitlements.1.userId", user.id);
sendPacket->SetVar("entitlements.1.entitlementTag", "BFBC2%3aPC%3aVIETNAM_PDLC");
sendPacket->SetVar("entitlements.1.version", "0");
sendPacket->SetVar("entitlements.1.terminationDate", "");
sendPacket->SetVar("entitlements.1.productId", "DR%3a219316800");
sendPacket->SetVar("entitlements.1.entitlementId", "2");
sendPacket->SetVar("entitlements.1.status", "ACTIVE");
count += 2;
}
if(fw->emuCfg().premium_for_all)
{
// need a buffer here to write the entitlement index number because this can vary
sprintf(buffer, "entitlements.%i.grantDate", count);
sendPacket->SetVar(buffer, "2010-12-21T22%3a34Z");
sprintf(buffer, "entitlements.%i.statusReasonCode", count);
sendPacket->SetVar(buffer, "");
sprintf(buffer, "entitlements.%i.groupName", count);
sendPacket->SetVar(buffer, groupName);
sprintf(buffer, "entitlements.%i.userId", count);
sendPacket->SetVar(buffer, user.id);
sprintf(buffer, "entitlements.%i.entitlementTag", count);
sendPacket->SetVar(buffer, "BFBC2%3aPC%3aLimitedEdition");
sprintf(buffer, "entitlements.%i.version", count);
sendPacket->SetVar(buffer, "1");
sprintf(buffer, "entitlements.%i.terminationDate", count);
sendPacket->SetVar(buffer, "");
sprintf(buffer, "entitlements.%i.productId", count);
sendPacket->SetVar(buffer, "OFB-BFBC%3a19120");
sprintf(buffer, "entitlements.%i.entitlementId", count);
sendPacket->SetVar(buffer, "3");
sprintf(buffer, "entitlements.%i.status", count);
sendPacket->SetVar(buffer, "ACTIVE");
count++;
}
if(fw->emuCfg().specact_for_all)
{
sprintf(buffer, "entitlements.%i.grantDate", count);
sendPacket->SetVar(buffer, "2010-12-21T22%3a34Z");
sprintf(buffer, "entitlements.%i.statusReasonCode", count);
sendPacket->SetVar(buffer, "");
sprintf(buffer, "entitlements.%i.groupName", count);
sendPacket->SetVar(buffer, groupName);
sprintf(buffer, "entitlements.%i.userId", count);
sendPacket->SetVar(buffer, user.id);
sprintf(buffer, "entitlements.%i.entitlementTag", count);
sendPacket->SetVar(buffer, "BFBC2%3aPC%3aALLKIT");
sprintf(buffer, "entitlements.%i.version", count);
sendPacket->SetVar(buffer, "0");
sprintf(buffer, "entitlements.%i.terminationDate", count);
sendPacket->SetVar(buffer, "");
sprintf(buffer, "entitlements.%i.productId", count);
sendPacket->SetVar(buffer, "DR%3a192365600");
sprintf(buffer, "entitlements.%i.entitlementId", count);
sendPacket->SetVar(buffer, "4");
sprintf(buffer, "entitlements.%i.status", count);
sendPacket->SetVar(buffer, "ACTIVE");
count++;
}
sendPacket->SetVar("entitlements.[]", count);
}
else if(groupName.compare("AddsVetRank") == 0)
{
//TODO: make the AddsVetRank entitlements database
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("entitlements.0.statusReasonCode", "");
sendPacket->SetVar("entitlements.0.groupName", "AddsVetRank");
sendPacket->SetVar("entitlements.0.grantDate", "2011-07-30T0%3a11Z");
sendPacket->SetVar("entitlements.0.version", "0");
sendPacket->SetVar("entitlements.0.entitlementId", "1114495162");
sendPacket->SetVar("entitlements.0.terminationDate", "");
sendPacket->SetVar("entitlements.0.productId", "");
sendPacket->SetVar("entitlements.0.entitlementTag", "BFBC2%3aPC%3aADDSVETRANK");
sendPacket->SetVar("entitlements.0.status", "ACTIVE");
sendPacket->SetVar("entitlements.0.userId", user.id);
if(fw->emuCfg().all_players_are_veterans)
{
sendPacket->SetVar("entitlements.1.statusReasonCode", "");
sendPacket->SetVar("entitlements.1.groupName", "AddsVetRank");
sendPacket->SetVar("entitlements.1.grantDate", "2011-10-27T6%3a49Z");
sendPacket->SetVar("entitlements.1.version", "0");
sendPacket->SetVar("entitlements.1.entitlementId", "1272849755");
sendPacket->SetVar("entitlements.1.terminationDate", "");
sendPacket->SetVar("entitlements.1.productId", "OFB-EAST%3a40873");
sendPacket->SetVar("entitlements.1.entitlementTag", "BF3%3aPC%3aADDSVETRANK");
sendPacket->SetVar("entitlements.1.status", "ACTIVE");
sendPacket->SetVar("entitlements.1.userId", user.id);
sendPacket->SetVar("entitlements.[]", "2");
}
else
sendPacket->SetVar("entitlements.[]", "1");
}
else
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("entitlements.[]", "0");
}
/*else if(groupName.compare("BattlefieldBadCompany2") == 0)
{
//TODO: make the BattlefieldBadCompany2 entitlements database
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("entitlements.[]", "0");
}
else if(groupName.compare("NoVetRank") == 0)
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("entitlements.[]", "0");
}
else
{
debug->warning(1, connectionType, "Could not handle TXN=%s group=%s", txn.c_str(), groupName.c_str());
return true;
}*/
}
else if(txn.compare("NuGrantEntitlement") == 0) //is this ever called from the client?
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
}
else if(txn.compare("GetLockerURL") == 0)
{
//URL=http%3a//bfbc2.gos.ea.com/fileupload/locker2.jsp
string url = "http%3a//";
if(fw->portsCfg().use_http)
url.append(emuIp);
else
url.append("127.0.0.1");
url.append("/fileupload/locker2.jsp");
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("URL", url);
}
else if(txn.compare("NuSearchOwners") == 0) // User searches for Persona name
{
string screenName = packet->GetVar("screenName");
list_entry tempPersona;
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
if(!database->getPersona(screenName, &tempPersona))
{
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "104");
sendPacket->SetVar("localizedMessage", "\"The data necessary for this transaction was not found\"");
}
else
{
// todo: check what to do here if persona was found (above msg is correct)
/*
<-TXN=NuSearchOwners
screenName=ugahbugah
searchType=1
retrieveUserIds=0
->users.[]=1
users.0.id=838297533
TXN=NuSearchOwners
users.0.name=UgaHBugaH
nameSpaceId=battlefield
users.0.type=1*/
}
//sendPacket->SetVar("retrieveUserIds", (int)result->rowsCount());
}
else if(txn.compare("NuEntitleGame") == 0) // User wants to register himself
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
/*localizedMessage="The user is not entitled to access this game"
errorContainer.[]=0
errorCode=120*/
//todo: capture this packet (with a valid code) and implement it
}
else if(txn.compare("NuEntitleUser") == 0) // User wants to redeem a code
{
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "181");
sendPacket->SetVar("localizedMessage", "\"The code is not valid for registering this game\"");
//sendPacket->SetVar("TXN", "NuGrantEntitlement");
//todo: capture this packet (with a valid code) and implement it
}
else if(txn.compare("NuLookupUserInfo") == 0)
{
string playerPersonaName = packet->GetVar("userInfo.0.userName");
list_entry result;
sendPacket = new Packet("acct", 0x80000000);
sendPacket->SetVar("TXN", txn);
if(database->getPersona(playerPersonaName, &result))
{
sendPacket->SetVar("userInfo.[]", "1");
sendPacket->SetVar("userInfo.0.userName", playerPersonaName);
sendPacket->SetVar("userInfo.0.namespace", "battlefield");
sendPacket->SetVar("userInfo.0.userId", result.id); //persona id
sendPacket->SetVar("userInfo.0.masterUserId", result.data[USER_ID]);
}
else
{
sendPacket->SetVar("userInfo.[]", "1");
sendPacket->SetVar("userInfo.0.userName", playerPersonaName);
}
}
else
return true;
if(sendPacket)
queue->push_back(sendPacket);
return false;
}
bool GameClient::asso(list<Packet*>* queue, Packet* packet, string txn)
{
Packet* sendPacket = NULL;
if(txn.compare("AddAssociations") == 0)
{
sendPacket = new Packet("asso", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("type", packet->GetVar("type"));
sendPacket->SetVar("result.[]", "0");
sendPacket->SetVar("domainPartition.domain", "eagames");
sendPacket->SetVar("domainPartition.subDomain", "BFBC2");
sendPacket->SetVar("maxListSize", "100");
}
else if(txn.compare("GetAssociations") == 0)
{
string type = packet->GetVar("type");
sendPacket = new Packet("asso", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("type", type);
sendPacket->SetVar("domainPartition.domain", packet->GetVar("domainPartition.domain"));
sendPacket->SetVar("domainPartition.subDomain", packet->GetVar("domainPartition.subDomain"));
sendPacket->SetVar("owner.id", persona.id);
sendPacket->SetVar("owner.name", persona.name);
sendPacket->SetVar("owner.type", "1");
if(type.compare("PlasmaMute") == 0)
{
//TODO: make the mute list database
sendPacket->SetVar("maxListSize", "20");
sendPacket->SetVar("members.[]", "0");
}
else if(type.compare("PlasmaBlock") == 0)
{
//TODO: make the block list database
sendPacket->SetVar("maxListSize", "20");
sendPacket->SetVar("members.[]", "0");
}
else if(type.compare("PlasmaFriends") == 0)
{
//TODO: make the friends list database
sendPacket->SetVar("maxListSize", "20");
sendPacket->SetVar("members.[]", "0");
/*members.[]=6
members.0.id=243057189
members.0.name=Ragetank
members.0.type=1
members.0.created="Aug-03-2011 09%3a38%3a08 UTC"
members.0.modified="Aug-03-2011 09%3a38%3a08 UTC"
members.1.id=245421566
members.1.name=-MrFrost.-
members.1.type=1
members.1.created="Aug-03-2011 09%3a38%3a18 UTC"
members.1.modified="Aug-03-2011 09%3a38%3a18 UTC"
members.2.name=Mr.Mustang
members.2.id=239645767
members.2.type=1
members.2.created="Dec-22-2010 00%3a48%3a19 UTC"
members.2.modified="Dec-22-2010 00%3a48%3a19 UTC"
members.3.id=329836197
members.3.name=Drnegative16
members.3.type=1
members.3.created="Jul-30-2011 00%3a11%3a06 UTC"
members.3.modified="Jul-30-2011 00%3a11%3a06 UTC"
members.4.id=286375350
members.4.name=Gen.RoadRunner
members.4.type=1
members.4.created="Dec-27-2010 10%3a44%3a31 UTC"
members.4.modified="Dec-27-2010 10%3a44%3a32 UTC"
members.5.id=335897299
members.5.name=BaaldEagle
members.5.type=1
members.5.created="Jan-26-2012 10%3a18%3a49 UTC"
members.5.modified="Jan-26-2012 10%3a18%3a49 UTC"*/
}
else if(type.compare("PlasmaRecentPlayers") == 0)
{
//TODO: make the recent list database
sendPacket->SetVar("maxListSize", "100");
sendPacket->SetVar("members.[]", "0");
}
else
debug->warning(1, connectionType, "Could not handle TXN=%s type=%s", txn.c_str(), type.c_str());
}
else
return true;
if(sendPacket)
queue->push_back(sendPacket);
return false;
}
bool GameClient::xmsg(list<Packet*>* queue, Packet* packet, string txn)
{
Packet* sendPacket = NULL;
if(txn.compare("ModifySettings") == 0)
{
//TODO: modify settings in database
sendPacket = new Packet("xmsg", 0x80000000);
sendPacket->SetVar("TXN", txn);
}
else if(txn.compare("SendMessage") == 0) //friend request is being sent
{
sendPacket = new Packet("xmsg", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("status.0.status", "0");
sendPacket->SetVar("status.[]", "1");
sendPacket->SetVar("status.0.userid", packet->GetVar("to.0"));
sendPacket->SetVar("messageId", "1"); //need to make unique id's for the message?
/*localizedMessage="Message recipients/to invalid"
errorContainer.[]=0
TXN=SendMessage
errorCode=22006
<-TXN=SendMessage
messageType=invite/friend
to.[]=1
to.0=838297533
attachments.0.key=response
attachments.0.type=text/plain
attachments.0.data="27,1"
attachments.[]=1
expires=2592000
deliveryType=standard
purgeStrategy=all-recipients
->status.0.status=0
TXN=SendMessage
status.[]=1
status.0.userid=838297533
messageId=289421781.
*/
}
else if(txn.compare("GetMessages") == 0)
{
//are there any friend requests?
sendPacket = new Packet("xmsg", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("messages.[]", "0");
/*xmsg..."...^messages.0.from.type=1
messages.0.attachments.0.type=text/plain
messages.0.messageType=inviteresp/friend
messages.0.from.name=StonySniper
messages.0.purgeStrategy=all-recipients
messages.[]=1
messages.0.deliveryType=standard
messages.0.messageId=284890756
messages.0.timeSent="Jan-17-2013 21%3a26%3a22 UTC"
messages.0.attachments.0.key=response
messages.0.to.0.name=-%3dRick%3d-
messages.0.to.0.id=272593863
messages.0.to.[]=1
messages.0.expiration=2592000
messages.0.from.id=791406346
TXN=GetMessages
messages.0.attachments.[]=1
messages.0.to.0.type=1
messages.0.attachments.0.data=accepted*/
}
else
return true;
if(sendPacket)
queue->push_back(sendPacket);
return false;
}
bool GameClient::pres(list<Packet*>* queue, Packet* packet, string txn)
{
Packet* sendPacket = NULL;
if(txn.compare("PresenceSubscribe") == 0) //is called when client has friends - one packet request for each friend
{
sendPacket = new Packet("pres", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("responses.[]", "1");
sendPacket->SetVar("responses.0.outcome", "0");
sendPacket->SetVar("responses.0.owner.type", "0");
sendPacket->SetVar("responses.0.owner.id", packet->GetVar("requests.0.userId"));
sendPacket->SetVar("responses.0.owner.name", "player");
//(maybe multiple responses if client has multiple personas from same user as friend?)
}
else if(txn.compare("SetPresenceStatus") == 0)
{
//TODO: make the Presence Status database
sendPacket = new Packet("pres", 0x80000000);
sendPacket->SetVar("TXN", txn);
}
else
return true;
if(sendPacket)
queue->push_back(sendPacket);
return false;
}
bool GameClient::recp(list<Packet*>* queue, Packet* packet, string txn)
{
Packet* sendPacket = NULL;
if(txn.compare("GetRecord") == 0)
{
//TODO: find out what it is, and what to do with it - no difference to logged packets
// recordName=clan
sendPacket = new Packet("recp", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("localizedMessage", "\"Record not found\"");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("errorCode", "5000");
}
else if(txn.compare("GetRecordAsMap") == 0) // get all dogtags the persona possesses
{
stats_data = new Stats(user.name, persona.name, false, true);
int count = stats_data->loadDogtags();
sendPacket = new Packet("recp", 0x80000000);
sendPacket->SetVar("TXN", txn);
sendPacket->SetVar("TTL", "0");
sendPacket->SetVar("state", "1");
if(count > 0)
{
sendPacket->SetVar("values.{}", count);
sendPacket->SetVar("lastModified", "2011-03-08 09%3a43%3a35.0"); // todo: access the last modified time of the dogtag file and use it here
Stats::dogtag entry;
for(int i = 1; i <= count; i++) // loadDogtags() returns a 1 based value
{
string buffer;
entry = stats_data->getDogtag(i);
buffer = "values.{";
buffer.append(entry.id);
buffer.append("}");
sendPacket->SetVar(buffer, entry.data);
}
}
else
sendPacket->SetVar("values.{}", "0");
delete stats_data;
stats_data = NULL;
}
else if(txn.compare("AddRecord") == 0) //is called when player gets an enemy dogtag that he hasn't yet?...packet response should be fine though
{ //or maybe this adds a new record for a player that isn't in the database yet? I got this when I wanted to play with my new persona
//todo: implement dogtags
/*TXN=AddRecord
recordName=dogtags
values.[]=1
values.0.key=298916317
values.0.value=S0lMTEVSQk9XIChOTCkAABaS5kQAAAEAAAAjAA%3d%3d
TTL=0*/
sendPacket = new Packet("recp", 0x80000000);
sendPacket->SetVar("TXN", txn);
}
else if(txn.compare("UpdateRecord") == 0) //is called when a player gets an enemy dogtag but already has some of them (then why was this called during testing)? (thats at least one action that triggers this) - packet response should be fine though
{
//todo: implement dogtags
/*TXN=UpdateRecord
recordName=dogtags
values.[]=1
values.0.key=334048277
values.0.value=U3phbWFuMTg1X1BMAAAAAC+S5kQAAAEAAAAdAA%3d%3d
TTL=0
remove.[]=0*/
Stats::dogtag newDogtag;
newDogtag.id = packet->GetVar("values.0.key");
newDogtag.data = packet->GetVar("values.0.value");
stats_data = new Stats(user.name, persona.name, false, true);
stats_data->loadDogtags();
stats_data->addDogtag(newDogtag);
stats_data->saveDogtags();
delete stats_data;
stats_data = NULL;
sendPacket = new Packet("recp", 0x80000000);
sendPacket->SetVar("TXN", txn);
}
else
return true;
if(sendPacket)
queue->push_back(sendPacket);
return false;
}
bool GameClient::pnow(list<Packet*>* queue, Packet* packet, string txn)
{
Packet* sendPacket = NULL;
if(txn.compare("Start") == 0) // User wants us to select a server for him to automatically join
{
sendPacket = new Packet("pnow", 0x80000000);
sendPacket->SetVar("TXN", txn);
//int id = rand() % 5000 + 1;
string id = "1";
sendPacket->SetVar("id.id", id); // what id is this? for now put constant number
sendPacket->SetVar("id.partition", "/eagames/BFBC2");
queue->push_back(sendPacket);
sendPacket = new Packet("pnow", 0x80000000, false);
sendPacket->SetVar("TXN", "Status");
sendPacket->SetVar("id.id", id);
sendPacket->SetVar("id.partition", "/eagames/BFBC2");
sendPacket->SetVar("sessionState", "COMPLETE");
sendPacket->SetVar("props.{}", "2");
sendPacket->SetVar("props.{resultType}", "JOIN");
list<linked_key> filters;
linked_key tempFilter;
list<int> filtered;
string temp = packet->GetVar("players.0.props.{filter-gameMod}");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = GAME_MOD;
filters.push_back(tempFilter);
}
temp = packet->GetVar("players.0.props.{filter-gamemode}");
if(!temp.empty() && temp.find_first_of('|') == string::npos) // if a '|' is in the string then all game modes are allowed so we dont need to filter
{
tempFilter.key = temp;
tempFilter.id = GAMEMODE;
filters.push_back(tempFilter);
}
temp = packet->GetVar("players.0.props.{pref-level}");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = SERVER_LEVEL;
filters.push_back(tempFilter);
}
database->listMatchingGames(&filters, &filtered);
if(!filtered.empty())
{
sendPacket->SetVar("props.{games}.0.fit", "1001");
sendPacket->SetVar("props.{games}.0.gid", filtered.front());
sendPacket->SetVar("props.{games}.0.lid", database->getLobbyInfo(LOBBY_ID));
sendPacket->SetVar("props.{games}.[]", "1"); // we only filter one game out
}
else
sendPacket->SetVar("props.{games}.[]", "0");
}
else
return true;
if(sendPacket)
queue->push_back(sendPacket);
return false;
}
int GameClient::rank(list<Packet*>* queue, Packet* packet, string txn)
{
Packet* sendPacket = NULL;
int state = NORMAL;
if(txn.compare("GetStats") == 0) // get Stats for current persona
{
stats_data = new Stats(user.name, persona.name);
sendPacket = new Packet("rank", 0x80000000);
sendPacket->SetVar("TXN", txn);
string buffer = packet->GetVar("keys.[]");
int count = lexical_cast<int>(buffer);
sendPacket->SetVar("stats.[]", buffer);
char iterBuf[24];
for(int i = 0; i < count; i++)
{
sprintf(iterBuf, "keys.%i", i);
buffer = packet->GetVar(iterBuf);
string value = stats_data->getKey(buffer);
sprintf(iterBuf, "stats.%i.key", i);
sendPacket->SetVar(iterBuf, buffer);
sprintf(iterBuf, "stats.%i.value", i);
sendPacket->SetVar(iterBuf, value);
}
delete stats_data;
stats_data = NULL;
}
else if(txn.compare("UpdateStats") == 0)
{
//this is coming sometimes from the server (not on EA though?) so we have to accept only u.0.ot=1 to ignore that case
//rank 0xc000001c {TXN=UpdateStats, u.0.o=100, u.0.ot=11, u.0.s.[]=1, u.0.s.0.ut=3, u.0.s.0.k=veteran, u.0.s.0.v=1.0000, u.0.s.0.pt=0, u.[]=1}
if(packet->GetVar("u.0.ot").compare("1") == 0)// && !fw->emuCfg().override_stats_with_template)
{
int playerPersonaId = lexical_cast<int>(packet->GetVar("u.0.o"));
list_entry result;
if(database->getPersona(playerPersonaId, &result))
{
Stats* stats_data = new Stats(result.data[USER_EMAIL], result.name);
int count = lexical_cast<int>(packet->GetVar("u.0.s.[]"));
double temp_score = 0.0f;
char buffer[16];
for(int i = 0; i < count; i++)
{
sprintf(buffer, "u.0.s.%i.k", i); //is now key name
string key = packet->GetVar(buffer);
sprintf(buffer, "u.0.s.%i.ut", i);
int updateMethod = lexical_cast<int>(packet->GetVar(buffer));
sprintf(buffer, "u.0.s.%i.v", i); //is now value of key
string value = packet->GetVar(buffer);
string old = stats_data->getKey(key);
if(updateMethod == 3 && !old.empty() && !value.empty()) //1 = absolute value, 3 = relative value, 0 = absolute value (should round it)
{
double old_value = atof(old.c_str());
double new_value = atof(value.c_str());
if(key.compare("sc_assault") == 0 || key.compare("sc_award") == 0 || key.compare("sc_demo") == 0 || key.compare("sc_recon") == 0 || key.compare("sc_support") == 0 || key.compare("sc_vehicle") == 0)
{
temp_score += new_value;
//debug->notification(3, connectionType, "Score relevant key \"%s\", value=%.4f, temp_score=%.4f", key.c_str(), new_value, temp_score);
}
new_value += old_value;
sprintf(buffer, "%.4f", new_value);
value = buffer;
}
strcpy(buffer, old.c_str());
debug->notification(3, connectionType, "Updated key %s with value \"%s\", old value=\"%s\", updateMethod=%i", key.c_str(), value.c_str(), buffer, updateMethod);
stats_data->setKey(key, value, true);
// normally only webstats related keys are not found
/*if(!stats_data->setKey(key, value, true)) // store which keys were not found
{
fstream file("./templates/ignoredKeys", fstream::in | fstream::out);
if(file.is_open() && file.good())
{
stringstream strmbuf;
strmbuf << file.rdbuf();
string unknownKeys = strmbuf.str();
key.insert(0, "\"");
key.append("\"\n");
int result = unknownKeys.find(key, 0);
if(result == string::npos)
file.write(key.c_str(), key.size());
file.close();
key.pop_back(); //we dont want the newline at the end to be printed
//key.erase(key.size()-1, 1);
if(result != string::npos)
debug->notification(4, connectionType, "Key %s is already in ignoredKeys file", key.c_str());
else
debug->notification(3, connectionType, "Added key %s to ignoredKeys file", key.c_str());
}
}*/
}
if(temp_score != 0.0f)
{
double score = atof(stats_data->getKey("score").c_str());
score += temp_score;
sprintf(buffer, "%.4f", score);
stats_data->setKey("score", buffer);
}
stats_data->saveStats();
delete stats_data;
}
else
debug->warning(1, connectionType, "Couldn't find persona id %i in database!!", playerPersonaId);
}
sendPacket = new Packet("rank", 0x80000000);
sendPacket->SetVar("TXN", txn);
}
else if(txn.compare("GetRankedStats") == 0) // User wants to see worldwide Stats
{
sendPacket = new Packet("rank", 0x80000000);
sendPacket->SetVar("TXN", txn);
// not sure why this is being sent but it's like this in the logged traffic
sendPacket->SetVar("errorCode", "99");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("localizedMessage", "\"System Error\"");
/*
<-TXN=GetRankedStats
owner=230518387
ownerType=1
periodId=0
periodPast=0
keys.0=score
rankOrders.0=0
keys.1=rank
rankOrders.1=0
keys.2=form
rankOrders.2=0
keys.3=level
rankOrders.3=0
keys.4=kills
rankOrders.4=0
keys.5=deaths
rankOrders.5=0
keys.6=sc_squad
rankOrders.6=0
keys.7=time
rankOrders.7=0
keys.8=veteran
rankOrders.8=0
keys.9=elo
rankOrders.9=0
keys.10=score
rankOrders.10=0
keys.[]=11
rankOrders.[]=11
->stats.[]=9
stats.3.text=20100316
stats.1.value=28.0
stats.0.value=851985.0
stats.5.value=2993.0
stats.7.value=372294.71
stats.4.key=kills
stats.5.key=deaths
stats.2.rank=220437
stats.8.key=score
stats.1.rank=250001
stats.6.key=sc_squad
stats.0.key=score
stats.1.key=rank
stats.4.rank=250001
stats.2.value=1.0
stats.6.rank=250001
stats.7.key=time
stats.0.rank=250001
stats.5.rank=250001
stats.6.value=42955.0
stats.8.rank=250001
stats.3.rank=187362
stats.8.value=851985.0
TXN=GetRankedStats
stats.2.key=form
stats.4.value=3088.0
stats.7.rank=250001
stats.3.value=145.0
stats.3.key=level*/
}
else if(txn.compare("GetRankedStatsForOwners") == 0) // User wants to see himself on worldwide stats?
{
sendPacket = new Packet("rank", 0x80000000);
sendPacket->SetVar("TXN", txn);
// not sure why this is being sent but it's like this in the logged traffic
sendPacket->SetVar("errorCode", "99");
sendPacket->SetVar("errorContainer.[]", "0");
sendPacket->SetVar("localizedMessage", "\"System Error\"");
/*
<-TXN=GetRankedStatsForOwners
owners.[]=1
owners.0.ownerId=230518387
owners.0.ownerType=1
periodId=0
keys.0=score
keys.1=rank
keys.2=form
keys.3=level
keys.4=kills
keys.5=deaths
keys.6=sc_squad
keys.7=time
keys.8=veteran
keys.9=elo
keys.10=" score"
keys.[]=11
->rankedStats.0.ownerType=1
rankedStats.0.rankedStats.5.value=2993.0
rankedStats.[]=1
rankedStats.0.rankedStats.3.text=20100316
rankedStats.0.rankedStats.6.rank=250001
rankedStats.0.rankedStats.3.value=145.0
rankedStats.0.rankedStats.6.key=sc_squad
rankedStats.0.rankedStats.5.key=deaths
rankedStats.0.rankedStats.7.value=372294.71
rankedStats.0.rankedStats.3.rank=187362
rankedStats.0.rankedStats.2.rank=220437
rankedStats.0.rankedStats.2.key=form
rankedStats.0.rankedStats.1.rank=250001
rankedStats.0.rankedStats.0.value=851985.0
rankedStats.0.ownerId=230518387
rankedStats.0.rankedStats.4.value=3088.0
rankedStats.0.rankedStats.4.key=kills
rankedStats.0.rankedStats.4.rank=250001
rankedStats.0.rankedStats.3.key=level
rankedStats.0.rankedStats.[]=8
rankedStats.0.rankedStats.0.rank=250001
rankedStats.0.rankedStats.0.key=score
rankedStats.0.rankedStats.1.key=rank
rankedStats.0.rankedStats.5.rank=250001
rankedStats.0.rankedStats.6.value=42955.0
rankedStats.0.rankedStats.2.value=1.0
rankedStats.0.rankedStats.7.rank=250001
TXN=GetRankedStatsForOwners
rankedStats.0.rankedStats.7.key=time
rankedStats.0.rankedStats.1.value=28.0*/
}
else if(txn.compare("GetTopNAndStats") == 0)
{
sendPacket = new Packet("rank", 0x80000000);
sendPacket->SetVar("TXN", txn);
/*
<-TXN=GetTopNAndStats
key=score
ownerType=1
minRank=250001
maxRank=250100
periodId=0
periodPast=0
rankOrder=0
keys.0=score
keys.1=rank
keys.2=form
keys.3=level
keys.4=kills
keys.5=deaths
keys.6=sc_squad
keys.7=time
keys.8=veteran
keys.9=elo
keys.[]=10
->stats.[]=0
TXN=GetTopNAndStats
=================
<-TXN=GetTopNAndStats
key=score
ownerType=1
minRank=1
maxRank=100
periodId=0
periodPast=0
rankOrder=0
keys.0=score
keys.1=rank
keys.2=form
keys.3=level
keys.4=kills
keys.5=deaths
keys.6=sc_squad
keys.7=time
keys.8=veteran
keys.9=elo
keys.[]=10
->stats.26.addStats.6.key=sc_squad
stats.1.addStats.8.value=7.0
stats.73.addStats.5.key=deaths
stats.50.addStats.7.value=8821996.97
stats.50.addStats.8.key=veteran
stats.8.addStats.3.value=0.0
stats.59.addStats.0.key=score
stats.49.addStats.3.text=20100315
stats.83.addStats.1.key=rank
stats.61.addStats.3.value=0.0
stats.97.addStats.2.key=form
stats.0.addStats.[]=10
stats.85.addStats.4.key=kills
stats.1.addStats.0.value=6.8625345E7
stats.5.addStats.4.key=kills
stats.52.name=leytenant
...
*/
}
else
state = UNKNOWN;
if(sendPacket)
queue->push_back(sendPacket);
return state;
}
int GameClient::ProcessTheater(list<Packet*>* queue, Packet* packet, char* type, const char* ip)
{
Packet* sendPacket = NULL;
int state = NORMAL;
if(strcmp(type, "CONN") == 0)
{
sendPacket = new Packet("CONN", 0x00000000);
sendPacket->SetVar("TID", ++sock_tid);
sendPacket->SetVar("TIME", (int)time(NULL));
sendPacket->SetVar("activityTimeoutSecs", "240");
sendPacket->SetVar("PROT", packet->GetVar("PROT"));
}
else if(strcmp(type, "PING") == 0)
{
state = SKIP;
}
else if(strcmp(type, "USER") == 0)
{
persona_lkey = packet->GetVar("LKEY");
int id = database->theaterLogin(persona_lkey);
if(id > 0)
{
database->getPersona(id, &persona);
sendPacket = new Packet("USER", 0x00000000);
sendPacket->SetVar("TID", ++sock_tid);
sendPacket->SetVar("NAME", persona.name);
}
else
state = SKIP;
}
else if(strcmp(type, "LLST") == 0) //Lobby List
{
sendPacket = new Packet("LLST", 0x00000000);
sendPacket->SetVar("TID", ++sock_tid);
sendPacket->SetVar("NUM-LOBBIES", "1");
queue->push_back(sendPacket);
sendPacket = new Packet("LDAT", 0x00000000);
sendPacket->SetVar("TID", sock_tid);
sendPacket->SetVar("LID", database->getLobbyInfo(LOBBY_ID));
sendPacket->SetVar("PASSING", database->getLobbyGames());
sendPacket->SetVar("NAME", database->getLobbyInfo(LOBBY_NAME));
sendPacket->SetVar("LOCALE", database->getLobbyInfo(LOBBY_LOCALE));
sendPacket->SetVar("MAX-GAMES", database->getLobbyInfo(LOBBY_MAX_GAMES));
sendPacket->SetVar("FAVORITE-GAMES", "0"); //TODO: find out how it works
sendPacket->SetVar("FAVORITE-PLAYERS", "0"); //TODO: find out how it works
sendPacket->SetVar("NUM-GAMES", database->getLobbyGames());
}
else if(strcmp(type, "GLST") == 0) //Game List
{
list<int> filtered;
int limit = lexical_cast<int>(packet->GetVar("COUNT"));
if(limit < 0)
limit = 500;
string gid = packet->GetVar("GID");
if(!gid.empty()) // client is joining a specific server, get information about it
{
// there were filters in here before but why should we need them if there is an id?
/*int tempId = lexical_cast<int>(gid);
if(database->isValidGid(tempId))
filtered.push_back(tempId);*/
// actually I'm not sure if we should do anything here, normally the given gid is invalid or not part of the wanted games
}
else
{
if(fw->emuCfg().enable_server_filters)
filterGames(packet, &filtered);
else
database->listAllGames(&filtered);
}
//todo: make filters
sendPacket = new Packet("GLST", 0x00000000);
sendPacket->SetVar("TID", ++sock_tid);
sendPacket->SetVar("LID", packet->GetVar("LID"));
sendPacket->SetVar("LOBBY-NUM-GAMES", database->getLobbyGames());
sendPacket->SetVar("LOBBY-MAX-GAMES", database->getLobbyInfo(LOBBY_MAX_GAMES));
sendPacket->SetVar("NUM-GAMES", (int)filtered.size());
while(!filtered.empty() && limit > 0)
{
queue->push_back(sendPacket);
int id = filtered.front();
string* game = database->getGameData(filtered.front(), false);
sendPacket = new Packet("GDAT", 0x00000000);
sendPacket->SetVar("TID", sock_tid);
sendPacket->SetVar("LID", database->getLobbyInfo(LOBBY_ID)); // id of lobby
sendPacket->SetVar("GID", id); // id of game/server
string persona_name, persona_id;
if(game[PLATFORM].compare("PC") == 0) // I know these checks are consistent but it's better than nothing
{
persona_name = "bfbc2.server.p";
persona_id = "1";
}
else if(game[PLATFORM].compare("ps3") == 0)
{
persona_name = "bfbc.server.ps";
persona_id = "2";
}
else
{
persona_name = "bfbc.server.xe";
persona_id = "3";
}
sendPacket->SetVar("HN", persona_name); // account name of server (host name)
sendPacket->SetVar("HU", persona_id); // account id of server (host user)
sendPacket->SetVar("N", game[SERVER_NAME]); // name of server in list
if(!fw->isEmuLocal()) // a public IP is in the config so we have to determine in what network the server is
{
string serverIp = game[IP];
if(!isLocal && serverIp.compare(ip) != 0) // client is outside and remote server ip is not equal to the client ip
sendPacket->SetVar("I", serverIp);
else if(!isLocal && serverIp.compare(ip) == 0) // client is outside but server has the same ip as the client so they must be in the same network
sendPacket->SetVar("I", game[INTERNAL_IP]);
else if(isLocal && serverIp.compare(fw->portsCfg().emulator_ip) == 0) // client is local but server has same public ip as emulator so they must be in the same network
sendPacket->SetVar("I", game[INTERNAL_IP]);
else // client is local so check in what network the server is
{
if(!fw->isIpLocal(serverIp)) // server is in another network so send public ip to client
sendPacket->SetVar("I", serverIp); // IP (probably outside server ip)
else // everyone is local
sendPacket->SetVar("I", game[INTERNAL_IP]); // we want this if the server is in the same network as the client
}
}
else
sendPacket->SetVar("I", game[IP]); // IP (probably outside server ip)
sendPacket->SetVar("P", game[PORT]); // Port
sendPacket->SetVar("JP", game[JOINING_PLAYERS]);// players that are joining the server right now?
sendPacket->SetVar("QP", game[QUEUE_LENGTH]); // something with the queue...lets just set this equal to B-U-QueueLength
sendPacket->SetVar("AP", game[ACTIVE_PLAYERS]); // current number of players on server
sendPacket->SetVar("MP", game[MAX_PLAYERS]); // Maximum players on server
sendPacket->SetVar("PL", game[PLATFORM]); // Platform - PC / XENON / PS3
// constants (at least I never saw any other values in the logged traffic)
sendPacket->SetVar("F", "0"); // ???
sendPacket->SetVar("NF", "0"); // ???
sendPacket->SetVar("J", game[JOIN]); // ??? constant value - "O"
sendPacket->SetVar("TYPE", game[TYPE]); // what type?? constant value - "G"
sendPacket->SetVar("PW", "0"); // ??? - its certainly not something like "hasPassword"
// other server specific values
sendPacket->SetVar("B-U-Softcore", game[SOFTCORE]); // Game is softcore - what does that mean?
sendPacket->SetVar("B-U-Hardcore", game[HARDCORE]); // Game is hardcore
sendPacket->SetVar("B-U-HasPassword", game[HAS_PASSWORD]); // Game has password
sendPacket->SetVar("B-U-Punkbuster", game[PUNKBUSTER]); // Game has punkbuster?
sendPacket->SetVar("B-U-EA", game[EA]); // is server EA Orginal?
sendPacket->SetVar("B-version", game[VERSION]); // Version of the server (exact version) - TRY TO CONNECT TO ACTUAL VERSION OF SERVER
sendPacket->SetVar("V", game[CLIENT_VERSION]); // "clientVersion" of server (shows up in server log on startup)
sendPacket->SetVar("B-U-level", game[SERVER_LEVEL]);// current map of server
sendPacket->SetVar("B-U-gamemode", game[GAMEMODE]); // Gameplay Mode (Conquest, Rush, SQDM, etc)
sendPacket->SetVar("B-U-sguid", game[SGUID]); // Game PB Server GUID?
sendPacket->SetVar("B-U-Time", game[TIME]); // uptime of server?
sendPacket->SetVar("B-U-hash", game[HASH]); // Game hash?
sendPacket->SetVar("B-U-region", game[REGION]); // Game region
sendPacket->SetVar("B-U-public", game[IS_PUBLIC]); // Game is public
sendPacket->SetVar("B-U-elo", game[ELO]); // value that determines how good the players on the server are?
// also constant but could be changed? (is not implemented in the game anyway)
sendPacket->SetVar("B-numObservers", game[NUM_OBSERVERS]); // observers = spectators? or admins?
sendPacket->SetVar("B-maxObservers", game[MAX_OBSERVERS]); // Game max observers
sendPacket->SetVar("B-U-Provider", game[PROVIDER]); // provider id, figured out by server
sendPacket->SetVar("B-U-gameMod", game[GAME_MOD]); // maybe different value for vietnam here?
sendPacket->SetVar("B-U-QueueLength", game[QUEUE_LENGTH]); // players in queue or maximum queue length? (sometimes smaller than QP (-1?))
if(game[PUNKBUSTER].compare("1") == 0)
sendPacket->SetVar("B-U-PunkBusterVersion", game[PUNKBUSTER_VERSION]);
filtered.pop_front();
limit--;
}
}
else if(strcmp(type, "GDAT") == 0)
{
string lobbyID = packet->GetVar("LID");
string gameID = packet->GetVar("GID");
if(lobbyID.empty() || gameID.empty())
{
sendPacket = new Packet("GDAT", 0x00000000);
debug->warning(3, connectionType, "GDAT - GID or LID is null");
sendPacket->SetVar("TID", ++sock_tid);
}
else
{
GamesEntry server;
int gid = lexical_cast<int>(gameID);
server.key = database->getGameData(gid, false);
server.gdet = database->getGameData(gid, true);
if(server.key && server.gdet)
{
// TODO: implement queues (properly)
if(queue_position > 0)
{
int cur_queue_length = lexical_cast<int>(server.key[QUEUE_LENGTH]);
debug->notification(3, connectionType, "Client is still in queue, prev_queue_length=%i, cur_queue_length=%i, queue_position=%i", original_queue_length, cur_queue_length, queue_position);
if(original_queue_length != cur_queue_length)
{
if(cur_queue_length < original_queue_length)
queue_position -= original_queue_length - cur_queue_length;
else
debug->notification(3, connectionType, "Someone joined the queue, don't update pos");
original_queue_length = cur_queue_length;
debug->notification(3, connectionType, "Queue length has changed, queue_position=%i", original_queue_length, queue_position);
}
// if queue_length is smaller than queue_pos then that means a player got it (we were at the end of the queue previously right?
// why was QPOS=0 and QLEN=1 when I tried it?
sendPacket = new Packet("QLEN", 0x00000000);
sendPacket->SetVar("QPOS", queue_position-1); // is this one off??
sendPacket->SetVar("QLEN", original_queue_length);
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
queue->push_back(sendPacket);
}
sendPacket = new Packet("GDAT", 0x00000000);// interesting...no 'F' or 'NF' required here or 'B-U-PunkBusterVersion'
sendPacket->SetVar("TID", ++sock_tid);
sendPacket->SetVar("LID", lobbyID); // is the LID always in this specific packet? if not take the LID from the database entry
sendPacket->SetVar("GID", gameID);
string persona_name, persona_id;
if(server.key[PLATFORM].compare("PC") == 0) // I know this check is anything but consistent but it's better than nothing
{
persona_name = "bfbc2.server.p";
persona_id = "1";
}
else if(server.key[PLATFORM].compare("ps3") == 0)
{
persona_name = "bfbc.server.ps";
persona_id = "2";
}
else
{
persona_name = "bfbc.server.xe";
persona_id = "3";
}
sendPacket->SetVar("HU", persona_id); // account id of server (host user)
sendPacket->SetVar("HN", persona_name); // account name of server (host name)
if(!fw->isEmuLocal()) // a public IP is in the config so we have to determine in what network the server is
{
string serverIp = server.key[IP];
if(!isLocal && serverIp.compare(ip) != 0) // client is outside and remote server ip is not equal to the client ip
sendPacket->SetVar("I", serverIp);
else if(!isLocal && serverIp.compare(ip) == 0) // client is outside but server has the same ip as the client so they must be in the same network
sendPacket->SetVar("I", server.key[INTERNAL_IP]); // we want this if the server is in the same network as the client
else if(isLocal && serverIp.compare(fw->portsCfg().emulator_ip) == 0) // client is local but server has same public ip as the emulator so they must be in the same network
sendPacket->SetVar("I", server.key[INTERNAL_IP]);
else // client is local so check in what network the server is
{
if(!fw->isIpLocal(serverIp)) // server is in another network so send public ip to client
sendPacket->SetVar("I", serverIp); // IP (probably outside server ip)
else // everyone is local
sendPacket->SetVar("I", server.key[INTERNAL_IP]); // we want this if the server is in the same network as the client
}
}
else
sendPacket->SetVar("I", server.key[IP]); // IP (probably outside server ip)
sendPacket->SetVar("P", server.key[PORT]); // server Port
sendPacket->SetVar("N", server.key[SERVER_NAME]); // name of server in list
sendPacket->SetVar("AP", server.key[ACTIVE_PLAYERS]); // current number of players on server(Active players)
sendPacket->SetVar("MP", server.key[MAX_PLAYERS]); // Maximum players on server
sendPacket->SetVar("QP", server.key[QUEUE_LENGTH]); // something with the queue...lets just set this equal to B-U-QueueLength
sendPacket->SetVar("JP", server.key[JOINING_PLAYERS]); // players that are joining the server right now?
sendPacket->SetVar("PL", server.key[PLATFORM]); // Platform - PC / XENON / PS3
// constant:
sendPacket->SetVar("PW", "0"); // ???
sendPacket->SetVar("TYPE", server.key[TYPE]); // always "G"?
sendPacket->SetVar("J", server.key[JOIN]); // always "O"?
// Userdata
sendPacket->SetVar("B-U-Softcore", server.key[SOFTCORE]); // Game is softcore - what does that mean?
sendPacket->SetVar("B-U-Hardcore", server.key[HARDCORE]); // Game is hardcore
sendPacket->SetVar("B-U-HasPassword", server.key[HAS_PASSWORD]);// Game has password
sendPacket->SetVar("B-U-Punkbuster", server.key[PUNKBUSTER]); // Game has punkbuster?
sendPacket->SetVar("B-U-EA", server.key[EA]); // is server EA Orginal
sendPacket->SetVar("B-version", server.key[VERSION]); // Version of the server (exact version) - TRY TO CONNECT TO ACTUAL VERSION OF SERVER
sendPacket->SetVar("V", server.key[CLIENT_VERSION]); // "clientVersion" of server (shows up in server log)
sendPacket->SetVar("B-U-level", server.key[SERVER_LEVEL]); // current map of server
sendPacket->SetVar("B-U-gamemode", server.key[GAMEMODE]); // Gameplay Mode (Conquest, Rush, SQDM, etc)
sendPacket->SetVar("B-U-sguid", server.key[SGUID]); // Game PB Server GUID?
sendPacket->SetVar("B-U-Time", server.key[TIME]); // uptime of server?
sendPacket->SetVar("B-U-hash", server.key[HASH]); // Game hash?
sendPacket->SetVar("B-U-region", server.key[REGION]); // Game region
sendPacket->SetVar("B-U-public", server.key[IS_PUBLIC]); // Game is public
sendPacket->SetVar("B-U-elo", server.key[ELO]); // value that determines how good the players on the server are?
sendPacket->SetVar("B-numObservers", server.key[NUM_OBSERVERS]); // observers = spectators? or admins?
sendPacket->SetVar("B-maxObservers", server.key[MAX_OBSERVERS]); // Game max observers
sendPacket->SetVar("B-U-Provider", server.key[PROVIDER]); // provider id, figured out by server
sendPacket->SetVar("B-U-gameMod", server.key[GAME_MOD]); // maybe different value for vietnam here?
sendPacket->SetVar("B-U-QueueLength", server.key[QUEUE_LENGTH]); // players in queue or maximum queue length? (sometimes smaller than QP (-1?))
queue->push_back(sendPacket);
sendPacket = new Packet("GDET", 0x00000000);
sendPacket->SetVar("TID", sock_tid);
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
sendPacket->SetVar("D-AutoBalance", server.gdet[AUTOBALANCE]);
sendPacket->SetVar("D-Crosshair", server.gdet[CROSSHAIR]);
sendPacket->SetVar("D-FriendlyFire", server.gdet[FRIENDLY_FIRE]);
sendPacket->SetVar("D-KillCam", server.gdet[KILLCAM]);
sendPacket->SetVar("D-Minimap", server.gdet[MINIMAP]);
sendPacket->SetVar("D-MinimapSpotting", server.gdet[MINIMAP_SPOTTING]);
sendPacket->SetVar("UGID", server.key[UGID]);
string serverDescription = server.gdet[SERVER_DESCRIPTION];
int count = 0;
if(!serverDescription.empty())
{
for(int i = 0, pos = 0; pos < (int)serverDescription.size(); i++, pos += 63) // 63 is max limit of one description "cell"?
{
char buffer[24];
sprintf(buffer, "D-ServerDescription%i", i);
int length = 63;
if(pos+length > (int)serverDescription.size())
length = serverDescription.size()-pos;
string splittedDescr = serverDescription.substr(pos, length);
sendPacket->SetVar(buffer, splittedDescr);
count++;
}
}
sendPacket->SetVar("D-ServerDescriptionCount", count);
if(!server.gdet[BANNERURL].empty())
sendPacket->SetVar("D-BannerUrl", server.gdet[BANNERURL]);
sendPacket->SetVar("D-ThirdPersonVehicleCameras", server.gdet[THIRD_PERSON_VEHICLE_CAMERAS]);
sendPacket->SetVar("D-ThreeDSpotting", server.gdet[THREE_D_SPOTTING]);
for(int i = 0; i < 32; i++) //get all the player names that are on the server so we can get their id's
{
string entry = server.gdet[PDAT00+i];
if(entry.compare("|0|0|0|0") != 0) //when the column contains a different value than the default then we have a player
{
string name = entry;
int end = name.find_first_of('|');
name.erase(end, name.size()-end);
debug->notification(4, connectionType, "player name on server: %s", name.c_str());
playerList.push_back(name);
}
char buffer[12];
if(i < 10)
sprintf(buffer, "D-pdat0%i", i);
else
sprintf(buffer, "D-pdat%i", i);
sendPacket->SetVar(buffer, entry);
}
queue->push_back(sendPacket);
while(!playerList.empty()) //gather all the information of the players that are already on the server and put them into packets
{
list_entry tempPersona;
string name = playerList.front();
if(database->getPersona(name, &tempPersona))
{
sendPacket = new Packet("PDAT", 0x00000000);
sendPacket->SetVar("NAME", name);
sendPacket->SetVar("TID", sock_tid);
if(tempPersona.data)
sendPacket->SetVar("PID", tempPersona.data[PLAYER_ID]); //id of the player on the server
else
debug->warning(2, connectionType, "tempPersona with id %i contains no data!!", tempPersona.id);
sendPacket->SetVar("UID", tempPersona.id); //this is definitely persona_id
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
queue->push_back(sendPacket);
}
else
{
debug->warning(2, connectionType, "GDAT no persona with name \"%s\" found!", name.c_str());
break;
}
playerList.pop_front();
}
sendPacket = NULL;
}
else // invalid GID
{
sendPacket = new Packet("GDAT", 0x00000000);
sendPacket->SetVar("TID", ++sock_tid);
}
}
}
else if(strcmp(type, "EGAM") == 0)
{
debug->notification(4, connectionType, "Client requesting to join server");
string lobbyID = packet->GetVar("LID");
string gameID = packet->GetVar("GID");
int gid = lexical_cast<int>(gameID);
bool server_is_full = false;
string* game = database->getGameData(gid, false);
if(game)
{
bool isServerLocal = false;
int temp_players = lexical_cast<int>(game[ACTIVE_PLAYERS]);
int max_players = lexical_cast<int>(game[MAX_PLAYERS]);
if(temp_players+1 > max_players)
{
temp_players = lexical_cast<int>(game[QUEUE_LENGTH])+1;
game[QUEUE_LENGTH] = lexical_cast<string>(temp_players);
server_is_full = true;
}
if(!server_is_full)
{
sendPacket = new Packet("EGAM", 0x00000000);
sendPacket->SetVar("TID", ++sock_tid);
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
}
else
{
original_queue_length = queue_position = temp_players; // if we get here, temp_players already contains the queue_length value of the database
debug->notification(3, connectionType, "Client got queued, queue_length=%i, queue_position=%i", original_queue_length, queue_position);
sendPacket = new Packet("EGAM", 0x71756575); // send EGAMqueu if server is full, header: 0x71756575
sendPacket->SetVar("TID", ++sock_tid);
// a new player is automatically at the end of a queue, right? but why was QPOS=1 and QLEN=2 when I tried it? simply put +/-1 for testing purposes
sendPacket->SetVar("QPOS", queue_position); // position of client in queue?
sendPacket->SetVar("QLEN", queue_position+1); // the whole length of the queue (probably) or how much players are currently in the queue?
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
}
queue->push_back(sendPacket);
// put a mutex here?
TcpConnection* server_socket = fw->getServerSocket(gid);
persona.data[PLAYER_ID] = lexical_cast<string>(server_socket->getGameServer()->addPlayerId()); // player_id is a unique id of the player on the server (just a count of all players that connected)
string ticket = fw->randomString(10, SEED_NUMBERS);
if(server_socket)
{
// this packet gets sent to the SERVER the client connects to, it contains information about the client
if(!server_is_full)
{
sendPacket = new Packet("EGRQ", 0x00000000);
sendPacket->SetVar("R-INT-PORT", packet->GetVar("R-INT-PORT"));
sendPacket->SetVar("R-INT-IP", packet->GetVar("R-INT-IP")); // internal ip where the CLIENT is hosted
sendPacket->SetVar("PORT", packet->GetVar("PORT"));
sendPacket->SetVar("NAME", persona.name);
sendPacket->SetVar("PTYPE", packet->GetVar("PTYPE"));
sendPacket->SetVar("TICKET", ticket);
sendPacket->SetVar("PID", persona.data[PLAYER_ID]);
sendPacket->SetVar("UID", persona.id); // this has to be persona_id
if(!fw->isEmuLocal()) // a public IP is in the config so we have to determine in what network the server is
{
string serverIp = game[IP];
if(!isLocal && serverIp.compare(ip) != 0) // client is outside and remote server ip is not equal to the client ip
sendPacket->SetVar("IP", ip);
else if(!isLocal && serverIp.compare(ip) == 0) // client is outside but server has the same ip as the client so they must be in the same network
sendPacket->SetVar("IP", packet->GetVar("R-INT-IP"));
else if(isLocal && serverIp.compare(fw->portsCfg().emulator_ip) == 0) // client is local and server has the same public ip as the emulator so they must be in the same network
{
isServerLocal = true;
sendPacket->SetVar("IP", packet->GetVar("R-INT-IP"));
}
else // client is local so check in what network the server is
{
isServerLocal = fw->isIpLocal(serverIp);
if(!isServerLocal) // server is in another network so send ip from the emulator config
sendPacket->SetVar("IP", emuIp);
else // everyone is local
sendPacket->SetVar("IP", ip);
}
}
else
{
isServerLocal = true;
sendPacket->SetVar("IP", ip);
}
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
}
else //<-A QENT {R-INT-PORT=10000, R-INT-IP=192.168.178.29, NAME=Brueckel, PID=27, UID=812008934, LID=257, GID=77635}
{
sendPacket = new Packet("QENT", 0x00000000);
sendPacket->SetVar("R-INT-PORT", packet->GetVar("R-INT-PORT"));
sendPacket->SetVar("R-INT-IP", packet->GetVar("R-INT-IP")); // internal ip where the CLIENT is hosted
sendPacket->SetVar("NAME", persona.name);
sendPacket->SetVar("PID", persona.data[PLAYER_ID]);
sendPacket->SetVar("UID", persona.id); // this has to be persona_id
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
}
server_socket->handle_invoke(sendPacket);
server_socket = NULL;
}
string server_pid;
if(game[PLATFORM].compare("PC") == 0) // I know this check is anything but consistent but it's better than nothing
server_pid = "1";
else if(game[PLATFORM].compare("ps3") == 0)
server_pid = "2";
else
server_pid = "3";
sendPacket = new Packet("EGEG", 0x00000000);
sendPacket->SetVar("PL", "pc");
sendPacket->SetVar("TICKET", ticket);
sendPacket->SetVar("PID", persona.data[PLAYER_ID]);
if(!fw->isEmuLocal()) // a public IP is in the config so we have to determine in what network the server is
{
string serverIp = game[IP];
if(!isLocal && serverIp.compare(ip) != 0) // client is outside and remote server ip is not equal to the client ip
sendPacket->SetVar("I", serverIp);
else if(!isLocal && serverIp.compare(ip) == 0) // client is outside but server has the same ip as the client so they must be in the same network
sendPacket->SetVar("I", game[INTERNAL_IP]);
else // client is local so check in what network the server is
{
if(!isServerLocal) // server is in another network so send public ip
sendPacket->SetVar("I", serverIp);
else // everyone is local
sendPacket->SetVar("I", game[INTERNAL_IP]);
}
}
else
sendPacket->SetVar("I", game[IP]);
sendPacket->SetVar("P", game[PORT]);
sendPacket->SetVar("HUID", server_pid); // server persona id
sendPacket->SetVar("INT-PORT", game[INTERNAL_PORT]);
sendPacket->SetVar("EKEY", "AIBSgPFqRDg0TfdXW1zUGa4%3d"); // this must be the same key as the one we have on the server? keep it constant in both connections for now (we could integrate it in the database...)
sendPacket->SetVar("INT-IP", game[INTERNAL_IP]); // internal ip where the SERVER is hosted
sendPacket->SetVar("UGID", game[UGID]);
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
if(queue_position > 0)
sendPacket->isDelayed(true, 40);
}
else
debug->warning(2, connectionType, "GID %s was not found in the database", gameID.c_str());
}
else if(strcmp(type, "ECNL") == 0)
{
string gameID = packet->GetVar("GID");
string lobbyID = packet->GetVar("LID");
if(queue_position > 0)
{
debug->notification(3, connectionType, "Client is leaving queue, updating queue_length = %s", persona.data[PLAYER_ID].c_str()); // why did I use player id here?
string* game = database->getGameData(lexical_cast<int>(gameID), false);
if(!game)
debug->warning(1, connectionType, "EGAM - The requested GID %i was not found in the database, ignoring packet.", gameID.c_str());
else
{
int queue_length = lexical_cast<int>(game[QUEUE_LENGTH]);
if(queue_length > 0)
game[QUEUE_LENGTH] = lexical_cast<string>(queue_length-1);
// why do we need to send another packet to the server here?
TcpConnection* server_socket = fw->getServerSocket(lexical_cast<int>(gameID));
if(server_socket)
{
sendPacket = new Packet("QLVT", 0x00000000);
sendPacket->SetVar("PID", persona.data[PLAYER_ID]);
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
server_socket->handle_invoke(sendPacket);
server_socket = NULL;
}
}
queue_position = 0;
persona.data[PLAYER_ID] = "0";
}
sendPacket = new Packet("ECNL", 0x00000000); // when is this ECNLmisc?? in case we want to send that, this is the type: 0x6D697363
sendPacket->SetVar("TID", ++sock_tid);
sendPacket->SetVar("LID", lobbyID);
sendPacket->SetVar("GID", gameID);
}
else
state = UNKNOWN;
if(sendPacket)
queue->push_back(sendPacket);
return state;
}
void GameClient::filterGames(Packet* packet, list<int>* filtered)
{
/*
FILTER-ATTR-U-gameMod - GAME_MOD
FILTER-MIN-SIZE - ACTIVE_PLAYERS // just check if filter is present, then filter games with active_players > 0
FILTER-NOT-FULL - MAX_PLAYERS // just check if filter is present, then filter games where active_players < max_players
FILTER-ATTR-U-EA - EA
FILTER-ATTR-U-HasPassword - HAS_PASSWORD
FILTER-ATTR-U-Punkbuster - PUNKBUSTER
FILTER-ATTR-U-Softcore - SOFTCORE
FILTER-ATTR-U-gamemode - GAMEMODE
FILTER-ATTR-U-level - SERVER_LEVEL
FILTER-ATTR-U-region - REGION
FILTER-ATTR-U-public - IS_PUBLIC
FAV-GAME - SERVER_NAME // multiple choices, seperated with ";"
FAV-GAME-UID - UGID // multiple choices, seperated with ";"
*/
list<linked_key> filters;
linked_key tempFilter;
string temp = packet->GetVar("FILTER-ATTR-U-gameMod");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = GAME_MOD;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-MIN-SIZE");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = ACTIVE_PLAYERS;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-NOT-FULL");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = MAX_PLAYERS;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-ATTR-U-EA");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = EA;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-ATTR-U-HasPassword");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = HAS_PASSWORD;
filters.push_back(tempFilter);
}
//ignore punkbuster filters since it should be always deactivated anyway
/*temp = packet->GetVar("FILTER-ATTR-U-Punkbuster");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = PUNKBUSTER;
filters.push_back(tempFilter);
}*/
temp = packet->GetVar("FILTER-ATTR-U-Softcore");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = SOFTCORE;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-ATTR-U-gamemode");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = GAMEMODE;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-ATTR-U-level");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = SERVER_LEVEL;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-ATTR-U-region");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = REGION;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FILTER-ATTR-U-public");
if(!temp.empty())
{
tempFilter.key = temp;
tempFilter.id = IS_PUBLIC;
filters.push_back(tempFilter);
}
temp = packet->GetVar("FAV-GAME");
if(!temp.empty())
{
size_t start = 0, end = 0;
if((end = temp.find(';', start)) != string::npos)
{
tempFilter.key = temp.substr(start, end-start);
tempFilter.id = SERVER_NAME;
filters.push_back(tempFilter);
start = end+1;
while((end = temp.find(';', start)) != string::npos && start < temp.size())
{
tempFilter.key = temp.substr(start, end-start);
tempFilter.id = SERVER_NAME;
filters.push_back(tempFilter);
start = end+1;
}
}
else
{
tempFilter.key = temp;
tempFilter.id = SERVER_NAME;
filters.push_back(tempFilter);
}
}
temp = packet->GetVar("FAV-GAME-UID");
if(!temp.empty())
{
size_t start = 0, end = 0;
if((end = temp.find(';', start)) != string::npos)
{
tempFilter.key = temp.substr(start, end-start);
tempFilter.id = UGID;
filters.push_back(tempFilter);
start = end+1;
while((end = temp.find(';', start)) != string::npos && start < temp.size())
{
tempFilter.key = temp.substr(start, end-start);
tempFilter.id = UGID;
filters.push_back(tempFilter);
start = end+1;
}
}
else
{
tempFilter.key = temp;
tempFilter.id = UGID;
filters.push_back(tempFilter);
}
}
database->listMatchingGames(&filters, filtered);
}