VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
DownloadQueue.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <string>
6#include <string_view>
7#include <unordered_map>
8#include <vector>
9
10namespace VoltMod
11{
12
13/** What the hook does to one outgoing join message. */
14enum class AddonAction
15{
16 Unchanged, ///< send the message as it is
17 Send, ///< point the client at @ref AddonDecision::Id and wait for its reconnect
18 TrimToFirst, ///< the message names several addons; keep only @ref AddonDecision::Id
19 Kick, ///< the client refused @ref AddonDecision::Id too often
20 KeepMounted, ///< a map change names no addon; name @ref AddonDecision::Id so the client keeps it
21};
22
24{
27 std::size_t Remaining = 0; ///< addons still owed after Id, or cut from the message; for logging
28};
29
30/** Which required workshop addons each client still owes, and what to send it next. Keyed by SteamID,
31 * since a reconnecting client's slot changes; times are monotonic seconds. */
33{
34public:
35 /** Require @p id of every client. Reference counted; false for id 0. */
36 bool Add(uint64_t id);
37 void Remove(uint64_t id);
38
39 /** Whether nothing is required. */
40 bool Empty() const { return _required.empty(); }
41
42 /** What every client must have, in send order. */
43 std::vector<uint64_t> Required() const;
44
45 /** What @p steamId has still to download. */
46 std::vector<uint64_t> MissingFor(int64_t steamId) const;
47 bool HasMissing(int64_t steamId) const;
48
49 /** Required addons @p steamId has downloaded or is downloading: what it mounts on connect. */
50 std::vector<uint64_t> ClientMountList(int64_t steamId) const;
51
52 /** The next addon for @p steamId. A repeat offer counts an attempt; past @p maxAttempts, Kick. */
54
55 /** Decide one outgoing join message for @p steamId. A message already sending the client away
56 * (@p reconnect) goes through, and its addon counts as sending. */
57 AddonDecision DecideJoinMessage(int64_t steamId, bool reconnect, std::string_view addons, double now,
58 int maxAttempts);
59
60 /** Record that @p id reached @p steamId without our offer. Costs no attempt. */
61 void MarkSending(int64_t steamId, uint64_t id, double now);
62
63 /** Count the sending addon as downloaded if @p steamId reconnected within @p timeoutSec. */
64 void ClientReconnected(int64_t steamId, double now, double timeoutSec);
65
66 /** @p steamId left; clients gone longer than @p forgetAfterSec are forgotten. */
67 void ClientLeft(int64_t steamId, double now, double forgetAfterSec);
68
69 /** Forget every client's progress, keeping the requirements. */
70 void ClearProgress();
71
72private:
73 struct Requirement
74 {
75 uint64_t Id = 0;
76 int Holders = 0;
77 };
78
79 struct Client
80 {
81 std::vector<uint64_t> Downloaded;
82 uint64_t Sending = 0;
83 double SentAt = 0.0;
84 int Attempts = 0;
85 double LeftAt = 0.0; ///< 0 while connected
86 };
87
88 const Client* FindClient(int64_t steamId) const;
89
90 // A handful of entries kept in send order, so flat vectors.
91 std::vector<Requirement> _required;
92 std::unordered_map<int64_t, Client> _clients;
93};
94
95/** Split a join message's comma-separated `addons` field, skipping malformed entries. */
96std::vector<uint64_t> ParseAddonList(std::string_view field);
97
98/** Append each of @p ids that @p field lacks. Returns the ids appended. */
99std::vector<uint64_t> AppendToAddonList(std::string& field, const std::vector<uint64_t>& ids);
100
101/** Remove one entry per id in @p ids, keeping the other entries verbatim. */
102void RemoveFromAddonList(std::string& field, const std::vector<uint64_t>& ids);
103
104} // namespace VoltMod
bool Add(uint64_t id)
AddonDecision DecideJoinMessage(int64_t steamId, bool reconnect, std::string_view addons, double now, int maxAttempts)
bool HasMissing(int64_t steamId) const
void Remove(uint64_t id)
void ClientReconnected(int64_t steamId, double now, double timeoutSec)
std::vector< uint64_t > ClientMountList(int64_t steamId) const
void MarkSending(int64_t steamId, uint64_t id, double now)
std::vector< uint64_t > Required() const
void ClientLeft(int64_t steamId, double now, double forgetAfterSec)
AddonDecision NextToSend(int64_t steamId, double now, int maxAttempts)
std::vector< uint64_t > MissingFor(int64_t steamId) const
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
std::vector< uint64_t > AppendToAddonList(std::string &field, const std::vector< uint64_t > &ids)
void RemoveFromAddonList(std::string &field, const std::vector< uint64_t > &ids)
std::vector< uint64_t > ParseAddonList(std::string_view field)
@ TrimToFirst
the message names several addons; keep only AddonDecision::Id
@ Kick
the client refused AddonDecision::Id too often
@ Unchanged
send the message as it is
@ Send
point the client at AddonDecision::Id and wait for its reconnect
@ KeepMounted
a map change names no addon; name AddonDecision::Id so the client keeps it
std::size_t Remaining
addons still owed after Id, or cut from the message; for logging