VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
ConVar.cpp
Go to the documentation of this file.
2
4#include <format>
5#include <string>
6#include <string_view>
7#include <tier1/convar.h>
8#include <type_traits>
9
10namespace VoltMod
11{
12
13// Keep the SDK-independent type mirror aligned with the engine.
14static_assert(static_cast<int>(ConVarType::Invalid) == EConVarType_Invalid);
15static_assert(static_cast<int>(ConVarType::Bool) == EConVarType_Bool);
16static_assert(static_cast<int>(ConVarType::Int16) == EConVarType_Int16);
17static_assert(static_cast<int>(ConVarType::UInt16) == EConVarType_UInt16);
18static_assert(static_cast<int>(ConVarType::Int32) == EConVarType_Int32);
19static_assert(static_cast<int>(ConVarType::UInt32) == EConVarType_UInt32);
20static_assert(static_cast<int>(ConVarType::Int64) == EConVarType_Int64);
21static_assert(static_cast<int>(ConVarType::UInt64) == EConVarType_UInt64);
22static_assert(static_cast<int>(ConVarType::Float32) == EConVarType_Float32);
23static_assert(static_cast<int>(ConVarType::Float64) == EConVarType_Float64);
24static_assert(static_cast<int>(ConVarType::String) == EConVarType_String);
25static_assert(static_cast<int>(ConVarType::Color) == EConVarType_Color);
26static_assert(static_cast<int>(ConVarType::Vector2) == EConVarType_Vector2);
27static_assert(static_cast<int>(ConVarType::Vector3) == EConVarType_Vector3);
28static_assert(static_cast<int>(ConVarType::Vector4) == EConVarType_Vector4);
29static_assert(static_cast<int>(ConVarType::QAngle) == EConVarType_Qangle);
30static_assert(static_cast<int>(ConVarType::VectorWS) == EConVarType_VectorWS);
31
32template <ConVarValue T>
33Result<ConVar<T>> ConVars::Find(std::string_view name)
34{
35 const std::string owned(name);
37 if (!ref.IsValidRef() || !ref.IsConVarDataAvailable())
38 {
39 return std::unexpected(Error::NotFound(std::format("no convar '{}'", owned)));
40 }
41
42 const auto type = static_cast<ConVarType>(ref.GetType());
43 if (!ConVarTypeMatches<T>(type))
44 {
45 return std::unexpected(Error::Invalid(
46 std::format("convar '{}' is engine type {}, not the requested one", owned, static_cast<int>(type))));
47 }
48
49 // Prefer shared storage; some convars expose only slot 0.
50 CVValue_t* storage = ref.GetConVarData()->Value(CSplitScreenSlot(-1));
51 if (!storage)
52 {
53 storage = ref.GetConVarData()->Value(CSplitScreenSlot(0));
54 }
55 if (!storage)
56 {
57 return std::unexpected(Error::Engine(std::format("convar '{}' has no value storage", owned)));
58 }
59
61 handle._service = this;
62 handle._name = owned;
63 handle._storage = storage;
64 handle._type = ref.GetType();
65 return handle;
66}
67
68template <class T>
70{
71 if (!_storage)
72 {
73 return T{};
74 }
75
76 const auto type = static_cast<ConVarType>(_type);
77
78 if constexpr (std::is_same_v<T, bool>)
79 {
80 return _storage->m_bValue;
81 }
82 else if constexpr (std::is_same_v<T, std::string>)
83 {
84 const char* text = _storage->m_StringValue.Get();
85 return text ? std::string(text) : std::string{};
86 }
87 else if constexpr (std::is_same_v<T, float>)
88 {
89 return _storage->m_fl32Value;
90 }
91 else
92 {
93 // Read the declared width to avoid adjacent bytes.
94 switch (type)
95 {
97 return _storage->m_i16Value;
99 return _storage->m_u16Value;
100 default:
101 return _storage->m_i32Value;
102 }
103 }
104}
105
106template <class T>
108{
109 if (!_storage || !_service)
110 {
111 return std::unexpected(Error::NotReady("convar handle is unresolved"));
112 }
113
114 // ConVarText renders unquoted: the console path quotes in SetByConsole, while the
115 // replicated payload at SendToClient below must stay unquoted.
116 _service->SetByConsole(_name, ConVarText(value));
117 return {};
118}
119
120template <class T>
121void ConVar<T>::SetRaw(const T& value)
122 requires RawConVarValue<T>
123{
124 if (!_storage)
125 {
126 return;
127 }
128
129 const auto type = static_cast<ConVarType>(_type);
130 if constexpr (std::is_same_v<T, bool>)
131 {
132 _storage->m_bValue = value;
133 }
134 else if constexpr (std::is_same_v<T, float>)
135 {
136 _storage->m_fl32Value = value;
137 }
138 else
139 {
140 switch (type)
141 {
143 _storage->m_i16Value = static_cast<int16_t>(value);
144 break;
146 _storage->m_u16Value = static_cast<uint16_t>(value);
147 break;
148 default:
149 _storage->m_i32Value = value;
150 break;
151 }
152 }
153}
154
155template <class T>
156void ConVar<T>::SetFor(int slot, const T& value) const
157{
158 if (_storage && _service)
159 {
160 _service->SendToClient(slot, _name, ConVarText(value));
161 }
162}
163
164template <class T>
166 requires RawConVarValue<T>
167 : _cvar(cvar ? &cvar : nullptr), _previous(cvar.Get())
168{
169 cvar.SetRaw(value);
170}
171
172template <class T>
174{
175 if (_cvar)
176 {
177 _cvar->SetRaw(_previous);
178 }
179}
180
181// The templates above are defined only here, so these instantiations are what makes the handles
182// exist. The lists mirror the ConVarValue and RawConVarValue concepts; a type in a concept but not
183// here is a link error at the consumer rather than a silently different handle.
184template class ConVar<bool>;
185template class ConVar<int>;
186template class ConVar<float>;
187template class ConVar<std::string>;
188template class ConVarRawScope<bool>;
189template class ConVarRawScope<int>;
190template class ConVarRawScope<float>;
191template Result<ConVar<bool>> ConVars::Find<bool>(std::string_view);
192template Result<ConVar<int>> ConVars::Find<int>(std::string_view);
193template Result<ConVar<float>> ConVars::Find<float>(std::string_view);
194template Result<ConVar<std::string>> ConVars::Find<std::string>(std::string_view);
195
196} // namespace VoltMod
ConVarRawScope(ConVar< T > &cvar, const T &value)
Definition ConVar.cpp:165
T Get() const
Definition ConVar.cpp:69
Status Set(const T &value)
Definition ConVar.cpp:107
void SetFor(int slot, const T &value) const
Definition ConVar.cpp:156
Result< ConVar< T > > Find(std::string_view name)
Definition ConVar.cpp:33
std::string ConVarText(const T &value)
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
std::expected< void, Error > Status
Definition Result.hpp:67
std::expected< T, Error > Result
Definition Result.hpp:64
static Error Invalid(std::string detail)
Definition Result.hpp:51
static Error Engine(std::string detail)
Definition Result.hpp:58
static Error NotFound(std::string detail)
Definition Result.hpp:49
static Error NotReady(std::string detail)
Definition Result.hpp:50