Files
Robin MoellerandClaude Opus 5.5 07cd31b8aa Votes: the same answer in the next poll is no longer turned away as a repeat
Chat's flood control refuses a line identical to the one before it within 15 s, which also
caught /2 in a poll right after /2 in the last one. An answer to the running vote or poll is now
exempt from that one rule, on the server and in the game; the pace still holds, and the vote
still counts one answer per player.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 02:02:56 +02:00

38 lines
1.5 KiB
C++

#pragma once
#include <algorithm>
#include <cstdint>
#include <string>
#include <string_view>
namespace dingosdk {
// Chat flood control, the same for the sender, the host, a dedicated server and every
// receiver: a few lines at once, then one every 1.5 s, and never the same line twice within
// 15 s. Receivers allow one line of slack for lines that arrive bunched up. An answer to the
// running vote or poll may repeat the line before it (`answer`): the same /2 is a new answer
// in the next poll, and the vote itself counts one answer per player. The pace still holds.
struct ChatRate {
static constexpr double burst = 4, interval_us = 1500000;
static constexpr std::uint64_t repeat_us = 15000000;
enum class Verdict { accepted, too_fast, repeated };
double tokens = burst;
std::uint64_t refilled{}, previous_at{};
std::string previous;
Verdict accept(std::uint64_t now, std::string_view text, double slack = 0, bool answer = false) {
const double limit = burst + slack;
if (!refilled || now < refilled) {
tokens = limit;
} else {
tokens = std::min(limit, tokens + static_cast<double>(now - refilled) / interval_us);
}
refilled = now;
if (!answer && !previous.empty() && text == previous && now >= previous_at && now - previous_at < repeat_us)
return Verdict::repeated;
if (tokens < 1) return Verdict::too_fast;
tokens -= 1;
previous.assign(text);
previous_at = now;
return Verdict::accepted;
}
};
} // namespace dingosdk