| 404 | } |
| 405 | |
| 406 | void ServerApplication::DedicatedServerLoop() |
| 407 | { |
| 408 | LOG_INFO(Core, "Dedicated server main loop started"); |
| 409 | |
| 410 | int stat1Time = GlobalTickCount(); |
| 411 | (void)stat1Time; // Reserved for stats reporting |
| 412 | |
| 413 | auto& appConfig = AppConfig::Instance(); |
| 414 | bool simulateMode = appConfig.IsSimulateMode(); |
| 415 | int durationSec = appConfig.GetSimulateDuration(); |
| 416 | DWORD startTick = GlobalTickCount(); |
| 417 | |
| 418 | // Harness server for external test orchestration (Trident). |
| 419 | // Server advertises only the queries that make sense headless (no |
| 420 | // screenshot, no UI interaction, no SQF eval — there's no client |
| 421 | // scene or game state on the dedicated side). |
| 422 | std::unique_ptr<HarnessServer> harnessServer; |
| 423 | int harnessPort = appConfig.GetHarnessPort(); |
| 424 | if (harnessPort >= 0) |
| 425 | { |
| 426 | harnessServer = std::make_unique<HarnessServer>(); |
| 427 | if (!harnessServer->Start(harnessPort)) |
| 428 | { |
| 429 | LOG_ERROR(Core, "Failed to start harness server on port {}", harnessPort); |
| 430 | harnessServer.reset(); |
| 431 | } |
| 432 | else |
| 433 | { |
| 434 | harnessServer->RegisterCommand( |
| 435 | {"query", |
| 436 | "Query server state (what: players, mission, ngs, connections, master_server_server_detail, " |
| 437 | "master_server_mod_detail, master_server_mod_versions, master_server_mod_servers)", |
| 438 | {{"what", "string", true}}}, |
| 439 | [](const std::string&, cJSON* root) -> std::string |
| 440 | { |
| 441 | std::string resp = HarnessBuiltins::AnswerNetworkQuery(HarnessProtocol::GetString(root, "what")); |
| 442 | if (resp.empty()) |
| 443 | resp = HarnessBuiltins::AnswerServiceQuery(HarnessProtocol::GetString(root, "what"), root); |
| 444 | return resp.empty() ? HarnessProtocol::ErrorResponse("unknown query target") : resp; |
| 445 | }); |
| 446 | |
| 447 | // SQF eval/exec — the dedicated server has a world + GameState, so server-side test |
| 448 | // verbs (triAssertNgs, triEndTest) run here just as on the client. |
| 449 | HarnessBuiltins::RegisterSqf(*harnessServer); |
| 450 | |
| 451 | harnessServer->RegisterEvent({"player_joined", "Player connected", {{"dpid", "int"}, {"name", "string"}}}); |
| 452 | harnessServer->RegisterEvent({"player_left", "Player disconnected", {{"dpid", "int"}, {"name", "string"}}}); |
| 453 | harnessServer->RegisterEvent( |
| 454 | {"mission_state", "Server game state transition", {{"state", "string"}, {"prev", "string"}}}); |
| 455 | } |
| 456 | } |
| 457 | HarnessPlayerTracker harnessPlayerTracker; |
| 458 | HarnessMissionStateTracker harnessMissionStateTracker; |
| 459 | |
| 460 | #ifndef _WIN32 |
| 461 | signal(SIGINT, Poseidon::Foundation::handleInt); |
| 462 | #endif |
| 463 |
nothing calls this directly
no test coverage detected