5#include "common/config.h"
8#if !defined(EVENGINE_ANDROID) && !defined(EVENGINE_IOS) && !defined(EVENGINE_WEBGPU)
13#include <simplesquirrel/simplesquirrel.hpp>
23#if defined(EVENGINE_WEBGPU)
24#include <emscripten.h>
29ssq::VM* gFrameVm =
nullptr;
30ssq::Function* gFrameFunc =
nullptr;
32void webgpuFrameTick() {
33 if (!gFrameVm || !gFrameFunc || gFrameFunc->isEmpty())
return;
36 EM_ASM({
if (
window.hideEVELoading)
window.hideEVELoading(); });
39 ssq::Object r = gFrameVm->callFunc(*gFrameFunc, *gFrameVm);
40 if (r.getType() == ssq::Type::BOOL) keep = r.toBool();
41 }
catch (
const std::exception& e) {
42 fprintf(stderr,
"EVEngine: eve_frame error: %s\n", e.what());
45 if (!keep) emscripten_cancel_main_loop();
50#if defined(EVENGINE_ANDROID)
51#include <android/log.h>
52#define EVE_ANDROID_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "EVEngine", __VA_ARGS__)
53#elif defined(EVENGINE_IOS) || defined(EVENGINE_WEBGPU)
55#define EVE_ANDROID_LOGE(...) do { fprintf(stderr, "EVEngine: "); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
57#define EVE_ANDROID_LOGE(...) ((void)0)
71 void setup(CLI::App& app, std::shared_ptr<CLI::Formatter> formatter)
override {
72 auto run = app.add_subcommand(
"run",
"Run game under current path");
73 run->allow_extras()->formatter(formatter);
74 run->add_flag(
"--no-window",
no_window,
"Run script only, no window mode");
75 run->add_flag(
"--debug",
debug,
"debug mode (slicer + pause/breakpoints/snapshot/MCP)");
76 run->add_option(
"--dap-port",
dap_port,
77 "Start Debug Adapter Protocol server on port (implies --debug)");
78 run->add_option(
"--mcp-port",
mcp_port,
79 "Start Model Context Protocol server on port for AI agents (implies --debug)");
81 "Remote hot-reload dev server URL (eve dev), e.g. http://192.168.1.5:8765");
82 run->add_option(
"-l,--log",
log_path,
"log messages into a file");
83 run->add_option(
"-r,--root",
root_path,
"give a entry script instead of using the system default one");
87 auto run = app.get_subcommand(
"run");
88 if (run->parsed() || cmd.
getArgc() == 1) {
94 cerr <<
"Cannot open root script: " <<
root_path << endl;
97 std::string load_root((istreambuf_iterator<char>(ifs)), (istreambuf_iterator<char>()));
114int Cmdline::Run(std::string path, std::string root,
bool debug,
int dapPort,
int mcpPort,
115 std::string devServer) {
116 std::fprintf(stderr,
"[startup] Run() begins at process clock %.1f ms\n",
117 (
double) std::clock() * 1000.0 / (
double) CLOCKS_PER_SEC);
121 std::string gameDir = path;
122 std::string archivePath;
126 if (path.empty() || path ==
".")
127 gameDir = std::filesystem::current_path(ec).string();
129 gameDir = std::filesystem::absolute(path, ec).string();
130 if (ec) gameDir = path;
132 std::filesystem::path gp(gameDir);
133 if (std::filesystem::is_regular_file(gp, ec) && gp.extension() ==
".eve") {
135 archivePath = gp.string();
136 gameDir = gp.parent_path().string();
138 std::filesystem::path bundled = gp /
"game.eve";
139 if (std::filesystem::is_regular_file(bundled, ec))
140 archivePath = bundled.string();
146 if (!gameDir.empty() && gameDir !=
".") {
148 std::filesystem::current_path(gameDir, ec);
150 cerr <<
"Cannot chdir to game path '" << gameDir <<
"': " << ec.message() << endl;
151 EVE_ANDROID_LOGE(
"Cannot chdir to game path '%s': %s", gameDir.c_str(), ec.message().c_str());
158 auto *fs = eve::filesystem::Filesystem::create();
160 if (!archivePath.empty()) {
162 std::ifstream ifs(archivePath, std::ios::binary);
164 cerr <<
"Cannot open game archive: " << archivePath << endl;
167 std::vector<char> bytes((std::istreambuf_iterator<char>(ifs)),
168 std::istreambuf_iterator<char>());
169 if (bytes.empty() || !fs->setSourceFromMemory(bytes.data(), bytes.size())) {
170 cerr <<
"Failed to mount game archive from memory: " << archivePath << endl;
175 auto cwd = std::filesystem::current_path(ec);
178 fs->setSource(cwd.string());
184 Runtime runtime(2048, ssq::Libs::ALL);
186#if !defined(EVENGINE_ANDROID) && !defined(EVENGINE_IOS) && !defined(EVENGINE_WEBGPU)
189 dt.attach(runtime.
vm(),
true);
190 dt.exposeScriptApi(runtime.
vm());
192 const int bound = dt.startDap(
static_cast<uint16_t
>(dapPort));
194 cerr <<
"DAP listening on 127.0.0.1:" << bound << endl;
198 if (!dt.dap().waitUntilConfigured(15000))
199 cerr <<
"DAP: timed out waiting for client; starting anyway" << endl;
201 cerr <<
"Failed to start DAP server on port " << dapPort << endl;
206 dt.mcp().setGameRoot(std::filesystem::current_path().
string());
209 const int bound = dt.startMcp(
static_cast<uint16_t
>(mcpPort));
211 cerr <<
"MCP listening on 127.0.0.1:" << bound
212 <<
" (newline JSON-RPC; use tools/eve-mcp for Cursor stdio)" << endl;
214 cerr <<
"Failed to start MCP server on port " << mcpPort << endl;
225 ssq::Table
eve = runtime.
table(
"eve");
232 eve.set(
"devServerArg", devServer);
233 const char* bench = std::getenv(
"EVE_BOOT_BENCH");
234 eve.set(
"bootBench", bench && bench[0] !=
'\0' && bench[0] !=
'0');
235#if defined(EVENGINE_WEBGPU)
238 eve.set(
"hostDrivesFrames",
true);
240 eve.set(
"hostDrivesFrames",
false);
247 ssq::Table
eve = runtime.
table(
"eve");
248 eve.set(
"moduleList", runtime.
root().find(
"eve_modules"));
254 std::fprintf(stderr,
"[startup] load.nut begins at process clock %.1f ms\n",
255 (
double) std::clock() * 1000.0 / (
double) CLOCKS_PER_SEC);
257#if defined(EVENGINE_WEBGPU)
262 gFrameVm = &runtime.
vm();
263 gFrameFunc =
new ssq::Function(runtime.
vm().find(
"eve_frame").toFunction());
264 emscripten_set_main_loop(&webgpuFrameTick, 0, 1);
266#if !defined(EVENGINE_ANDROID) && !defined(EVENGINE_IOS) && !defined(EVENGINE_WEBGPU)
270 }
catch (
const std::exception& e) {
271#if !defined(EVENGINE_ANDROID) && !defined(EVENGINE_IOS) && !defined(EVENGINE_WEBGPU)
277 std::string report = dt.lastReport();
278 if (!(scriptError && scriptError->reported()) || report.empty())
279 report = dt.notifyError(e.what());
280 cerr << report << endl;
283 cerr <<
"Run failed: " << e.what() << endl;
286 cerr <<
"Run failed: " << e.what() << endl;
291#if !defined(EVENGINE_ANDROID) && !defined(EVENGINE_IOS) && !defined(EVENGINE_WEBGPU)
293 const std::string report =
295 cerr << report << endl;
298 cerr <<
"Run failed: unknown exception" << endl;
301 cerr <<
"Run failed: unknown exception" << endl;
#define EVE_ANDROID_LOGE(...)
ssq::Table table(const char *name) const
Looks up a named global table.
ScriptId runSource(std::string source, std::string sourceName="buffer")
Convenience: compileSource() then execute().
void initialize()
Exposes registered engine modules into the script root table. Safe to call more than once.
ssq::Table root() const
Root script table of the VM.
ssq::VM & vm() noexcept
Underlying SimpleSquirrel VM (requires a live, initialized runtime).
Exception raised at the public Runtime boundary.
命令行模块(eve.cmd):run / build / package / test / zip / dev-server 等子命令入口。
static std::string get_remaining(CLI::App *sub, std::string default_path=".")
子命令剩余位置参数。
int Run(std::string path, std::string root, bool debug=false, int dapPort=0, int mcpPort=0, std::string devServer="")
运行游戏(debug 时附加 DAP/MCP 端口)。
void installScriptFileApi(ssq::VM &vm)
用 PhysFS 版本覆盖 Squirrel 的 file / dofile / loadfile 全局, 使脚本与资源从已挂载的游戏源(真实目录或内存挂载的 .eve 归档)解析; PhysFS 中不...
WidgetDesc window(std::string title, std::vector< WidgetDesc > children, std::string id)
Top-level window widget with a title bar.
const char * async_content
const char * load_content
const char * module_list_content
const char * scene_director_content
const char * demo_content
One eve <subcommand> handler: CLI setup + argument parsing.
void setup(CLI::App &app, std::shared_ptr< CLI::Formatter > formatter) override
Registers options on the CLI11 sub-app.
int parse(CLI::App &app, Cmdline &cmd) override
Parses arguments and runs the command; returns the exit code.