Checkpoint 2: integrate native Editor, MCP, gameplay builds and standalone export

This commit is contained in:
Emil
2026-09-18 03:40:15 +03:00
parent 5c6b24d34d
commit 999686a896
125 changed files with 16086 additions and 1714 deletions
+87
View File
@@ -0,0 +1,87 @@
#include "../examples/extensions/beacon/Beacon.hpp"
#include <faset/core/io.hpp>
#include <faset/editor/plugins.hpp>
#include <iostream>
#define CHECK(x) \
do { \
if (!(x)) \
throw std::runtime_error("Check failed: " #x); \
} while (false)
int main() {
using namespace faset;
using namespace faset::editor;
const auto root = std::filesystem::temp_directory_path() / ("faset-plugins-" + new_id());
try {
std::filesystem::create_directories(root);
const auto folder = root / "Plugins";
std::filesystem::copy(FASET_TEST_PLUGIN_DIRECTORY, folder,
std::filesystem::copy_options::recursive);
authoring::AuthoringService authoring(root);
authoring.register_schemas(Json::array({beacon::schema()}));
Commands commands(authoring);
const auto document = authoring.create("Extension test");
const auto id = document.at("id");
{
PluginManager plugins(commands, [](auto) {});
plugins.load(folder);
CHECK(plugins.status().size() == 1);
CHECK(plugins.status()[0]["state"] == "loaded");
CHECK(plugins.panels().size() == 1);
const auto edited = commands.call("plugin_example_beacon_create", {{"document", id}});
CHECK(edited["revision"] == 1);
CHECK(edited["scene"]["entities"][0]["components"][2]["type"] == "example.beacon");
authoring.undo(id, 1);
CHECK(authoring.query(id)["scene"]["entities"].empty());
authoring.redo(id, 2);
authoring.save(id, "Scenes/plugin.scene.json");
// The extension's runtime component runs without loading its Editor DLL/SO.
runtime::Runtime world;
beacon::register_behavior(world);
world.load(edited.at("scene"));
world.advance(1.0 / 60);
CHECK(world.transform(world.find(edited["scene"]["entities"][0]["id"])).rotation[1] >
0);
}
for (const auto& command : commands.list())
CHECK(command["name"] != "plugin_example_beacon_create");
authoring::AuthoringService absent(root);
CHECK(absent.open("Scenes/plugin.scene.json")["scene"]["entities"][0]["components"][2]
["fields"]["speed"] == 1.0);
const auto file = folder / "beacon.faset-plugin.json";
const auto original = read_json(file);
auto wrong = original;
wrong["build_fingerprint"] = "wrong-build";
atomic_write_json(file, wrong);
{
PluginManager plugins(commands, [](auto) {});
plugins.load(folder);
CHECK(plugins.status()[0]["state"] == "failed");
CHECK(plugins.panels().empty());
}
auto cycle = original;
cycle["dependencies"] = Json::array({{{"id", "example.beacon"}, {"version", "1.0.0"}}});
atomic_write_json(file, cycle);
{
PluginManager plugins(commands, [](auto) {});
plugins.load(folder);
CHECK(plugins.status()[0]["state"] == "failed");
CHECK(plugins.panels().empty());
}
auto missing = original;
missing["dependencies"] = Json::array({{{"id", "missing"}, {"version", "1.0.0"}}});
atomic_write_json(file, missing);
{
PluginManager plugins(commands, [](auto) {});
plugins.load(folder);
CHECK(plugins.status()[0]["state"] == "failed");
}
std::filesystem::remove_all(root);
std::cout << "Native plugin ABI, ownership, commands, runtime component, missing package "
"preservation and dependency validation passed\n";
return 0;
} catch (const std::exception& error) {
std::filesystem::remove_all(root);
std::cerr << error.what() << '\n';
return 1;
}
}