mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-24 14:01:05 +00:00
Revamp CNShared logic
* Use a specialized connection object * Copy the Player object less frequently * Use a randomly generated serial key for shard auth * Refuse invalid shard connection attempts * Clean up connection metadata when a Player joins the shard * Prune abandoned connections when they time out
This commit is contained in:
parent
c5dd745aa1
commit
741bfb675b
@ -14,9 +14,7 @@
|
||||
struct Player : public Entity {
|
||||
int accountId = 0;
|
||||
int accountLevel = 0; // permission level (see CN_ACCOUNT_LEVEL enums)
|
||||
int64_t SerialKey = 0;
|
||||
int32_t iID = 0;
|
||||
uint64_t FEKey = 0;
|
||||
|
||||
int level = 0;
|
||||
int HP = 0;
|
||||
|
@ -28,7 +28,7 @@ using namespace PlayerManager;
|
||||
|
||||
std::map<CNSocket*, Player*> PlayerManager::players;
|
||||
|
||||
static void addPlayer(CNSocket* key, Player plr) {
|
||||
static void addPlayer(CNSocket* key, Player& plr) {
|
||||
Player *p = new Player();
|
||||
|
||||
// copy object into heap memory
|
||||
@ -209,20 +209,22 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
auto enter = (sP_CL2FE_REQ_PC_ENTER*)data->buf;
|
||||
INITSTRUCT(sP_FE2CL_REP_PC_ENTER_SUCC, response);
|
||||
|
||||
// TODO: check if serialkey exists, if it doesn't send sP_FE2CL_REP_PC_ENTER_FAIL
|
||||
Player plr = CNShared::getPlayer(enter->iEnterSerialKey);
|
||||
LoginMetadata *lm = CNShared::getLoginMetadata(enter->iEnterSerialKey);
|
||||
if (lm == nullptr) {
|
||||
delete lm;
|
||||
|
||||
std::cout << "[WARN] Refusing invalid REQ_PC_ENTER" << std::endl;
|
||||
INITSTRUCT(sP_FE2CL_REP_PC_ENTER_FAIL, fail);
|
||||
sock->sendPacket(fail, P_FE2CL_REP_PC_ENTER_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
// for convenience
|
||||
Player& plr = lm->plr;
|
||||
|
||||
plr.groupCnt = 1;
|
||||
plr.iIDGroup = plr.groupIDs[0] = plr.iID;
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2FE_REQ_PC_ENTER:" << std::endl;
|
||||
std::cout << "\tID: " << AUTOU16TOU8(enter->szID) << std::endl;
|
||||
std::cout << "\tSerial: " << enter->iEnterSerialKey << std::endl;
|
||||
std::cout << "\tTemp: " << enter->iTempValue << std::endl;
|
||||
std::cout << "\tPC_UID: " << plr.PCStyle.iPC_UID << std::endl;
|
||||
)
|
||||
|
||||
// check if account is already in use
|
||||
if (isAccountInUse(plr.accountId)) {
|
||||
// kick the other player
|
||||
@ -267,7 +269,6 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
// nanos
|
||||
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
||||
response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i];
|
||||
//response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i] = {0};
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
response.PCLoadData2CL.aNanoSlots[i] = plr.equippedNanos[i];
|
||||
@ -308,11 +309,10 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
response.PCLoadData2CL.iFirstUseFlag2 = plr.iFirstUseFlag[1];
|
||||
}
|
||||
|
||||
plr.SerialKey = enter->iEnterSerialKey;
|
||||
plr.instanceID = INSTANCE_OVERWORLD; // the player should never be in an instance on enter
|
||||
|
||||
sock->setEKey(CNSocketEncryption::createNewKey(response.uiSvrTime, response.iID + 1, response.PCLoadData2CL.iFusionMatter + 1));
|
||||
sock->setFEKey(plr.FEKey);
|
||||
sock->setFEKey(lm->FEKey);
|
||||
sock->setActiveKey(SOCKETKEY_FE); // send all packets using the FE key from now on
|
||||
|
||||
sock->sendPacket(response, P_FE2CL_REP_PC_ENTER_SUCC);
|
||||
@ -320,7 +320,9 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
// transmit MOTD after entering the game, so the client hopefully changes modes on time
|
||||
Chat::sendServerMessage(sock, settings::MOTDSTRING);
|
||||
|
||||
// copy Player object into the shard
|
||||
addPlayer(sock, plr);
|
||||
|
||||
// check if there is an expiring vehicle
|
||||
Items::checkItemExpire(sock, getPlayer(sock));
|
||||
|
||||
@ -337,6 +339,9 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
for (auto& pair : players)
|
||||
if (pair.second->notify)
|
||||
Chat::sendServerMessage(pair.first, "[ADMIN]" + getPlayerName(&plr) + " has joined.");
|
||||
|
||||
// deallocate lm (and therefore the plr object)
|
||||
delete lm;
|
||||
}
|
||||
|
||||
void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) {
|
||||
@ -705,4 +710,6 @@ void PlayerManager::init() {
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, exitPlayerVehicle);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, changePlayerGuide);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET, setFirstUseFlag);
|
||||
|
||||
REGISTER_SHARD_TIMER(CNShared::pruneLoginMetadata, CNSHARED_TIMEOUT);
|
||||
}
|
||||
|
@ -5,23 +5,47 @@
|
||||
#else
|
||||
#include <mutex>
|
||||
#endif
|
||||
std::map<int64_t, Player> CNShared::players;
|
||||
std::mutex playerCrit;
|
||||
|
||||
void CNShared::setPlayer(int64_t sk, Player& plr) {
|
||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
||||
static std::unordered_map<int64_t, LoginMetadata*> login;
|
||||
static std::mutex mtx;
|
||||
|
||||
players[sk] = plr;
|
||||
void CNShared::storeLoginMetadata(int64_t sk, LoginMetadata *lm) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
// take ownership of connection data
|
||||
login[sk] = lm;
|
||||
}
|
||||
|
||||
Player CNShared::getPlayer(int64_t sk) {
|
||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
||||
LoginMetadata* CNShared::getLoginMetadata(int64_t sk) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
return players[sk];
|
||||
// fail if the key isn't found
|
||||
if (login.find(sk) == login.end())
|
||||
return nullptr;
|
||||
|
||||
// transfer ownership of connection data to shard
|
||||
LoginMetadata *lm = login[sk];
|
||||
login.erase(sk);
|
||||
|
||||
return lm;
|
||||
}
|
||||
|
||||
void CNShared::erasePlayer(int64_t sk) {
|
||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
||||
void CNShared::pruneLoginMetadata(CNServer *serv, time_t currTime) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
players.erase(sk);
|
||||
auto it = login.begin();
|
||||
while (it != login.end()) {
|
||||
auto& sk = it->first;
|
||||
auto& lm = it->second;
|
||||
|
||||
if (lm->timestamp + CNSHARED_TIMEOUT > currTime) {
|
||||
std::cout << "[WARN] Pruning hung connection attempt" << std::endl;
|
||||
|
||||
// deallocate object and remove map entry
|
||||
delete login[sk];
|
||||
it = login.erase(it); // skip the invalidated iterator
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,11 +10,16 @@
|
||||
|
||||
#include "Player.hpp"
|
||||
|
||||
namespace CNShared {
|
||||
// serialkey corresponds to player data
|
||||
extern std::map<int64_t, Player> players;
|
||||
#define CNSHARED_TIMEOUT 30000
|
||||
|
||||
void setPlayer(int64_t sk, Player& plr);
|
||||
Player getPlayer(int64_t sk);
|
||||
void erasePlayer(int64_t sk);
|
||||
struct LoginMetadata {
|
||||
uint64_t FEKey;
|
||||
Player plr;
|
||||
time_t timestamp;
|
||||
};
|
||||
|
||||
namespace CNShared {
|
||||
void storeLoginMetadata(int64_t sk, LoginMetadata *lm);
|
||||
LoginMetadata* getLoginMetadata(int64_t sk);
|
||||
void pruneLoginMetadata(CNServer *serv, time_t currTime);
|
||||
}
|
||||
|
@ -468,21 +468,24 @@ void CNLoginServer::characterSelect(CNSocket* sock, CNPacketData* data) {
|
||||
resp.g_FE_ServerIP[strlen(shard_ip)] = '\0';
|
||||
resp.g_FE_ServerPort = settings::SHARDPORT;
|
||||
|
||||
// pass player to CNShared
|
||||
Player passPlayer = {};
|
||||
Database::getPlayer(&passPlayer, selection->iPC_UID);
|
||||
LoginMetadata *lm = new LoginMetadata();
|
||||
lm->FEKey = sock->getFEKey();
|
||||
lm->timestamp = getTime();
|
||||
|
||||
Database::getPlayer(&lm->plr, selection->iPC_UID);
|
||||
// this should never happen but for extra safety
|
||||
if (passPlayer.iID == 0)
|
||||
if (lm->plr.iID == 0)
|
||||
return invalidCharacter(sock);
|
||||
|
||||
passPlayer.FEKey = sock->getFEKey();
|
||||
resp.iEnterSerialKey = passPlayer.iID;
|
||||
CNShared::setPlayer(resp.iEnterSerialKey, passPlayer);
|
||||
resp.iEnterSerialKey = Rand::rand(); // TODO: cryptographic RNG
|
||||
|
||||
// transfer ownership of connection data to CNShared
|
||||
CNShared::storeLoginMetadata(resp.iEnterSerialKey, lm);
|
||||
|
||||
sock->sendPacket(resp, P_LS2CL_REP_SHARD_SELECT_SUCC);
|
||||
|
||||
// update current slot in DB
|
||||
Database::updateSelected(loginSessions[sock].userID, passPlayer.slot);
|
||||
Database::updateSelected(loginSessions[sock].userID, lm->plr.slot);
|
||||
}
|
||||
|
||||
void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) {
|
||||
|
@ -99,14 +99,7 @@ void CNShardServer::_killConnection(CNSocket* cns) {
|
||||
if (PlayerManager::players.find(cns) == PlayerManager::players.end())
|
||||
return;
|
||||
|
||||
Player* plr = PlayerManager::getPlayer(cns);
|
||||
|
||||
int64_t key = plr->SerialKey;
|
||||
|
||||
PlayerManager::removePlayer(cns); // removes the player from the list and saves it to DB
|
||||
|
||||
// remove from CNShared
|
||||
CNShared::erasePlayer(key);
|
||||
}
|
||||
|
||||
void CNShardServer::killConnection(CNSocket *cns) {
|
||||
|
Loading…
Reference in New Issue
Block a user