Initial commit: DayZ memory C++ port with DMA backend and overlay
This commit is contained in:
+128
@@ -0,0 +1,128 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include "Config.h"
|
||||
#include "Logger.h"
|
||||
#include "Overlay/GameOverlay.h"
|
||||
#include "Overlay/OverlayWindow.h"
|
||||
#include "Runtime/DayZRuntimeService.h"
|
||||
#include "Web/WebRadarServer.h"
|
||||
|
||||
// Shared stop flag so the console Ctrl+C / close handler can reach it.
|
||||
static std::atomic<bool>* g_stopFlag = nullptr;
|
||||
|
||||
static BOOL WINAPI ConsoleCtrlHandler(DWORD signal) {
|
||||
if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT ||
|
||||
signal == CTRL_BREAK_EVENT)
|
||||
{
|
||||
if (g_stopFlag) g_stopFlag->store(true);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::filesystem::create_directories("logs");
|
||||
AppLogger::Init();
|
||||
auto log = AppLogger::Get();
|
||||
log->info("DayZ Memory Reader - Starting");
|
||||
|
||||
RuntimeConfig rtCfg = RuntimeConfig::Load("config/config.cfg");
|
||||
DayZRuntimeService service(rtCfg);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Config — loaded from config/overlay.json (defaults if missing).
|
||||
// -------------------------------------------------------------------------
|
||||
static const std::string kCfgPath = "config/overlay.json";
|
||||
OverlayConfig cfg = OverlayConfig::Load(kCfgPath);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Web radar server — settings come from config.
|
||||
// -------------------------------------------------------------------------
|
||||
WebRadarConfig webCfg;
|
||||
webCfg.bindAddress = cfg.webBindAddress;
|
||||
webCfg.port = cfg.webPort;
|
||||
webCfg.password = cfg.webPassword;
|
||||
|
||||
WebRadarServer webRadar(webCfg);
|
||||
webRadar.Start();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Runtime service callback: log stats + push snapshot to web radar.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
service.SetUpdateCallback([&log, &webRadar](const RuntimeUpdate& update) {
|
||||
// Always push to the web radar (even when not yet attached) so the
|
||||
// browser client sees connection/status changes immediately.
|
||||
webRadar.PushSnapshot(update);
|
||||
|
||||
if (!update.areBaseObjectsReady) {
|
||||
// Status messages are already de-duplicated inside WaitForSession.
|
||||
return;
|
||||
}
|
||||
|
||||
// Periodic heartbeat at most once every 10 seconds. Kept terse — the
|
||||
// interesting per-event detail (admins detected, skeletons lost) is logged
|
||||
// as it happens by the runtime service, not polled here.
|
||||
using Clock = std::chrono::steady_clock;
|
||||
static auto nextLog = Clock::time_point{};
|
||||
auto now = Clock::now();
|
||||
if (now < nextLog) return;
|
||||
nextLog = now + std::chrono::seconds(10);
|
||||
|
||||
// Skeleton coverage: how many in-range players currently have valid bones.
|
||||
size_t skelOk = 0, admins = 0;
|
||||
for (const auto& p : update.players) {
|
||||
if (p.skeleton.valid) ++skelOk;
|
||||
if (p.isAdmin) ++admins;
|
||||
}
|
||||
|
||||
log->info("[Live] Players={} (bones {}/{}, admins {}) Zombies={} Vehicles={} Server='{}'",
|
||||
update.players.size(),
|
||||
skelOk, update.players.size(), admins,
|
||||
update.zombies.size(),
|
||||
update.carsAndBoats.size(),
|
||||
update.serverName.value_or("?"));
|
||||
});
|
||||
|
||||
service.Start();
|
||||
|
||||
// Overlay polls GetLatestUpdate() each frame; no extra callback needed.
|
||||
std::atomic<bool> stopFlag{false};
|
||||
g_stopFlag = &stopFlag;
|
||||
|
||||
GameOverlay overlay(service, cfg, kCfgPath);
|
||||
overlay.SetWebRadarPort(webCfg.port);
|
||||
overlay.SetExitCallback([&stopFlag]() { stopFlag.store(true); });
|
||||
OverlayWindow window;
|
||||
window.SetResolutionOverride(cfg.overlayWidth, cfg.overlayHeight);
|
||||
overlay.SetResizeCallback([&window](int w, int h) { window.ResizeTo(w, h); });
|
||||
|
||||
window.SetDrawCallback([&overlay](float w, float h) {
|
||||
overlay.Draw(w, h);
|
||||
});
|
||||
window.SetInputQueryCallback([&overlay]() {
|
||||
return overlay.IsMenuOpen();
|
||||
});
|
||||
window.SetFontReadyCallback([&overlay](ImFont* close, ImFont* lootFar) {
|
||||
overlay.SetLootFonts(close, lootFar);
|
||||
});
|
||||
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
|
||||
|
||||
// Blocks until the overlay window is closed or stopFlag is set externally.
|
||||
window.Run(stopFlag);
|
||||
|
||||
webRadar.Stop();
|
||||
service.Stop();
|
||||
log->info("Stopped.");
|
||||
spdlog::shutdown();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user