VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
Messaging and chat input

Table of Contents

Messages

One service covers every message destination. See Messages and chat colors for colors, translation keys and broadcasts.

auto& msg = runtime.Messages;
msg.Send(slot, "Hello!"); // chat line
msg.Send(slot, "Look up", VoltMod::MessageKind::Center); // plain center print
msg.Send(slot, "<b>Notice</b>", VoltMod::MessageKind::CenterHtml);
msg.Broadcast("Map change in 60s", VoltMod::MessageKind::Alert);
msg.SendKey(slot, "punish.banned", {{"admin", name}}); // translated for the player's language
msg.ClearCenterHtml(slot);
msg.Shake(slot, 1.0f, 40.0f, 8.0f); // duration, frequency, amplitude
@ Center
Plain center-screen print.
@ CenterHtml
Center HTML panel (same channel the menu system renders into).
@ Alert
Top-center alert bar.

CenterHtml

CS2 drops center HTML almost immediately after a death, a team switch or a HUD update, so a sticky panel has to be re-sent. VoltMod::CenterHtml owns that loop and nothing else; the deadline or expiry policy is yours.

// A member of your plugin object; the services belong to the runtime. A panel stops when its player leaves.
VoltMod::CenterHtml panel{runtime.Messages, runtime.Scheduler, runtime.Slots};
panel.Show(slot, /*refreshMs=*/100, [](int s) {
return std::format("<b>Time left: {}s</b>", RemainingSeconds(s)); // re-rendered every refresh
});
panel.Stop(slot); // cancel and clear the panel
Re-sends a center-HTML panel until stopped, since CS2 drops center HTML on death, a team switch and H...
void Show(int slot, int refreshMs, std::function< std::string(int slot)> render)

ChatInput

A per-slot prompt registry. It powers menu text input and can be used directly.

runtime.ChatInput.BeginCapture(slot, "Enter your nickname:",
[](int s, std::string_view text) -> bool {
if (text.size() > 32)
return false; // re-prompt
StoreNickname(s, std::string(text));
return true; // accept and clear the capture
},
/*timeoutMs=*/30000);
Method What it does
BeginCapture(slot, prompt, callback, timeoutMs = 60000) Wait for the slot's next chat line, replacing any pending prompt. A positive timeout cancels the capture.
IsCapturing(slot) Whether a prompt is pending.
CancelCapture(slot) Drop the prompt without firing the callback.
GetPrompt(slot) A copy of the active prompt as std::optional<std::string>.

The service subscribes to VoltMod::SlotEvents itself, so a pending prompt is cancelled when the slot changes hands.

The framework consumes active prompts before it dispatches commands, and raises runtime.Players.Said only for a line neither took. A plugin with its own chat rules subscribes to that and sets Blocked to keep the line out of chat:

_subs.Add(runtime.Players.Said += [this](VoltMod::ChatMessage& chat) {
if (IsMuted(chat.Sender.SteamId()))
chat.Blocked = true;
});

Vote

VoltMod::Vote draws the engine's yes/no panel with user messages and counts the ballots itself, from the vote command the panel's F1/F2 keys send. The panel is the engine's, so the title must be a #SFUI_vote... or #Panorama_vote... token the client already has; arbitrary text does not render. Only one vote runs at a time, open to every connected human; bots get no ballot.

runtime.Vote.Start({
.Title = "#SFUI_vote_changelevel",
.Detail = "Dust II", // the token's detail string
.DurationMs = 20000, // before it closes itself
.Caller = callerSlot, // whose name the panel credits; -1 for the server
.Passed = [](const VoltMod::VoteTally& tally) {
// Judging on ballots cast rather than everyone connected means abstaining is not a no.
return tally.Cast() > 0 && tally.Yes * 2 > tally.Cast();
},
.Finished = [](bool passed, VoltMod::VoteEndReason reason) { /* act on the outcome */ },
});
runtime.Vote.InProgress();
runtime.Vote.End(VoltMod::VoteEndReason::Cancelled); // call one off early
VoteEndReason
Definition Vote.hpp:24
@ Cancelled
an admin (or a peer feature) called it off

Start returns false when a vote is already running or no human is connected. A player who leaves without voting stops counting as eligible. VoteEndReason is AllVoted, TimeUp or Cancelled. Every callback runs on the game thread.