16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-19 03:09:18 +02:00
Files
maszyna/eu07-parser/include/eu07/scene/node/parse_util.hpp
maj00r beacc00932 Add headless parallel eu7v2 scenario bake with streaming and PLCE placements
Enable --eu7v2-bake from the main binary: parallel module pool, bounded-RAM
spool flush, streaming terrain triangles, flat include/model parsing, and
eu7v2 emit/load with optional verify. Large placement .scm files emit lean
PLCE records and bake referenced .inc modules separately for reuse.

- CLI: --eu7v2-bake, --eu7v2-verify, --eu7v2-mem-limit-gb, --eu7v2-threads,
  --eu7v2-max-parse; wire max_threads through to the bake parser
- eu7v2 v2 records: PLCE placements, runtime emitter/loader, batch verify
- Parallel bake pool with session cache; drop heavy-serial parse gate in spool
  mode; parse concurrency matches thread count
- Streaming terrain: batched parallel parse+bake, scan/bake pipeline, shape
  spool with persistent buffered I/O and flush-before-read
- Parallel flat-file streaming for models/includes; pack/model spool for
  low-memory incremental flush
- Optional 50 GB private-bytes guard during headless bake

Braniewo_szeroki: 160 modules, verify PASS, ~34s bake (nmt100 ~17s vs ~190s
serial baseline).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 21:15:42 +02:00

193 lines
5.5 KiB
C++

#pragma once
#include <eu07/parser.hpp>
#include <eu07/scene/cursor.hpp>
#include <eu07/scene/include_resolve.hpp>
#include <eu07/scene/match.hpp>
#include <eu07/scene/node/types.hpp>
#include <charconv>
#include <optional>
#include <string>
#include <string_view>
#include <system_error>
#include <vector>
namespace eu07::scene::node::io {
[[nodiscard]] inline bool isEndMarkerFor(
const std::string_view token,
const std::string_view subtype,
const std::string_view endMarker) noexcept {
if (isKeyword(token, endMarker)) {
return true;
}
if (isKeyword(subtype, "triangle_strip") && isKeyword(token, "endtri")) {
return true;
}
if (isKeyword(subtype, "triangles") &&
(isKeyword(token, "endtri") || isKeyword(token, "endtriangles"))) {
return true;
}
if (isKeyword(subtype, "triangle_fan") && isKeyword(token, "endtri")) {
return true;
}
if (isKeyword(subtype, "lines") &&
(isKeyword(token, "endlines") || isKeyword(token, "endline"))) {
return true;
}
if (isKeyword(subtype, "line_strip") &&
(isKeyword(token, "endline_strip") || isKeyword(token, "endline"))) {
return true;
}
if (isKeyword(subtype, "line_loop") &&
(isKeyword(token, "endline_loop") || isKeyword(token, "endline"))) {
return true;
}
if (isKeyword(subtype, "eventlauncher") &&
(isKeyword(token, "end") || isKeyword(token, endMarker))) {
return true;
}
return false;
}
inline void appendRaw(std::vector<SourceToken>& raw, const SourceToken& token) {
raw.push_back(token);
}
[[nodiscard]] inline bool isMissingNumericToken(const std::string_view text) noexcept {
return text.empty() || isKeywordIgnoreCase(text, "none") ||
isKeywordIgnoreCase(text, "undefined");
}
[[nodiscard]] inline std::optional<double> parseDouble(const std::string_view text) {
if (isMissingNumericToken(text)) {
return 0.0;
}
// std::from_chars: bez alokacji std::string, bez wyjatkow, niezalezne od locale
// (separator dziesietny zawsze '.', co odpowiada formatowi EU07).
std::string_view body = text;
// std::stod akceptowal wiodacy '+'; from_chars nie — zachowujemy zgodnosc.
if (!body.empty() && body.front() == '+') {
body.remove_prefix(1);
}
double value = 0.0;
const char* const first = body.data();
const char* const last = body.data() + body.size();
const std::from_chars_result result =
std::from_chars(first, last, value, std::chars_format::general);
if (result.ec != std::errc{} || result.ptr != last) {
return std::nullopt;
}
return value;
}
[[nodiscard]] inline bool takeToken(
TokenStream& stream,
std::vector<SourceToken>& raw,
SourceToken& out) {
if (stream.empty()) {
return false;
}
out = stream.consume();
appendRaw(raw, out);
return true;
}
[[nodiscard]] inline bool takeString(
TokenStream& stream,
std::vector<SourceToken>& raw,
std::string& out) {
SourceToken token;
if (!takeToken(stream, raw, token)) {
return false;
}
out = token.value;
return true;
}
[[nodiscard]] inline bool takeDouble(
TokenStream& stream,
std::vector<SourceToken>& raw,
double& out) {
SourceToken token;
if (!takeToken(stream, raw, token)) {
return false;
}
const std::optional<double> value = parseDouble(token.value);
if (!value) {
return false;
}
out = *value;
return true;
}
// Szybki odczyt liczby z goracych petli wierzcholkow: bez kopii SourceToken
// (uzywa peek zamiast consume) i bez dopisywania do bufora raw. Bezpieczne tam,
// gdzie raw wierzcholkow nie jest pozniej czytany (siatki: triangles/strip/fan).
// Przy niepowodzeniu NIE konsumuje tokenu — wolajacy i tak cofa strumien do
// kotwicy wezla, wiec pozycja w miejscu bledu nie ma znaczenia.
[[nodiscard]] inline bool takeDoubleFast(TokenStream& stream, double& out) {
if (stream.empty()) {
return false;
}
const std::optional<double> value = parseDouble(stream.peek().value);
if (!value) {
return false;
}
stream.skip();
out = *value;
return true;
}
[[nodiscard]] inline bool takeDoubleOrPlaceholder(
TokenStream& stream,
std::vector<SourceToken>& raw,
double& out) {
SourceToken token;
if (!takeToken(stream, raw, token)) {
return false;
}
if (const std::optional<double> value = parseDouble(token.value)) {
out = *value;
return true;
}
std::uint8_t paramIndex = 0;
if (detail::parseIncludeParameterIndex(token.value, paramIndex)) {
out = 0.0;
return true;
}
return false;
}
[[nodiscard]] inline bool takeVec3(
TokenStream& stream,
std::vector<SourceToken>& raw,
Vec3& out) {
return takeDouble(stream, raw, out.x) && takeDouble(stream, raw, out.y) &&
takeDouble(stream, raw, out.z);
}
[[nodiscard]] inline bool atEnd(
TokenStream& stream,
const std::string_view subtype,
const std::string_view endMarker) {
return !stream.empty() && isEndMarkerFor(stream.peek().value, subtype, endMarker);
}
[[nodiscard]] inline bool consumeEnd(
TokenStream& stream,
std::vector<SourceToken>& raw,
const std::string_view subtype,
const std::string_view endMarker) {
if (!atEnd(stream, subtype, endMarker)) {
return false;
}
SourceToken token;
return takeToken(stream, raw, token);
}
} // namespace eu07::scene::node::io