2#include "gaia/config/config.h"
17 inline bool runtime_patch_decode_token(ser::json_str_view encoded, util::str& token) {
20 GAIA_FOR(encoded.size()) {
21 const auto ch = encoded.data()[i];
27 if (i + 1 >= encoded.size())
30 const auto escaped = encoded.data()[++i];
33 else if (escaped ==
'1')
46 inline bool runtime_patch_parse_index(util::str_view token, uint32_t& index) {
51 GAIA_FOR(token.size()) {
52 const auto ch = token.data()[i];
53 if (ch <
'0' || ch >
'9')
55 value = value * 10 + (uint32_t)(ch -
'0');
56 if (value > UINT32_MAX)
60 index = (uint32_t)value;
74 runtime_patch_numeric_value_as(Entity type, Entity expectedType,
const void* data, uint32_t size,
double& value) {
75 if (type != expectedType || data ==
nullptr || size !=
sizeof(T))
79 memcpy(&result, data,
sizeof(result));
80 value = (double)result;
91 inline bool runtime_patch_numeric_value(
92 const ComponentCacheItem* pType, Entity type,
const void* data, uint32_t size,
double& value) {
93 if (pType !=
nullptr &&
94 (pType->typeKind == RuntimeTypeKind::Enum || pType->typeKind == RuntimeTypeKind::Bitmask))
95 type = pType->underlyingType;
97 return runtime_patch_numeric_value_as<int8_t>(type, S8, data, size, value) ||
98 runtime_patch_numeric_value_as<uint8_t>(type, U8, data, size, value) ||
99 runtime_patch_numeric_value_as<int16_t>(type, S16, data, size, value) ||
100 runtime_patch_numeric_value_as<uint16_t>(type, U16, data, size, value) ||
101 runtime_patch_numeric_value_as<int32_t>(type, S32, data, size, value) ||
102 runtime_patch_numeric_value_as<uint32_t>(type, U32, data, size, value) ||
103 runtime_patch_numeric_value_as<int64_t>(type, S64, data, size, value) ||
104 runtime_patch_numeric_value_as<uint64_t>(type, U64, data, size, value) ||
105 runtime_patch_numeric_value_as<float>(type, F32, data, size, value) ||
106 runtime_patch_numeric_value_as<double>(type, F64, data, size, value);
111 inline bool World::patch_comp_json(
112 Entity entity, Entity component, ser::json_str_view pointer, ser::json_str_view value,
113 ser::JsonDiagnostics& diagnostics,
const ser::RuntimeJsonPolicy& policy, uint64_t expectedRuntimeSchemaHash) {
114 auto error = [&](ser::JsonDiagReason reason,
const char* message) {
115 diagnostics.add(ser::JsonDiagSeverity::Error, reason, pointer, message);
119 if (expectedRuntimeSchemaHash != 0 && expectedRuntimeSchemaHash != runtime_schema_hash())
120 return error(ser::JsonDiagReason::StaleSchema,
"Runtime schema hash does not match the current manifest.");
122 const auto* pRoot = component.pair() ? m_compCache.find_pair_payload(component) : m_compCache.find(component);
123 if (pRoot ==
nullptr)
124 return error(ser::JsonDiagReason::UnknownComponent,
"Component patch target is not registered.");
128 return error(ser::JsonDiagReason::MissingComponentStorage,
"Component patch payload is unavailable.");
129 if (!pointer.empty() && pointer.data()[0] !=
'/')
130 return error(ser::JsonDiagReason::InvalidPatchPath,
"Component patch path must be an RFC 6901 JSON Pointer.");
131 if (!pointer.empty() && (pointer.size() == 1 || pointer.data()[pointer.size() - 1] ==
'/'))
132 return error(ser::JsonDiagReason::InvalidPatchPath,
"Component patch path contains an empty token.");
134 const ComponentCacheItem* pCurrent = pRoot;
135 const RuntimeFieldDesc* pSelectedField =
nullptr;
136 uint32_t pos = pointer.empty() ? pointer.size() : 1;
137 while (pos < pointer.size()) {
139 while (end < pointer.size() && pointer.data()[end] !=
'/')
143 if (!detail::runtime_patch_decode_token(ser::json_str_view(pointer.data() + pos, end - pos), token) ||
145 return error(ser::JsonDiagReason::InvalidPatchPath,
"Component patch path contains an invalid token.");
147 const ComponentCacheItem* pFieldOwner = pCurrent;
148 if (pFieldOwner !=
nullptr && pFieldOwner->typeKind == RuntimeTypeKind::Opaque)
149 pFieldOwner = m_compCache.find(pFieldOwner->opaque_as_type());
151 if (pFieldOwner !=
nullptr && pFieldOwner->typeKind == RuntimeTypeKind::Struct) {
152 const auto* pField = pFieldOwner->field(util::str_view(token.data(), token.size()));
153 if (pField ==
nullptr)
154 return error(ser::JsonDiagReason::InvalidPatchPath,
"Component patch field does not exist.");
155 if ((pField->flags & RuntimeFieldFlag_ReadOnly) != 0)
156 return error(ser::JsonDiagReason::ReadOnlyField,
"Component patch field is read-only.");
157 if ((pField->flags & RuntimeFieldFlag_Hidden) != 0)
158 return error(ser::JsonDiagReason::HiddenField,
"Component patch field is hidden.");
159 if (!
cursor.
field(util::str_view(token.data(), token.size())))
160 return error(ser::JsonDiagReason::InvalidPatchPath,
"Component patch field cannot be traversed.");
162 pSelectedField = pField;
163 pCurrent = m_compCache.find(pField->type);
166 if (!detail::runtime_patch_parse_index(util::str_view(token.data(), token.size()), index) ||
168 return error(ser::JsonDiagReason::InvalidPatchPath,
"Component patch sequence index is invalid.");
177 const bool charBuffer = pSelectedField !=
nullptr &&
cursor.
type() == Char8 && count.ok() && count.
value > 1;
179 if (pCurrent ==
nullptr || !detail::runtime_json_leaf_editable(*pCurrent) ||
180 (count.ok() && count.value > 1 && !charBuffer))
182 ser::JsonDiagReason::UnsupportedPatchValue,
"Component patch endpoint must be a supported reflected leaf.");
185 cnt::darray<uint8_t> original;
187 if (!
cursor.
get_raw(original.data(), (uint32_t)original.size()))
188 return error(ser::JsonDiagReason::UnsupportedPatchValue,
"Component patch endpoint cannot be read.");
190 cnt::darray<uint8_t>
bytes;
191 bytes.resize(original.size());
192 memcpy(
bytes.data(), original.data(), original.size());
194 ser::ser_json reader(value.data(), value.size());
196 detail::RuntimeJsonReadContext ctx{&m_compCache, reader, diagnostics, policy, valueOk};
200 ser::json_str_view text;
201 if (!reader.parse_string_view(text) || text.size() != 1)
202 return error(ser::JsonDiagReason::InvalidJson,
"Component patch character value must contain one character.");
204 bytes[0] = (uint8_t)text.data()[0];
205 }
else if (!detail::read_runtime_json_value(
208 ser::JsonDiagReason::InvalidJson,
"Component patch value is not valid JSON for the selected field.");
213 return error(ser::JsonDiagReason::InvalidJson,
"Component patch value contains trailing JSON.");
217 ser::JsonDiagReason::UnsupportedPatchValue,
218 "Component patch value is incompatible with the selected field.");
220 if (pSelectedField !=
nullptr &&
221 (pSelectedField->flags & (RuntimeFieldFlag_HasMinimum | RuntimeFieldFlag_HasMaximum)) != 0) {
225 ser::JsonDiagReason::UnsupportedPatchValue,
"Component patch range applies to a non-numeric field.");
227 if (((pSelectedField->flags & RuntimeFieldFlag_HasMinimum) != 0 && number < pSelectedField->minimum) ||
228 ((pSelectedField->flags & RuntimeFieldFlag_HasMaximum) != 0 && number > pSelectedField->maximum))
230 ser::JsonDiagReason::RangeViolation,
"Component patch value is outside the authored field range.");
235 (void)
cursor.
set_raw(original.data(), (uint32_t)original.size(),
false);
237 ser::JsonDiagReason::UnsupportedPatchValue,
"Component patch could not commit the selected field.");
GAIA_NODISCARD ComponentCursor cursor(Entity entity, Entity component) const
Creates a read-only cursor over a runtime component on entity. Inherited ids resolve like get_raw()....
Definition world.h:13051
GAIA_NODISCARD ComponentCursor cursor_mut(Entity entity, Entity component)
Creates a mutable cursor over a directly owned runtime component on entity. Direct writes through mut...
Definition world.h:13067
GAIA_NODISCARD uint32_t bytes(const T &data)
Calculates how many bytes data would need when serialized via ser::save. Useful when a destination st...
Definition ser_ct.h:88
bool field(uint32_t index)
Descends into the reflected field at index.
Definition component_cursor.h:394
GAIA_NODISCARD CursorResult< uint32_t > get_raw(void *data, uint32_t byteCount) const noexcept
Copies exact bytes from the current cursor position.
Definition component_cursor.h:798
GAIA_NODISCARD uint32_t size() const noexcept
Returns the current payload or field size in bytes.
Definition component_cursor.h:317
GAIA_NODISCARD bool valid() const noexcept
Definition component_cursor.h:280
GAIA_NODISCARD Entity type() const noexcept
Definition component_cursor.h:290
GAIA_NODISCARD CursorResult< uint32_t > count() const noexcept
Returns the element count for the current fixed or adapted sequence scope.
Definition component_cursor.h:347
bool elem(uint32_t index) noexcept
Descends into element index of the current fixed inline or named array scope.
Definition component_cursor.h:435
CursorResult< void > set_raw(const void *data, uint32_t byteCount) noexcept
Writes exact bytes to the current cursor position.
Definition component_cursor.h:828
GAIA_NODISCARD constexpr auto value() const noexcept
Raw identifier value.
Definition id.h:395