Skip to content

Commit 4458ba9

Browse files
authored
Realm queue is now responsible for active session tracking (#293)
All client authentication requests must go via the realm queue, allowing it to better manage player counts and apply back-pressure if required in the future.
1 parent 3a01e06 commit 4458ba9

8 files changed

Lines changed: 71 additions & 34 deletions

File tree

src/realm/ClientContext.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ ClientContext::ClientContext(executor& executor, const ConfigStore& cfg_store, E
2323
, account_rpc(account_rpc)
2424
, character_rpc(character_rpc)
2525
, realm_rpc(realm_rpc)
26-
, logger(logger) {}
26+
, logger(logger)
27+
, active(false) {}
2728

2829
void ClientContext::start_timer(const std::chrono::milliseconds& time) {
2930
timer_.expires_after(time);
@@ -60,6 +61,14 @@ bool ClientContext::packet_logging() const {
6061
return connection_->packet_logging();
6162
}
6263

64+
void ClientContext::set_active() {
65+
active = true;
66+
}
67+
68+
bool ClientContext::is_active() const {
69+
return active;
70+
}
71+
6372
/*
6473
* Helper that decides whether to print the IP address or username
6574
* and IP address in log outputs, based on whether authentication

src/realm/ClientContext.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ class ClientContext {
100100
connection_->send(packet);
101101
}
102102

103+
// if the client is occupying a slot
104+
void set_active();
105+
bool is_active() const;
106+
103107
StateContext state_ctx;
104108
std::optional<AccountInfo> account;
105109
const ConfigStore& cfg_store;
@@ -109,6 +113,7 @@ class ClientContext {
109113
CharacterClient& character_rpc;
110114
const RealmService& realm_rpc;
111115
log::Logger& logger;
116+
bool active;
112117

113118
friend class ClientHandler;
114119
};

src/realm/RealmQueue.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,16 @@ void RealmQueue::update_clients() {
5151
dirty_ = false;
5252
}
5353

54-
void RealmQueue::enqueue(ClientIdent client, int priority) {
54+
auto RealmQueue::enqueue(const ClientIdent& client, int priority) -> Result {
5555
std::lock_guard guard(lock_);
5656

57+
const auto max_slots = cstore_.config().max_slots;
58+
59+
if(active_ < max_slots && queue_.empty()) {
60+
++active_;
61+
return Result::success;
62+
}
63+
5764
if(queue_.empty()) {
5865
set_timer();
5966
}
@@ -64,6 +71,7 @@ void RealmQueue::enqueue(ClientIdent client, int priority) {
6471
// but allows for multiple priority levels without multiple hard-coded queues
6572
queue_.sort();
6673
dirty_ = true;
74+
return Result::queued;
6775
}
6876

6977
/*
@@ -75,9 +83,8 @@ void RealmQueue::dequeue(const ClientIdent& client) {
7583

7684
if(auto it = std::ranges::find(queue_, client, &QueueEntry::client); it != queue_.end()) {
7785
queue_.erase(it);
86+
dirty_ = true;
7887
}
79-
80-
dirty_ = true;
8188
}
8289

8390
/*
@@ -87,6 +94,8 @@ void RealmQueue::dequeue(const ClientIdent& client) {
8794
void RealmQueue::free_slot() {
8895
std::lock_guard guard(lock_);
8996

97+
--active_;
98+
9099
if(queue_.empty()) {
91100
return;
92101
}
@@ -119,6 +128,7 @@ void RealmQueue::shutdown() {
119128
}
120129

121130
std::size_t RealmQueue::size() const {
131+
std::lock_guard guard(lock_);
122132
return queue_.size();
123133
}
124134

src/realm/RealmQueue.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#pragma once
1010

11+
#include "ConfigStore.h"
1112
#include <shared/ClientIdent.h>
1213
#include <boost/asio/io_context.hpp>
1314
#include <boost/asio/steady_timer.hpp>
@@ -45,27 +46,37 @@ class RealmQueue final {
4546
boost::asio::steady_timer timer_;
4647
std::list<QueueEntry> queue_;
4748
EventDispatcher& dispatcher_;
48-
std::mutex lock_;
49+
mutable std::mutex lock_;
4950
std::atomic_bool dirty_;
51+
std::size_t active_;
52+
const ConfigStore& cstore_;
5053

5154
bool empty();
5255
void update_clients();
5356
void set_timer();
5457

5558
public:
5659
constexpr static std::size_t npos = 0;
60+
61+
enum class Result {
62+
success,
63+
queued,
64+
};
5765

5866
explicit RealmQueue(boost::asio::io_context& service,
5967
EventDispatcher& dispatcher,
68+
ConfigStore& config_store,
6069
std::chrono::milliseconds frequency = default_frequency)
6170
: frequency_(frequency)
6271
, timer_(service)
6372
, dispatcher_(dispatcher)
64-
, dirty_(false) { }
73+
, dirty_(false)
74+
, active_(0)
75+
, cstore_(config_store) { }
6576

6677
~RealmQueue();
6778

68-
void enqueue(ClientIdent client, int priority = 0);
79+
Result enqueue(const ClientIdent& client, int priority = 0);
6980
void dequeue(const ClientIdent& client);
7081
std::size_t poll(const ClientIdent& client);
7182
void free_slot();

src/realm/Service.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ void Service::initialise(const opts::variables_map& args) try {
233233
const auto nsd_port = args["nsd.port"].as<std::uint16_t>();
234234

235235
ctx->rpc_discovery = std::make_unique<NetworkServiceDiscovery>(*ctx->rpc, nsd_host, nsd_port, logger);
236-
ctx->queue = std::make_unique<RealmQueue>(service, *ctx->dispatcher);
236+
ctx->queue = std::make_unique<RealmQueue>(service, *ctx->dispatcher, *ctx->config_store);
237237

238238
// Misc. information
239239
const auto max_socks = utility::max_sockets_desc();

src/realm/commands/Ungrouped.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,27 @@ auto uptime(utility::CommandExecutor& exec, std::chrono::steady_clock::time_poin
2525
));
2626
}
2727

28+
auto queue(utility::CommandExecutor& exec, const RealmQueue& realm_queue, log::Logger& logger)
29+
-> std::shared_ptr<commands::Command> {
30+
return commands::create("queue")
31+
->description("Display authentication queue size")
32+
->handler(exec([&](auto&) {
33+
LOG_CONSOLE(logger, "Current realm queue size is {}", realm_queue.size());
34+
}
35+
));
36+
}
37+
2838
} // unnamed
2939

3040
void add_ungrouped_commands(ServiceContext& context, commands::Command& registry, log::Logger& logger) {
3141
auto impl = context.get();
32-
auto scoped = registry.scoped_insert(uptime(*impl->cmd_exec, impl->start_time, logger));
33-
impl->commands.emplace_back(std::move(scoped));
42+
auto& realm_queue = *impl->queue;
43+
44+
auto command = uptime(*impl->cmd_exec, impl->start_time, logger);
45+
impl->commands.emplace_back(registry.scoped_insert(std::move(command)));
46+
47+
command = queue(*impl->cmd_exec, realm_queue, logger);
48+
impl->commands.emplace_back(registry.scoped_insert(std::move(command)));
3449
}
3550

3651
} // realm, ember

src/realm/states/Authentication.cpp

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ void send_auth_challenge(ClientContext& ctx);
3939
void send_auth_result(ClientContext& ctx, protocol::Result result);
4040
void handle_authentication(ClientContext& ctx);
4141
void handle_queue_update(ClientContext& ctx, const QueuePosition& event);
42-
void handle_queue_success(ClientContext& ctx);
4342
void auth_success(ClientContext& ctx);
4443
void auth_queue(ClientContext& ctx);
4544
void prove_session(ClientContext& ctx, const Botan::BigInt& key);
@@ -197,18 +196,13 @@ void prove_session(ClientContext& ctx, const Botan::BigInt& key) {
197196
.username = auth_ctx.packet->username
198197
};
199198

200-
// todo, allowing for multiple realms to connect to a single world server
201-
// will require an external service to keep track of available slots
202-
unsigned int active_players = 0;
203-
const auto& config = ctx.cfg_store.config_tls();
204-
205-
if(active_players < config.max_slots) {
199+
ctx.cancel_timer();
200+
201+
if(ctx.queue.enqueue(ctx.handler().uuid(), 0) == RealmQueue::Result::success) {
206202
auth_success(ctx);
207203
} else {
208-
auth_queue(ctx);
204+
auth_state(ctx, State::in_queue);
209205
}
210-
211-
++active_players;
212206
}
213207

214208
void send_auth_challenge(ClientContext& ctx) {
@@ -248,26 +242,14 @@ void send_addon_data(ClientContext& ctx) {
248242
ctx.send(response);
249243
}
250244

251-
void auth_queue(ClientContext& ctx) {
252-
LOG_TRACE(ctx.logger, log_func);
253-
254-
const auto& uuid = ctx.handler().uuid();
255-
auto& dispatcher = ctx.dispatcher;
256-
257-
ctx.queue.enqueue(uuid);
258-
ctx.cancel_timer();
259-
auth_state(ctx, State::in_queue);
260-
261-
CLIENT_DEBUG(ctx, "Added to queue, position {}", ctx.queue.poll(uuid));
262-
}
263-
264245
void auth_success(ClientContext& ctx) {
265246
LOG_TRACE(ctx.logger, log_func);
266247

267248
send_auth_result(ctx, protocol::Result::auth_ok);
268249
send_addon_data(ctx);
269250
auth_state(ctx, State::success);
270251
ctx.state_update(ClientState::cs_character_list);
252+
ctx.set_active();
271253

272254
CLIENT_DEBUG(ctx, "Authenticated as {}", ctx.account->username);
273255
}
@@ -333,7 +315,7 @@ void handle_event(ClientContext& ctx, const Event& event) {
333315
handle_queue_update(ctx, event.as<QueuePosition>());
334316
break;
335317
case queue_success:
336-
handle_queue_success(ctx);
318+
auth_success(ctx);
337319
break;
338320
default:
339321
break;

src/realm/states/SessionClose.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@
1010
#include "../ClientContext.h"
1111
#include "../ClientHandler.h"
1212
#include "../Events.h"
13+
#include "../RealmQueue.h"
1314

1415
namespace ember::realm::session_close {
1516

1617
void enter(ClientContext& ctx) {
1718
ctx.handler().log_redirect_stop();
19+
20+
if(ctx.is_active()) {
21+
ctx.queue.free_slot();
22+
}
1823
}
1924

2025
void handle_packet(ClientContext& ctx, protocol::ClientOpcode opcode) {

0 commit comments

Comments
 (0)