VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
SchemaDump.cpp
Go to the documentation of this file.
2
4
8#include <cstdint>
9#include <map>
10#include <optional>
11#include <schemasystem/schemasystem.h>
12#include <string>
13#include <string_view>
14#include <vector>
15
16namespace VoltMod::Schema
17{
18
19// The document `voltmod framework schemagen` reads; member names are its JSON keys.
20
21/** `inner` is a pointer, array or templated atomic's element type. */
23{
24 std::string name;
25 std::string category;
26 std::optional<std::string> atomic;
27 std::optional<std::string> inner;
28 std::optional<int32_t> extent;
29};
30
32{
33 std::string name;
37 bool networked = false;
38};
39
41{
43 std::string base; // empty at the root; schema offsets flatten single inheritance
45 std::vector<FieldInfo> fields;
46};
47
49{
50 std::string name;
52};
53
55{
57 std::vector<EnumItem> items;
58};
59
60/** Ordered maps keep the file stable. */
62{
63 std::string build; // steam.inf ServerVersion
64 std::map<std::string, ClassInfo> classes;
65 std::map<std::string, EnumInfo> enums;
66};
67
68static std::string TypeName(CSchemaType* type)
69{
70 if (!type || !type->m_sTypeName.Get())
71 {
72 return {};
73 }
74 return type->m_sTypeName.Get();
75}
76
78{
79 if (!type)
80 {
81 return {};
82 }
83
84 TypeInfo out{.name = TypeName(type), .category = std::string(Name(type->m_eTypeCategory))};
85 switch (type->m_eTypeCategory)
86 {
88 out.inner = TypeName(static_cast<CSchemaType_Ptr*>(type)->m_pObjectType);
89 break;
91 {
92 auto* array = static_cast<CSchemaType_FixedArray*>(type);
93 out.inner = TypeName(array->m_pElementType);
94 out.extent = array->m_nElementCount;
95 break;
96 }
98 out.atomic = std::string(Name(type->m_eAtomicCategory));
99 // Only templated atomics have an element type.
100 if (type->m_eAtomicCategory == SCHEMA_ATOMIC_T || type->m_eAtomicCategory == SCHEMA_ATOMIC_COLLECTION_OF_T ||
101 type->m_eAtomicCategory == SCHEMA_ATOMIC_TT)
102 {
103 out.inner = TypeName(static_cast<CSchemaType_Atomic_T*>(type)->m_pTemplateType);
104 }
105 break;
106 default:
107 break;
108 }
109 return out;
110}
111
112/** @p network is null for a class the engine does not network. */
114{
115 const CSchemaClassInfo* base = klass->m_nBaseClassCount > 0 ? klass->m_pBaseClasses[0].m_pClass : nullptr;
116 // A non-negative offset enables a NotifyComponentOwner setter.
118
119 ClassInfo out{.size = klass->m_nSize,
120 .base = base && base->m_pszName ? base->m_pszName : "",
121 .chain_offset = chain ? chain->m_nSingleInheritanceOffset : -1};
122
123 out.fields.reserve(klass->m_nFieldCount);
124 for (uint16_t i = 0; i < klass->m_nFieldCount; ++i)
125 {
126 const auto& field = klass->m_pFields[i];
127 if (!field.m_pszName)
128 {
129 continue;
130 }
131
132 int size = 0;
133 uint8_t alignment = 0;
134 if (field.m_pType)
135 {
136 field.m_pType->GetSizeAndAlignment(size, alignment);
137 }
138
139 out.fields.push_back({.name = field.m_pszName,
140 .offset = field.m_nSingleInheritanceOffset,
141 .size = size,
142 .type = DescribeType(field.m_pType),
143 .networked = network && network->FindField(field.m_pszName)});
144 }
145
146 return out;
147}
148
150{
151 EnumInfo out{.size = enumeration->m_nSize};
152 out.items.reserve(enumeration->m_nEnumeratorCount);
153
154 for (uint16_t i = 0; i < enumeration->m_nEnumeratorCount; ++i)
155 {
156 const auto& item = enumeration->m_pEnumerators[i];
157 if (item.m_pszName)
158 {
159 out.items.push_back({.name = item.m_pszName, .value = item.m_nValue});
160 }
161 }
162
163 return out;
164}
165
167 const char* name)
168{
169 const auto index = network.m_ClassInfos.Find(name);
170 return index == network.m_ClassInfos.InvalidIndex() ? nullptr : network.m_ClassInfos.Element(index);
171}
172
173template <class T, class Hash>
174static std::vector<T> HashElements(Hash& hash)
175{
176 const int count = hash.Count();
177 if (count <= 0)
178 {
179 return {};
180 }
181
182 std::vector<UtlTSHashHandle_t> handles(static_cast<size_t>(count));
183 const int written = hash.GetElements(0, count, handles.data());
184
185 std::vector<T> out;
186 out.reserve(static_cast<size_t>(written));
187 for (int i = 0; i < written; ++i)
188 {
189 out.push_back(hash.Element(handles[static_cast<size_t>(i)]));
190 }
191 return out;
192}
193
195{
197 {
198 if (klass && klass->m_pszName)
199 {
200 doc.classes.insert_or_assign(klass->m_pszName,
202 }
203 }
204
206 {
207 if (enumeration && enumeration->m_pszName)
208 {
209 doc.enums.insert_or_assign(enumeration->m_pszName, DescribeEnum(enumeration));
210 }
211 }
212}
213
215 const CNetworkSerializerCodeGenDatabase& network, std::string_view path,
216 std::string_view gameBuild)
217{
218 SchemaDoc doc{.build = std::string(gameBuild)};
219
220 // Server definitions override global ones.
221 if (global)
222 {
224 }
225 MergeScope(doc, server, network);
226
227 const std::string text = Json::WritePretty(doc);
228 if (text.empty())
229 {
230 return std::unexpected(Error::Invalid("failed to serialize the schema document"));
231 }
232 return WriteAllText(path, text + "\n");
233}
234
235} // namespace VoltMod::Schema
Enumerator names without a hand-written switch.
static std::string WritePretty(const T &value)
Serialize value as prettified JSON.
Definition Json.hpp:89
static std::vector< T > HashElements(Hash &hash)
const SchemaClassFieldData_t * FindField(const CSchemaClassInfo *klass, std::string_view field)
static void MergeScope(SchemaDoc &doc, CSchemaSystemTypeScope *scope, const CNetworkSerializerCodeGenDatabase &network)
static EnumInfo DescribeEnum(const CSchemaEnumInfo *enumeration)
static TypeInfo DescribeType(CSchemaType *type)
static std::string TypeName(CSchemaType *type)
static ClassInfo DescribeClass(const CSchemaClassInfo *klass, const CNetworkSerializerClassInfo *network)
Status WriteDumpFile(CSchemaSystemTypeScope *global, CSchemaSystemTypeScope *server, const CNetworkSerializerCodeGenDatabase &network, std::string_view path, std::string_view gameBuild)
constexpr std::string_view ChainField
static const CNetworkSerializerClassInfo * NetworkClass(const CNetworkSerializerCodeGenDatabase &network, const char *name)
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
std::expected< void, Error > Status
Definition Result.hpp:67
Status WriteAllText(std::string_view path, std::string_view text)
Write text to path (resolved via ResolvePath), creating its directory.
Definition File.cpp:24
static Error Invalid(std::string detail)
Definition Result.hpp:51
std::vector< FieldInfo > fields
std::vector< EnumItem > items
std::map< std::string, ClassInfo > classes
std::map< std::string, EnumInfo > enums
std::optional< std::string > inner
std::optional< int32_t > extent
std::optional< std::string > atomic