Merge pull request #74 from xThrasherrr/feat/server-map-pool

Let server hosts choose the map pool and rotate through it
This commit is contained in:
zeex64 authored and GitHub committed 2026-10-05 16:53:52 -05:00
commit 022900d9fc
22 files changed
+402 -18

No files matched your search

+48 -2
View File
@@ -5,7 +5,7 @@
#include <cmath>
// Player votes (map, kick, time of day) and the chat commands that run them. Each vote is
// switched on and given its pass percentage in ReSkateServer.json ("votes").
// switched on and given its pass percentage in ReSkateServer.json ("votes"); also the map pool and rotation.
namespace dingosdk::server {
namespace {
constexpr std::array<std::string_view, 8> times{"default", "morning", "noon", "afternoon",
@@ -67,6 +67,7 @@ void Host::chat_command(Guest &guest, std::string_view line) {
if (votes & server_vote_kick) text += "/vote kick <player>: start a vote to kick a player\n";
if (votes & server_vote_time) text += "/vote tod <time>: vote for a time of day (morning, noon, night...)\n";
if (votes) text += "/yes or /no: vote in the running vote\n";
if (config_.map_rotation) text += "The map changes every " + std::to_string(config_.map_rotation) + " min.\n";
if (config_.parties) text += "/party: your party (invite, accept, leave...; /party help); /p <message>: party chat\n";
if (is_admin(guest.member.id)) text += "Admins: any server command as /<command>, e.g. /kick, /map, /tpall, /votes, /msg, /msg-party, /msg-admins\n";
const std::string whisper = "/w <player> <message>: send a private message";
@@ -128,8 +129,10 @@ void Host::start_vote(Guest &guest, VoteKind kind, std::string_view argument) {
case VoteKind::map: {
if (argument.empty()) return reply(guest, "/vote map <map>, e.g. /vote map grom");
// Players vote between the server's own maps (/maps); a raw level path is admins only.
if (!find_level(argument) || !valid_map_destination(map_destination(argument)))
const auto *level = find_level(argument);
if (!level || !valid_map_destination(map_destination(argument)))
return reply(guest, "No single map is called \"" + std::string(argument) + "\".");
if (!in_map_pool(config_, level->asset)) return reply(guest, level->name + " is not one of this server's maps.\n" + pool_text());
if (map_hash(map_destination(argument)) == map_) return reply(guest, "The server is already on that map.");
vote.value = std::string(argument);
vote.label = "change the map to " + map_label(argument);
@@ -230,4 +233,47 @@ void Host::check_vote(bool expired) {
break;
}
}
std::string Host::pool_text() const {
std::string text = config_.map_pool.empty() ? "Map pool: every map" : "Map pool:";
if (!config_.map_pool.empty())
for (const auto *level : pool_levels(config_)) text += "\n " + level->name + (same_map(level->asset) ? " (now)" : "");
return text;
}
std::string Host::rotation_text() const {
if (!config_.map_rotation) return "Map rotation is off.";
const auto every = "The map changes every " + std::to_string(config_.map_rotation) + " min";
const auto *next = next_pool_map(config_, config_.map);
if (!next) return every + ", but the map pool has no other map.";
if (!players()) return every + " while players are on. Next: " + next->name + ".";
const auto due = map_since_ + std::uint64_t{config_.map_rotation} * 60000000;
const auto left = due > now_ ? (due - now_ + 59999999) / 60000000 : 0;
return every + ". Next: " + next->name + " in about " + std::to_string(std::max<std::uint64_t>(left, 1)) + " min.";
}
void Host::resend_maps() {
for (auto &[id, guest] : guests_) guest->maps_sent = false;
}
void Host::tick_rotation() { // waits while nobody is on, and for a running map vote
if (!config_.map_rotation || !players()) {
map_since_ = now_;
rotation_warned_ = false;
return;
}
const auto due = map_since_ + std::uint64_t{config_.map_rotation} * 60000000;
if (now_ + 60000000 < due) return;
const auto *next = next_pool_map(config_, config_.map);
if (!next) {
map_since_ = now_;
return;
}
if (!rotation_warned_ && config_.map_rotation > 1) {
rotation_warned_ = true;
send_chat("Next map in 1 minute: " + next->name + ".");
}
if (now_ < due || (vote_ && vote_->kind == VoteKind::map)) return;
send_chat("Changing the map to " + next->name + ".");
log_("[rotation] Changing the map to " + next->name + ".");
change_map(next->name);
save();
}
} // namespace dingosdk::server