fix(server/game): replace the old session on a new login
This commit is contained in:
1 parent
59e7471014
commit
c16c95db94
6 files changed
+38
No files matched your search
@@ -32,6 +32,9 @@ void fillBasicInfo(proto::PlayerBasicInfo& info, const Player& player) {
|
|||||||
|
|
||||||
void onGetToken(net::Session& session, const proto::PlayerGetTokenCsReq& req) {
|
void onGetToken(net::Session& session, const proto::PlayerGetTokenCsReq& req) {
|
||||||
uint32_t uid = core::Config::get().player.uid;
|
uint32_t uid = core::Config::get().player.uid;
|
||||||
|
// Before the save is read: a session this login replaces saves its newest state on the
|
||||||
|
// way out, and cannot write a stale copy over this one's later.
|
||||||
|
session.dropOtherLogins(uid);
|
||||||
auto player = std::make_shared<Player>(uid);
|
auto player = std::make_shared<Player>(uid);
|
||||||
player->setMainCharacter(core::Config::get().gameplay.mainCharacter);
|
player->setMainCharacter(core::Config::get().gameplay.mainCharacter);
|
||||||
player->setMarchType(core::Config::get().gameplay.marchType);
|
player->setMarchType(core::Config::get().gameplay.marchType);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "core/logger.h"
|
#include "core/logger.h"
|
||||||
#include "core/util.h"
|
#include "core/util.h"
|
||||||
|
#include "game/player.h"
|
||||||
|
|
||||||
namespace net {
|
namespace net {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -102,6 +103,19 @@ void Gateway::drop(uint32_t conv, const char* why) {
|
|||||||
logging::info("game", "conv {} from {} disconnected ({})", conv, session->remote().str(), why);
|
logging::info("game", "conv {} from {} disconnected ({})", conv, session->remote().str(), why);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Gateway::dropOthers(const Session& keep, uint32_t uid) {
|
||||||
|
std::vector<uint32_t> stale;
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
for (const auto& [conv, session] : sessions_) {
|
||||||
|
if (session.get() == &keep) continue;
|
||||||
|
const game::Player* player = session->player();
|
||||||
|
if (player != nullptr && player->uid() == uid) stale.push_back(conv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint32_t conv : stale) drop(conv, "the account logged in again");
|
||||||
|
}
|
||||||
|
|
||||||
void Gateway::receiveLoop() {
|
void Gateway::receiveLoop() {
|
||||||
std::vector<char> buffer(kMaxDatagram);
|
std::vector<char> buffer(kMaxDatagram);
|
||||||
while (running_) {
|
while (running_) {
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ public:
|
|||||||
// `why` is logged: knowing whether the client hung up, timed out or handshook
|
// `why` is logged: knowing whether the client hung up, timed out or handshook
|
||||||
// again is most of the diagnosis when a session ends unexpectedly.
|
// again is most of the diagnosis when a session ends unexpectedly.
|
||||||
void drop(uint32_t conv, const char* why);
|
void drop(uint32_t conv, const char* why);
|
||||||
|
// A restarted client comes from a new port, so its old session outlives it; this drops
|
||||||
|
// that one before the new login reads the save it would otherwise overwrite later.
|
||||||
|
void dropOthers(const Session& keep, uint32_t uid);
|
||||||
std::shared_ptr<Session> find(uint32_t conv);
|
std::shared_ptr<Session> find(uint32_t conv);
|
||||||
// Every session still connected, in conv order. The admin routes act on these.
|
// Every session still connected, in conv order. The admin routes act on these.
|
||||||
std::vector<std::shared_ptr<Session>> sessions();
|
std::vector<std::shared_ptr<Session>> sessions();
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ void Session::sendRaw(uint16_t cmdId, std::string_view body) {
|
|||||||
|
|
||||||
void Session::sendEmpty(uint16_t cmdId) { sendRaw(cmdId, {}); }
|
void Session::sendEmpty(uint16_t cmdId) { sendRaw(cmdId, {}); }
|
||||||
|
|
||||||
|
void Session::dropOtherLogins(uint32_t uid) { gateway_.dropOthers(*this, uid); }
|
||||||
|
|
||||||
void Session::flushOutbound() {
|
void Session::flushOutbound() {
|
||||||
if (!kcp_) return;
|
if (!kcp_) return;
|
||||||
// ikcp_flush stamps every segment from kcp->current, and only ikcp_update ever
|
// ikcp_flush stamps every segment from kcp->current, and only ikcp_update ever
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ public:
|
|||||||
void sendRaw(uint16_t cmdId, std::string_view body);
|
void sendRaw(uint16_t cmdId, std::string_view body);
|
||||||
void sendEmpty(uint16_t cmdId);
|
void sendEmpty(uint16_t cmdId);
|
||||||
|
|
||||||
|
// Drops every other session logged in as `uid`; each saves on its way out.
|
||||||
|
void dropOtherLogins(uint32_t uid);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void send(uint16_t cmdId, const T& message) {
|
void send(uint16_t cmdId, const T& message) {
|
||||||
sendRaw(cmdId, message.serialize());
|
sendRaw(cmdId, message.serialize());
|
||||||
|
|||||||
@@ -1073,5 +1073,18 @@ void runFlowTests() {
|
|||||||
check(!client.await(cmd::ChessRogueQueryScRsp, packet, 600),
|
check(!client.await(cmd::ChessRogueQueryScRsp, packet, 600),
|
||||||
"a silenced module gets no reply");
|
"a silenced module gets no reply");
|
||||||
|
|
||||||
|
// The same account logging in from a new port replaces the old session, instead of
|
||||||
|
// leaving it to write its stale copy over the new login's save when it times out.
|
||||||
|
{
|
||||||
|
TestClient second;
|
||||||
|
check(second.connect(kTestPort), "a second client connects");
|
||||||
|
proto::PlayerGetTokenCsReq again;
|
||||||
|
again.account_uid = "capybara";
|
||||||
|
second.send(cmd::PlayerGetTokenCsReq, again);
|
||||||
|
net::Packet secondPacket;
|
||||||
|
check(second.await(cmd::PlayerGetTokenScRsp, secondPacket), "and logs in");
|
||||||
|
check(gateway.sessions().size() == 1, "leaving one session for the account");
|
||||||
|
}
|
||||||
|
|
||||||
gateway.stop();
|
gateway.stop();
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user