Skip to content

Commit

Permalink
players keep a spot and their score for 5 minutes
Browse files Browse the repository at this point in the history
  • Loading branch information
qchateau committed Sep 19, 2020
1 parent d8c4442 commit f3ef7f4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 35 deletions.
2 changes: 1 addition & 1 deletion server/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class player_t {

const auto& state() const { return state_; };
id_t id() const { return id_; }
std::string name() const { return name_; }
const std::string& name() const { return name_; }
bool alive() const { return alive_; }
bool fake() const { return fake_; }
double score() const { return score_; }
Expand Down
11 changes: 1 addition & 10 deletions server/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,8 @@ bool player_name_is_valid(std::string_view name)
}

session_t::session_t(std::shared_ptr<world_t> world, tcp::socket&& socket)
: world_{std::move(world)},
ws_{std::move(socket)},
remote_endpoint_{ws_.next_layer().socket().remote_endpoint()},
timer_{ws_.get_executor()}
: world_{std::move(world)}, ws_{std::move(socket)}, timer_{ws_.get_executor()}
{
spdlog::info("new session from {}", remote_endpoint().address().to_string());
}

session_t::~session_t()
{
spdlog::info("closing session from {}", remote_endpoint().address().to_string());
}

void session_t::run()
Expand Down
3 changes: 0 additions & 3 deletions server/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace sd {
class session_t : public std::enable_shared_from_this<session_t> {
public:
session_t(std::shared_ptr<world_t> world, tcp::socket&& socket);
~session_t();

session_t(const session_t&) = delete;
session_t(session_t&&) = delete;
Expand All @@ -23,7 +22,6 @@ class session_t : public std::enable_shared_from_this<session_t> {
session_t& operator=(session_t&&) = delete;

void run();
tcp::endpoint remote_endpoint() const { return remote_endpoint_; }

private:
net::awaitable<void> do_run();
Expand All @@ -38,7 +36,6 @@ class session_t : public std::enable_shared_from_this<session_t> {
std::shared_ptr<world_t> world_;
player_handle_t player_;
websocket::stream<beast::tcp_stream> ws_;
const tcp::endpoint remote_endpoint_;
net::steady_timer timer_;
};

Expand Down
99 changes: 80 additions & 19 deletions server/world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ namespace sd {

namespace {

constexpr auto check_idle_dt = std::chrono::seconds{1};

// an IDLE player keeps a reserved spot in the world
// this means he can reconnect to play with the same
// players and will keep his score
// In the meantime, a bot takes his place
constexpr auto idle_duration = std::chrono::minutes{5};

constexpr std::size_t max_players = 8;

std::string get_fake_player_name(
const std::vector<std::unique_ptr<player_t>>& current_players)
{
Expand Down Expand Up @@ -44,13 +54,12 @@ world_t::~world_t() = default;

int world_t::real_players() const
{
int real_players = 0;
for (const auto& p : players_) {
if (!p->fake()) {
++real_players;
}
}
return real_players;
return active_real_players() + idle_players_.size();
}

int world_t::active_real_players() const
{
return players_.size() - fake_players_.size();
}

int world_t::available_places() const
Expand Down Expand Up @@ -78,16 +87,29 @@ player_handle_t world_t::register_player(
throw player_already_registered{};
}

auto& new_player = players_.emplace_back(
std::make_unique<player_t>(*this, player_id, player_name, fake));
auto idle_it =
find_if(begin(idle_players_), end(idle_players_), [&](const auto& p) {
return p.player->id() == player_id;
});
if (idle_it != end(idle_players_)) {
players_.emplace_back(std::move(idle_it->player));
players_.back()->respawn();
idle_players_.erase(idle_it);
spdlog::info("restoring player {} ({})", player_name, to_string(player_id));
}
else {
players_.emplace_back(
std::make_unique<player_t>(*this, player_id, player_name, fake));

if (!fake) {
spdlog::info("registered player {}", to_string(new_player->id()));
if (!fake) {
spdlog::info(
"registering player {} ({})", player_name, to_string(player_id));
}
}

net::post(ioc_, [this]() { adjust_players(); });
return {
new_player.get(),
players_.back().get(),
[self = shared_from_this()](player_t* p) { self->unregister_player(*p); },
};
}
Expand All @@ -98,23 +120,26 @@ void world_t::unregister_player(const player_t& p)
return p == *player;
});
if (it == end(players_)) {
spdlog::warn("unregistered unknown player {}", to_string(p.id()));
spdlog::warn(
"unregistering unknown player {} ({})", p.name(), to_string(p.id()));
return;
}
players_.erase(it);

if (!p.fake()) {
spdlog::info("unregistered player {}", to_string(p.id()));
idle_players_.push_back({clock_t::now(), std::move(*it)});
spdlog::info("moving player {} to idle ({})", p.name(), to_string(p.id()));
}

players_.erase(it);

net::post(ioc_, [this]() { adjust_players(); });
}

void world_t::adjust_players()
{
if (real_players() == 0) {
if (active_real_players() == 0) {
if (!fake_players_.empty()) {
spdlog::info("no more players, removing all fake players");
spdlog::info("no more active players, removing all fake players");
fake_players_.clear();
}
return;
Expand Down Expand Up @@ -142,7 +167,13 @@ void world_t::run()
net::co_spawn(
ioc_,
[self = shared_from_this()]() -> net::awaitable<void> {
co_await self->on_run();
co_await self->update_loop();
},
net::detached);
net::co_spawn(
ioc_,
[self = shared_from_this()]() -> net::awaitable<void> {
co_await self->check_idle_players_loop();
},
net::detached);
}
Expand Down Expand Up @@ -179,7 +210,7 @@ nlohmann::json world_t::game_state_for_player(const player_handle_t& player)
return state;
}

net::awaitable<void> world_t::on_run()
net::awaitable<void> world_t::update_loop()
{
auto executor = co_await net::this_coro::executor;
net::steady_timer timer{executor};
Expand All @@ -192,6 +223,19 @@ net::awaitable<void> world_t::on_run()
}
}

net::awaitable<void> world_t::check_idle_players_loop()
{
auto executor = co_await net::this_coro::executor;
net::steady_timer timer{executor};
timer.expires_from_now(std::chrono::seconds{0});

while (true) {
check_idle_players();
timer.expires_at(timer.expires_at() + check_idle_dt);
co_await timer.async_wait(net::use_awaitable);
}
}

void world_t::update(std::chrono::nanoseconds dt)
{
// update player positions
Expand Down Expand Up @@ -296,4 +340,21 @@ void world_t::update_fake_player_dd(player_t& p)
fake_player_speed_factor * dy * player_t::max_dd);
}

void world_t::check_idle_players()
{
const auto remove_from = clock_t::now() - idle_duration;
auto end_it = std::remove_if(
begin(idle_players_), end(idle_players_), [&](const auto& p) {
if (p.from < remove_from) {
spdlog::info(
"unregistering player {} ({})",
p.player->name(),
to_string(p.player->id()));
return true;
}
return false;
});
idle_players_.erase(end_it, end(idle_players_));
}

} // sd
14 changes: 12 additions & 2 deletions server/world.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class player_already_registered : public std::runtime_error {
class world_t : public std::enable_shared_from_this<world_t> {
public:
static constexpr auto refresh_dt = std::chrono::milliseconds{20};
static constexpr std::size_t max_players = 8;

world_t(net::io_context& ioc);
~world_t();
Expand All @@ -34,22 +33,33 @@ class world_t : public std::enable_shared_from_this<world_t> {
const player_id_t& player_id,
std::string_view player_name);
int real_players() const;
int active_real_players() const;
int available_places() const;

private:
using clock_t = std::chrono::steady_clock;
struct idle_player {
clock_t::time_point from;
std::unique_ptr<player_t> player;
};

player_handle_t register_player(
const player_id_t& player_id,
std::string_view player_name,
bool fake);
void unregister_player(const player_t& p);
void adjust_players();

net::awaitable<void> on_run();
net::awaitable<void> update_loop();
net::awaitable<void> check_idle_players_loop();

void update(std::chrono::nanoseconds dt);
void update_fake_player_dd(player_t& p);
void check_idle_players();

net::io_context& ioc_;
std::vector<std::unique_ptr<player_t>> players_;
std::vector<idle_player> idle_players_;
std::list<player_handle_t> fake_players_;
boost::uuids::random_generator uuid_generator_;
};
Expand Down

0 comments on commit f3ef7f4

Please sign in to comment.