#include "server_host.h" #include "server_text.h" #include #include #include // 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"). namespace dingosdk::server { namespace { constexpr std::array times{"default", "morning", "noon", "afternoon", "evening", "night", "weatherday", "weathernight"}; const char *vote_name(Host::VoteKind kind) { return kind == Host::VoteKind::map ? "Map" : kind == Host::VoteKind::kick ? "Kick" : "Time of day"; } } // namespace const VoteSetting &Host::vote_setting(VoteKind kind) const { return kind == VoteKind::map ? config_.votes.map : kind == VoteKind::kick ? config_.votes.kick : config_.votes.time; } std::uint8_t Host::enabled_votes() const { std::uint8_t bits{}; if (config_.votes.map.enabled) bits |= server_vote_map; if (config_.votes.kick.enabled) bits |= server_vote_kick; // Time of day is a world layer choice: it only reaches players while layer sync is on. if (config_.votes.time.enabled && config_.world_layer_sync && !world_layers().empty()) bits |= server_vote_time; return bits; } void Host::reply(Guest &guest, std::string_view text) { // Chat lines are single lines: longer answers (admin commands) arrive a line at a time. unsigned lines{}; for (std::size_t at = 0; at <= text.size() && lines < 12;) { const auto end = text.find('\n', at); const auto line = text.substr(at, end == std::string_view::npos ? std::string_view::npos : end - at); if (!trim(line).empty()) { send_chat(line, &guest); ++lines; } if (end == std::string_view::npos) break; at = end + 1; } } Host::Guest *Host::match_player(std::string_view text) { // A SteamID64, or the start of one connected player's name. text = trim(text); if (text.empty()) return nullptr; if (const auto id = number(split(text).first)) { auto *guest = find(*id); return guest && guest->handshaken ? guest : nullptr; } Guest *match{}; for (auto &[id, guest] : guests_) if (guest->handshaken && lower(guest_name(*guest)).starts_with(lower(text))) { if (match) return nullptr; match = guest.get(); } return match; } void Host::chat_command(Guest &guest, std::string_view line) { const auto [first, rest] = split(line); const auto verb = lower(first); if (verb.empty() || verb == "help" || verb == "?") { std::string text; const auto votes = enabled_votes(); if (votes & server_vote_map) text += "/vote map : start a vote to change the map\n"; if (votes & server_vote_kick) text += "/vote kick : start a vote to kick a player\n"; if (votes & server_vote_time) text += "/vote tod