mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-24 22:11:04 +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 {
|
struct Player : public Entity {
|
||||||
int accountId = 0;
|
int accountId = 0;
|
||||||
int accountLevel = 0; // permission level (see CN_ACCOUNT_LEVEL enums)
|
int accountLevel = 0; // permission level (see CN_ACCOUNT_LEVEL enums)
|
||||||
int64_t SerialKey = 0;
|
|
||||||
int32_t iID = 0;
|
int32_t iID = 0;
|
||||||
uint64_t FEKey = 0;
|
|
||||||
|
|
||||||
int level = 0;
|
int level = 0;
|
||||||
int HP = 0;
|
int HP = 0;
|
||||||
|
@ -28,7 +28,7 @@ using namespace PlayerManager;
|
|||||||
|
|
||||||
std::map<CNSocket*, Player*> PlayerManager::players;
|
std::map<CNSocket*, Player*> PlayerManager::players;
|
||||||
|
|
||||||
static void addPlayer(CNSocket* key, Player plr) {
|
static void addPlayer(CNSocket* key, Player& plr) {
|
||||||
Player *p = new Player();
|
Player *p = new Player();
|
||||||
|
|
||||||
// copy object into heap memory
|
// 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;
|
auto enter = (sP_CL2FE_REQ_PC_ENTER*)data->buf;
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_ENTER_SUCC, response);
|
INITSTRUCT(sP_FE2CL_REP_PC_ENTER_SUCC, response);
|
||||||
|
|
||||||
// TODO: check if serialkey exists, if it doesn't send sP_FE2CL_REP_PC_ENTER_FAIL
|
LoginMetadata *lm = CNShared::getLoginMetadata(enter->iEnterSerialKey);
|
||||||
Player plr = CNShared::getPlayer(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.groupCnt = 1;
|
||||||
plr.iIDGroup = plr.groupIDs[0] = plr.iID;
|
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
|
// check if account is already in use
|
||||||
if (isAccountInUse(plr.accountId)) {
|
if (isAccountInUse(plr.accountId)) {
|
||||||
// kick the other player
|
// kick the other player
|
||||||
@ -267,7 +269,6 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
// nanos
|
// nanos
|
||||||
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
||||||
response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i];
|
response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i];
|
||||||
//response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i] = {0};
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
response.PCLoadData2CL.aNanoSlots[i] = plr.equippedNanos[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];
|
response.PCLoadData2CL.iFirstUseFlag2 = plr.iFirstUseFlag[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
plr.SerialKey = enter->iEnterSerialKey;
|
|
||||||
plr.instanceID = INSTANCE_OVERWORLD; // the player should never be in an instance on enter
|
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->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->setActiveKey(SOCKETKEY_FE); // send all packets using the FE key from now on
|
||||||
|
|
||||||
sock->sendPacket(response, P_FE2CL_REP_PC_ENTER_SUCC);
|
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
|
// transmit MOTD after entering the game, so the client hopefully changes modes on time
|
||||||
Chat::sendServerMessage(sock, settings::MOTDSTRING);
|
Chat::sendServerMessage(sock, settings::MOTDSTRING);
|
||||||
|
|
||||||
|
// copy Player object into the shard
|
||||||
addPlayer(sock, plr);
|
addPlayer(sock, plr);
|
||||||
|
|
||||||
// check if there is an expiring vehicle
|
// check if there is an expiring vehicle
|
||||||
Items::checkItemExpire(sock, getPlayer(sock));
|
Items::checkItemExpire(sock, getPlayer(sock));
|
||||||
|
|
||||||
@ -337,6 +339,9 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
for (auto& pair : players)
|
for (auto& pair : players)
|
||||||
if (pair.second->notify)
|
if (pair.second->notify)
|
||||||
Chat::sendServerMessage(pair.first, "[ADMIN]" + getPlayerName(&plr) + " has joined.");
|
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) {
|
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_VEHICLE_OFF, exitPlayerVehicle);
|
||||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, changePlayerGuide);
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, changePlayerGuide);
|
||||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET, setFirstUseFlag);
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET, setFirstUseFlag);
|
||||||
|
|
||||||
|
REGISTER_SHARD_TIMER(CNShared::pruneLoginMetadata, CNSHARED_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
@ -5,23 +5,47 @@
|
|||||||
#else
|
#else
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#endif
|
#endif
|
||||||
std::map<int64_t, Player> CNShared::players;
|
|
||||||
std::mutex playerCrit;
|
|
||||||
|
|
||||||
void CNShared::setPlayer(int64_t sk, Player& plr) {
|
static std::unordered_map<int64_t, LoginMetadata*> login;
|
||||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
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) {
|
LoginMetadata* CNShared::getLoginMetadata(int64_t sk) {
|
||||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
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) {
|
void CNShared::pruneLoginMetadata(CNServer *serv, time_t currTime) {
|
||||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
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"
|
#include "Player.hpp"
|
||||||
|
|
||||||
namespace CNShared {
|
#define CNSHARED_TIMEOUT 30000
|
||||||
// serialkey corresponds to player data
|
|
||||||
extern std::map<int64_t, Player> players;
|
|
||||||
|
|
||||||
void setPlayer(int64_t sk, Player& plr);
|
struct LoginMetadata {
|
||||||
Player getPlayer(int64_t sk);
|
uint64_t FEKey;
|
||||||
void erasePlayer(int64_t sk);
|
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_ServerIP[strlen(shard_ip)] = '\0';
|
||||||
resp.g_FE_ServerPort = settings::SHARDPORT;
|
resp.g_FE_ServerPort = settings::SHARDPORT;
|
||||||
|
|
||||||
// pass player to CNShared
|
LoginMetadata *lm = new LoginMetadata();
|
||||||
Player passPlayer = {};
|
lm->FEKey = sock->getFEKey();
|
||||||
Database::getPlayer(&passPlayer, selection->iPC_UID);
|
lm->timestamp = getTime();
|
||||||
|
|
||||||
|
Database::getPlayer(&lm->plr, selection->iPC_UID);
|
||||||
// this should never happen but for extra safety
|
// this should never happen but for extra safety
|
||||||
if (passPlayer.iID == 0)
|
if (lm->plr.iID == 0)
|
||||||
return invalidCharacter(sock);
|
return invalidCharacter(sock);
|
||||||
|
|
||||||
passPlayer.FEKey = sock->getFEKey();
|
resp.iEnterSerialKey = Rand::rand(); // TODO: cryptographic RNG
|
||||||
resp.iEnterSerialKey = passPlayer.iID;
|
|
||||||
CNShared::setPlayer(resp.iEnterSerialKey, passPlayer);
|
// transfer ownership of connection data to CNShared
|
||||||
|
CNShared::storeLoginMetadata(resp.iEnterSerialKey, lm);
|
||||||
|
|
||||||
sock->sendPacket(resp, P_LS2CL_REP_SHARD_SELECT_SUCC);
|
sock->sendPacket(resp, P_LS2CL_REP_SHARD_SELECT_SUCC);
|
||||||
|
|
||||||
// update current slot in DB
|
// 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) {
|
void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) {
|
||||||
|
@ -99,14 +99,7 @@ void CNShardServer::_killConnection(CNSocket* cns) {
|
|||||||
if (PlayerManager::players.find(cns) == PlayerManager::players.end())
|
if (PlayerManager::players.find(cns) == PlayerManager::players.end())
|
||||||
return;
|
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
|
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) {
|
void CNShardServer::killConnection(CNSocket *cns) {
|
||||||
|
Loading…
Reference in New Issue
Block a user