载入中...
搜索中...
未找到
PointGraphAsset.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <iomanip>
5#include <functional>
6#include <sstream>
7
8namespace eve::procgen {
9namespace {
10
11std::string hexEncode(const std::string& value) {
12 static constexpr char digits[] = "0123456789abcdef";
13 std::string result;
14 result.reserve(value.size() * 2);
15 for (const unsigned char byte : value) {
16 result.push_back(digits[byte >> 4u]);
17 result.push_back(digits[byte & 0x0fu]);
18 }
19 return result;
20}
21
22int hexDigit(char value) {
23 if (value >= '0' && value <= '9') return value - '0';
24 if (value >= 'a' && value <= 'f') return value - 'a' + 10;
25 if (value >= 'A' && value <= 'F') return value - 'A' + 10;
26 return -1;
27}
28
29bool hexDecode(const std::string& value, std::string& result) {
30 if (value.size() % 2 != 0) return false;
31 result.clear();
32 result.reserve(value.size() / 2);
33 for (size_t i = 0; i < value.size(); i += 2) {
34 const int high = hexDigit(value[i]);
35 const int low = hexDigit(value[i + 1]);
36 if (high < 0 || low < 0) return false;
37 result.push_back(char((high << 4) | low));
38 }
39 return true;
40}
41
42} // namespace
43
45 std::ostringstream out;
46 out << "EVPCG_POINT_GRAPH 1\n";
47 out << std::setprecision(9);
48 for (const auto& id : nodeOrder_) {
49 const auto& node = nodes_.at(id);
50 out << "NODE " << std::quoted(id) << ' ' << std::quoted(node.operation) << '\n';
51 std::vector<std::string> keys;
52 keys.reserve(node.floats.size());
53 for (const auto& [key, value] : node.floats) keys.push_back(key);
54 std::sort(keys.begin(), keys.end());
55 for (const auto& key : keys)
56 out << "FLOAT " << std::quoted(id) << ' ' << std::quoted(key) << ' '
57 << node.floats.at(key) << '\n';
58 keys.clear();
59 keys.reserve(node.ints.size());
60 for (const auto& [key, value] : node.ints) keys.push_back(key);
61 std::sort(keys.begin(), keys.end());
62 for (const auto& key : keys)
63 out << "INT " << std::quoted(id) << ' ' << std::quoted(key) << ' '
64 << node.ints.at(key) << '\n';
65 keys.clear();
66 keys.reserve(node.strings.size());
67 for (const auto& [key, value] : node.strings) keys.push_back(key);
68 std::sort(keys.begin(), keys.end());
69 for (const auto& key : keys)
70 out << "STRING " << std::quoted(id) << ' ' << std::quoted(key) << ' '
71 << std::quoted(node.strings.at(key)) << '\n';
72 if (node.subgraph) {
73 out << "SUBGRAPH " << std::quoted(id) << ' ' << std::quoted(node.subgraphInput) << ' '
74 << std::quoted(node.subgraphOutput) << ' '
75 << std::quoted(hexEncode(node.subgraph->serializeDefinition())) << '\n';
76 }
77 }
78 std::vector<std::string> parameterNames = parameterOrder_;
79 std::sort(parameterNames.begin(), parameterNames.end());
80 for (const auto& name : parameterNames) {
81 const auto& parameter = parameters_.at(name);
82 out << "PARAM " << std::quoted(name) << ' ' << std::quoted(parameter.nodeId) << ' '
83 << std::quoted(parameter.key) << '\n';
84 }
85 for (const auto& id : nodeOrder_) {
86 const auto& node = nodes_.at(id);
87 for (int input = 0; input < 2; ++input)
88 if (!node.inputs[input].empty())
89 out << "EDGE " << std::quoted(node.inputs[input]) << ' ' << std::quoted(id) << ' '
90 << input << '\n';
91 }
92 out << "END\n";
93 return out.str();
94}
95
96bool PointGraph::deserializeDefinition(const std::string& definition) {
97 PointGraph replacement;
98 std::istringstream input(definition);
99 std::string magic;
100 int version = 0;
101 if (!(input >> magic >> version) || magic != "EVPCG_POINT_GRAPH" || version != 1) {
102 error_ = "invalid point graph header";
103 return false;
104 }
105 std::string line;
106 std::getline(input, line);
107 bool ended = false;
108 while (std::getline(input, line)) {
109 if (line.empty()) continue;
110 std::istringstream record(line);
111 std::string kind;
112 record >> kind;
113 if (kind == "END") {
114 ended = true;
115 break;
116 }
117 std::string id;
118 if (kind == "NODE") {
119 std::string operation;
120 if (!(record >> std::quoted(id) >> std::quoted(operation)) ||
121 !replacement.addNode(id, operation)) {
122 error_ = "invalid NODE record";
123 return false;
124 }
125 } else if (kind == "EDGE") {
126 std::string to;
127 int slot = -1;
128 if (!(record >> std::quoted(id) >> std::quoted(to) >> slot) ||
129 !replacement.connect(id, to, slot)) {
130 error_ = "invalid EDGE record";
131 return false;
132 }
133 } else if (kind == "FLOAT") {
134 std::string key;
135 float value = 0.f;
136 if (!(record >> std::quoted(id) >> std::quoted(key) >> value) ||
137 !replacement.setNodeFloat(id, key, value)) {
138 error_ = "invalid FLOAT record";
139 return false;
140 }
141 } else if (kind == "INT") {
142 std::string key;
143 int value = 0;
144 if (!(record >> std::quoted(id) >> std::quoted(key) >> value) ||
145 !replacement.setNodeInt(id, key, value)) {
146 error_ = "invalid INT record";
147 return false;
148 }
149 } else if (kind == "STRING") {
150 std::string key;
151 std::string value;
152 if (!(record >> std::quoted(id) >> std::quoted(key) >> std::quoted(value)) ||
153 !replacement.setNodeString(id, key, value)) {
154 error_ = "invalid STRING record";
155 return false;
156 }
157 } else if (kind == "SUBGRAPH") {
158 std::string nestedInput;
159 std::string nestedOutput;
160 std::string nestedEncoded;
161 std::string nestedDefinition;
162 if (!(record >> std::quoted(id) >> std::quoted(nestedInput) >>
163 std::quoted(nestedOutput) >> std::quoted(nestedEncoded)) ||
164 !hexDecode(nestedEncoded, nestedDefinition)) {
165 error_ = "invalid SUBGRAPH record";
166 return false;
167 }
168 PointGraph nested;
169 if (!nested.deserializeDefinition(nestedDefinition) ||
170 !replacement.setNodeSubgraph(id, &nested, nestedInput, nestedOutput)) {
171 error_ = "invalid nested graph at " + id + ": " + nested.getError();
172 return false;
173 }
174 } else if (kind == "PARAM") {
175 std::string nodeId;
176 std::string key;
177 if (!(record >> std::quoted(id) >> std::quoted(nodeId) >> std::quoted(key)) ||
178 !replacement.exposeParameter(id, nodeId, key)) {
179 error_ = "invalid PARAM record";
180 return false;
181 }
182 } else {
183 error_ = "unknown graph record: " + kind;
184 return false;
185 }
186 record >> std::ws;
187 if (!record.eof()) {
188 error_ = "trailing graph record data: " + kind;
189 return false;
190 }
191 }
192 if (!ended) {
193 error_ = "point graph END record is missing";
194 return false;
195 }
196
197 // Structural cycle check without requiring runtime PointSet/SpatialData slots.
198 std::unordered_map<std::string, int> states;
199 std::function<bool(const std::string&)> visit = [&](const std::string& id) {
200 if (states[id] == 2) return true;
201 if (states[id] == 1) return false;
202 states[id] = 1;
203 const auto found = replacement.nodes_.find(id);
204 if (found == replacement.nodes_.end()) return false;
205 for (const auto& dependency : found->second.inputs)
206 if (!dependency.empty() && !visit(dependency)) return false;
207 states[id] = 2;
208 return true;
209 };
210 for (const auto& id : replacement.nodeOrder_) {
211 if (!visit(id)) {
212 error_ = "cycle in serialized graph at node: " + id;
213 return false;
214 }
215 }
216 *this = std::move(replacement);
217 error_.clear();
218 return true;
219}
220
222 auto* instance = new PointGraph();
223 if (instance->deserializeDefinition(serializeDefinition())) return instance;
224 delete instance;
225 return nullptr;
226}
227
228} // namespace eve::procgen
ActionParameterOperation operation
double value
std::string nodeId
EvpackChunkInput input
Definition Evpack.cpp:170
std::uint32_t key
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
TokenKind kind
bool ended
std::string name
std::string id
Definition PlayHost.cpp:108
const RoadNode * node
bool found
EVENGINE_API_DOMAINS public API.
Definition PointGraph.h:66
bool setNodeFloat(const std::string &id, const std::string &key, float value)
Sets the node float.
PointGraph * instantiate() const
Create an independent runtime instance without external inputs or overrides.
bool exposeParameter(const std::string &name, const std::string &nodeId, const std::string &key)
Expose one reflected node parameter for per-instance overrides.
bool setNodeString(const std::string &id, const std::string &key, const std::string &value)
Sets the node string.
bool connect(const std::string &fromId, const std::string &toId, int inputIndex=0)
Connect an output to input slot 0 or 1. Replaces that input connection.
std::string serializeDefinition() const
Serialize graph topology, parameters and nested graphs.
bool deserializeDefinition(const std::string &definition)
Transactionally replace this graph from serializeDefinition output.
bool setNodeSubgraph(const std::string &id, PointGraph *graph, const std::string &inputNode, const std::string &outputNode)
Assign an immutable nested graph.
std::string getError() const
Render the last compatibility execute/validate or legacy authoring error; canonical Result calls do n...
bool setNodeInt(const std::string &id, const std::string &key, int value)
Sets the node int.
bool addNode(const std::string &id, const std::string &operation)
Add a node using one of the operations returned by operationAt().