From 167c04b107364fae2f4b6fb96e916cb7ba036d75 Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 2 Mar 2026 14:05:23 +0600 Subject: [PATCH 1/6] install --- proj-web/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proj-web/README.md b/proj-web/README.md index 3d0307f..7969215 100644 --- a/proj-web/README.md +++ b/proj-web/README.md @@ -3,6 +3,7 @@ Activate the environment: ``` +C:\Work\Projects\emsdk\emsdk.bat install latest C:\Work\Projects\emsdk\emsdk.bat activate latest C:\Work\Projects\emsdk\emsdk_env.bat ``` From 3879eb1d751e221316a36f88cc850ae1b4200dc9 Mon Sep 17 00:00:00 2001 From: Vlad Date: Tue, 3 Mar 2026 19:17:07 +0600 Subject: [PATCH 2/6] fixed web ship types boxes / local boxes collision and projectiles --- src/Game.cpp | 10 +++++-- src/network/LocalClient.cpp | 8 ++++-- src/network/WebSocketClientEmscripten.cpp | 34 +++++++++++++++++++---- src/network/WebSocketClientEmscripten.h | 4 +++ 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/Game.cpp b/src/Game.cpp index 210d181..8bcc280 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -174,6 +174,12 @@ namespace ZL std::cerr << "Sent JOIN: " << joinMsg << std::endl; } + space.boxCoordsArr.clear(); + space.boxRenderArr.clear(); + //space.boxLabels.clear(); + space.boxAlive.clear(); + space.serverBoxesApplied = false; + lastTickCount = 0; spaceGameStarted = 1; }; @@ -440,12 +446,12 @@ namespace ZL render(); if (networkClient) { -#ifndef NETWORK +//#ifndef NETWORK auto localClient = dynamic_cast(networkClient.get()); if (localClient) { localClient->setLocalPlayerState(Environment::shipState); } -#endif +//#endif networkClient->Poll(); } mainThreadHandler.processMainThreadTasks(); diff --git a/src/network/LocalClient.cpp b/src/network/LocalClient.cpp index 1d43a78..4125593 100644 --- a/src/network/LocalClient.cpp +++ b/src/network/LocalClient.cpp @@ -418,11 +418,13 @@ namespace ZL { pinfo.position = pr.pos; pinfo.rotation = dir.toRotationMatrix(); pinfo.velocity = velocity; - pendingProjectiles.push_back(pinfo); - std::cout << "LocalClient: Created projectile at pos (" << shotPos.x() << ", " + if (pinfo.shooterId != GetClientId()) { + pendingProjectiles.push_back(pinfo); + } + std::cout << "LocalClient: Created server projectile at pos (" << shotPos.x() << ", " << shotPos.y() << ", " << shotPos.z() << ") vel (" << pr.vel.x() << ", " - << pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl; + << pr.vel.y() << ", " << pr.vel.z() << ") shooter=" << pr.shooterId << std::endl; } } } diff --git a/src/network/WebSocketClientEmscripten.cpp b/src/network/WebSocketClientEmscripten.cpp index 9534cf0..931f3fe 100644 --- a/src/network/WebSocketClientEmscripten.cpp +++ b/src/network/WebSocketClientEmscripten.cpp @@ -22,13 +22,32 @@ namespace ZL { emscripten_websocket_set_onmessage_callback(socket_, this, onMessage); emscripten_websocket_set_onerror_callback(socket_, this, onError); emscripten_websocket_set_onclose_callback(socket_, this, onClose); + + connected = false; + } + + void WebSocketClientEmscripten::flushOutgoingQueue() { + std::lock_guard lock(outgoingMutex); + if (!socket_) return; + while (!outgoingQueue.empty()) { + const std::string &m = outgoingQueue.front(); + emscripten_websocket_send_utf8_text(socket_, m.c_str()); + outgoingQueue.pop(); + } } void WebSocketClientEmscripten::Send(const std::string& message) { - if (connected && socket_ > 0) { - auto signedMsg = SignMessage(message); - std::cout << "[WebWS] Sending message: " << signedMsg << std::endl; - emscripten_websocket_send_utf8_text(socket_, signedMsg.c_str()); + std::string signedMsg = SignMessage(message); + + { + std::lock_guard lock(outgoingMutex); + if (connected && socket_ > 0) { + std::cout << "[WebWS] Sending message (immediate): " << signedMsg << std::endl; + emscripten_websocket_send_utf8_text(socket_, signedMsg.c_str()); + return; + } + outgoingQueue.push(signedMsg); + std::cout << "[WebWS] Queued outgoing message (waiting for open): " << signedMsg << std::endl; } } @@ -44,11 +63,11 @@ namespace ZL { while (!localQueue.empty()) { const std::string& msg = localQueue.front(); - std::cout << "[WebWS] Processing message: " << msg << std::endl; + std::cout << "[WebWS] Processing message: " << msg << std::endl; // Передаем в базовый класс для парсинга игровых событий (BOXES, UPD, и т.д.) HandlePollMessage(msg); - + localQueue.pop(); } } @@ -59,6 +78,9 @@ namespace ZL { auto* self = static_cast(userData); self->connected = true; std::cout << "[WebWS] Connection opened" << std::endl; + + self->flushOutgoingQueue(); + return EM_TRUE; } diff --git a/src/network/WebSocketClientEmscripten.h b/src/network/WebSocketClientEmscripten.h index 2aac059..6e27c2e 100644 --- a/src/network/WebSocketClientEmscripten.h +++ b/src/network/WebSocketClientEmscripten.h @@ -20,6 +20,10 @@ namespace ZL { std::queue messageQueue; std::mutex queueMutex; + std::queue outgoingQueue; + std::mutex outgoingMutex; + void flushOutgoingQueue(); + public: WebSocketClientEmscripten() = default; virtual ~WebSocketClientEmscripten() = default; From 9eafcd27fb799dc189531c494b5316b7aa839a68 Mon Sep 17 00:00:00 2001 From: vottozi Date: Tue, 3 Mar 2026 19:42:08 +0600 Subject: [PATCH 3/6] Fix lead indicator prediction & align projectile lifetime (client/server) --- server/server.cpp | 2 +- src/Space.cpp | 114 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/server/server.cpp b/server/server.cpp index e7fa469..dd1a900 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -423,7 +423,7 @@ private: float len = worldForward.norm(); if (len > 1e-6f) worldForward /= len; pr.vel = worldForward * velocity; - pr.lifeMs = 5000.0f; + pr.lifeMs = 15000.0f; g_projectiles.push_back(pr); std::cout << "Server: Created projectile from player " << id_ diff --git a/src/Space.cpp b/src/Space.cpp index 3639912..9ec49f2 100644 --- a/src/Space.cpp +++ b/src/Space.cpp @@ -25,6 +25,13 @@ #include "network/LocalClient.h" #endif +// --- TEMP DEBUG SHOT LOG --- +#define DEBUG_SHOTLOG 1 +#if DEBUG_SHOTLOG +static uint64_t g_lastShotLogMs = 0; +static uint64_t g_lastAimLogMs = 0; +#endif + namespace ZL { @@ -1143,40 +1150,56 @@ namespace ZL Vector3f shooterPos = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f }; // скорость цели в мире (вектор) - Vector3f shooterVel = ForwardFromRotation(Environment::shipState.rotation) * Environment::shipState.velocity; + // Vector3f shooterVel = ForwardFromRotation(Environment::shipState.rotation) * Environment::shipState.velocity; + + float shooterSpeed = std::abs(Environment::shipState.velocity); + // В нашей физике линейная скорость корабля всегда направлена по его forward (-Z). + // Когда игрок "наводится" на lead, forward (и скорость) становятся сонаправлены с выстрелом, + // поэтому эффективная скорость снаряда в мире ≈ muzzle + shipSpeed. + const float effectiveProjectileSpeed = projectileSpeed + shooterSpeed; + Vector3f shooterVel = Vector3f::Zero(); // скорость уже учтена в effectiveProjectileSpeed Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity; + // --- ВАЖНО: у нас remote state берется на now_ms - CLIENT_DELAY + // Значит shipWorld - это позиция ~0.5 сек назад. + // Для корректного lead нужно предсказать положение цели на "сейчас". + const float clientDelaySec = (float)CLIENT_DELAY / 1000.0f; + Vector3f targetPosNow = shipWorld + targetVel * clientDelaySec; + const float minTargetSpeed = 0.5f; // подобрать (в твоих единицах) bool targetMoving = (targetVel.norm() > minTargetSpeed); // альфа круга float leadAlpha = targetMoving ? 1.0f : 0.5f; - Vector3f leadWorld = shipWorld; + // Vector3f leadWorld = shipWorld; + Vector3f leadWorld = targetPosNow; bool haveLead = false; - // чтобы круг не улетал далеко: максимум 4 секунды (подстроить под игру) - float distToTarget = (Environment::shipState.position - shipWorld).norm(); - float maxLeadTime = std::clamp((distToTarget / projectileSpeed) * 1.2f, 0.05f, 4.0f); + // Дистанцию лучше считать от реальной точки вылета + float distToTarget = (shooterPos - targetPosNow).norm(); + // Максимальное время перехвата ограничиваем жизнью пули + const float projectileLifeSec = (float)PROJECTILE_LIFE / 1000.0f; + float maxLeadTime = std::clamp((distToTarget / effectiveProjectileSpeed) * 1.25f, 0.01f, projectileLifeSec * 0.98f); if (!targetMoving) { // Цель стоит: рисуем lead прямо на ней, но полупрозрачный - leadWorld = shipWorld; + leadWorld = targetPosNow; haveLead = true; } else { float tLead = 0.0f; // 1) Пытаемся “правильное” решение перехвата - bool ok = SolveLeadInterceptTime(shooterPos, shooterVel, shipWorld, targetVel, projectileSpeed, tLead); + bool ok = SolveLeadInterceptTime(shooterPos, shooterVel, targetPosNow, targetVel, effectiveProjectileSpeed, tLead); // 2) Если решения нет / оно плохое — fallback (чтобы круг не пропадал при пролёте "вбок") // Это ключевое изменение: lead всегда будет. if (!ok || !(tLead > 0.0f) || tLead > maxLeadTime) { - tLead = std::clamp(distToTarget / projectileSpeed, 0.05f, maxLeadTime); + tLead = std::clamp(distToTarget / effectiveProjectileSpeed, 0.05f, maxLeadTime); } - leadWorld = shipWorld + targetVel * tLead; + leadWorld = targetPosNow + targetVel * tLead; haveLead = true; } @@ -1421,7 +1444,8 @@ namespace ZL firePressed = false; if (now_ms - lastProjectileFireTime >= static_cast(projectileCooldownMs)) { lastProjectileFireTime = now_ms; - const float projectileSpeed = 250.0f; + // const float projectileSpeed = 250.0f; + const float projectileSpeed = PROJECTILE_VELOCITY; this->fireProjectiles(); @@ -1429,7 +1453,71 @@ namespace ZL Eigen::Vector3f worldForward = (Environment::shipState.rotation * localForward).normalized(); Eigen::Vector3f centerPos = Environment::shipState.position + - Environment::shipState.rotation * Vector3f{ 0, 0.9f, 5.0f }; + Environment::shipState.rotation * Vector3f{ 0, 0.9f - 6.0f, 5.0f }; + +#if DEBUG_SHOTLOG + // лог не чаще чем раз в 250мс, чтобы не заспамить консоль + if (now_ms - g_lastShotLogMs > 250) { + g_lastShotLogMs = now_ms; + + // 1) кого мы сейчас таргетим (как в HUD) + int tid = pickTargetId(); + if (tid >= 0 && remotePlayerStates.count(tid)) { + const ClientState& st = remotePlayerStates.at(tid); + + // shooterPos ДОЛЖЕН совпадать с тем, что используется в lead (HUD) + Vector3f shooterPos = Environment::shipState.position + + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f }; + + Vector3f targetPos = st.position; + Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity; + + const float clientDelaySec = (float)CLIENT_DELAY / 1000.0f; + Vector3f targetPosNow = targetPos + targetVel * clientDelaySec; + + float shooterSpeed = std::abs(Environment::shipState.velocity); + float effSpeed = PROJECTILE_VELOCITY + shooterSpeed; + + float dist = (shooterPos - targetPosNow).norm(); + float projectileLifeSec = (float)PROJECTILE_LIFE / 1000.0f; + float tStraight = dist / max(1e-3f, effSpeed); + + // попытка solve lead + float tLead = 0.0f; + bool ok = SolveLeadInterceptTime(shooterPos, Vector3f::Zero(), targetPosNow, targetVel, effSpeed, tLead); + + // clamp как у HUD + float maxLeadTime = std::clamp(tStraight * 1.25f, 0.01f, projectileLifeSec * 0.98f); + if (!ok || !(tLead > 0.0f) || tLead > maxLeadTime) { + tLead = std::clamp(tStraight, 0.05f, maxLeadTime); + } + + Vector3f leadWorld = targetPosNow + targetVel * tLead; + + std::cout + << "\n[SHOTLOG] now_ms=" << now_ms + << " tid=" << tid + << " dist=" << dist + << " tStraight=" << tStraight + << " tLead=" << tLead + << " lifeSec=" << projectileLifeSec + << " effSpeed=" << effSpeed + << " shipSpeed=" << shooterSpeed + << "\n shooterPos=(" << shooterPos.x() << "," << shooterPos.y() << "," << shooterPos.z() << ")" + << "\n centerPos (sent)=(" << centerPos.x() << "," << centerPos.y() << "," << centerPos.z() << ")" + << "\n targetPos(raw)=(" << targetPos.x() << "," << targetPos.y() << "," << targetPos.z() << ")" + << "\n targetPosNow =(" << targetPosNow.x() << "," << targetPosNow.y() << "," << targetPosNow.z() << ")" + << "\n targetVel=(" << targetVel.x() << "," << targetVel.y() << "," << targetVel.z() << ")" + << "\n leadWorld=(" << leadWorld.x() << "," << leadWorld.y() << "," << leadWorld.z() << ")" + << "\n okSolve=" << (ok ? "true" : "false") + << "\n WILL_REACH=" << ((tStraight <= projectileLifeSec) ? "YES" : "NO (life too short)") + << std::endl; + } + else { + std::cout << "\n[SHOTLOG] now_ms=" << now_ms << " no target\n"; + } + } +#endif Eigen::Quaternionf q(Environment::shipState.rotation); float speedToSend = projectileSpeed + Environment::shipState.velocity; @@ -1743,8 +1831,8 @@ namespace ZL const float size = 0.5f; for (const auto& pi : pending) { const std::vector localOffsets = { - Vector3f{ -1.5f, 0.9f, 5.0f }, - Vector3f{ 1.5f, 0.9f, 5.0f } + Vector3f{ -1.5f, 0.9f - 6.0f, 5.0f }, + Vector3f{ 1.5f, 0.9f - 6.0f, 5.0f } }; Vector3f localForward = { 0, 0, -1 }; From 2f50dc152255f613ae3834252f19f042353c1752 Mon Sep 17 00:00:00 2001 From: vottozi Date: Wed, 4 Mar 2026 01:42:57 +0600 Subject: [PATCH 4/6] fix Lead Indicator rendering when target off-screen --- src/Space.cpp | 233 +++++++++++++++++++------------------------------- 1 file changed, 89 insertions(+), 144 deletions(-) diff --git a/src/Space.cpp b/src/Space.cpp index 9ec49f2..cd968aa 100644 --- a/src/Space.cpp +++ b/src/Space.cpp @@ -25,13 +25,6 @@ #include "network/LocalClient.h" #endif -// --- TEMP DEBUG SHOT LOG --- -#define DEBUG_SHOTLOG 1 -#if DEBUG_SHOTLOG -static uint64_t g_lastShotLogMs = 0; -static uint64_t g_lastAimLogMs = 0; -#endif - namespace ZL { @@ -1153,16 +1146,16 @@ namespace ZL // Vector3f shooterVel = ForwardFromRotation(Environment::shipState.rotation) * Environment::shipState.velocity; float shooterSpeed = std::abs(Environment::shipState.velocity); - // В нашей физике линейная скорость корабля всегда направлена по его forward (-Z). - // Когда игрок "наводится" на lead, forward (и скорость) становятся сонаправлены с выстрелом, + // В нашей физике линейная скорость корабля всегда направлена по его forward (-Z) + // Когда игрок наводится на lead indicator, forward (и скорость) становятся сонаправлены с выстрелом // поэтому эффективная скорость снаряда в мире ≈ muzzle + shipSpeed. const float effectiveProjectileSpeed = projectileSpeed + shooterSpeed; Vector3f shooterVel = Vector3f::Zero(); // скорость уже учтена в effectiveProjectileSpeed Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity; - // --- ВАЖНО: у нас remote state берется на now_ms - CLIENT_DELAY + // ВАЖНО: remote state берется на now_ms - CLIENT_DELAY // Значит shipWorld - это позиция ~0.5 сек назад. - // Для корректного lead нужно предсказать положение цели на "сейчас". + // Для корректного lead нужно предсказать положение цели на сейчас const float clientDelaySec = (float)CLIENT_DELAY / 1000.0f; Vector3f targetPosNow = shipWorld + targetVel * clientDelaySec; @@ -1172,7 +1165,6 @@ namespace ZL // альфа круга float leadAlpha = targetMoving ? 1.0f : 0.5f; - // Vector3f leadWorld = shipWorld; Vector3f leadWorld = targetPosNow; bool haveLead = false; @@ -1203,30 +1195,60 @@ namespace ZL haveLead = true; } - // 2) проекция + // Проекция цели (для рамок/стрелки) float ndcX, ndcY, ndcZ, clipW; if (!projectToNDC(shipWorld, ndcX, ndcY, ndcZ, clipW)) return; - // behind camera? bool behind = (clipW <= 0.0f); - // on-screen check (NDC) bool onScreen = (!behind && ndcX >= -1.0f && ndcX <= 1.0f && ndcY >= -1.0f && ndcY <= 1.0f); - // 3) расстояние float dist = (Environment::shipState.position - shipWorld).norm(); - - // time for arrow bob float t = static_cast(SDL_GetTicks64()) * 0.001f; - // 4) Настройки стиля + // Проекция Lead + float leadNdcX = 0.f, leadNdcY = 0.f, leadNdcZ = 0.f, leadClipW = 0.f; + bool leadOnScreen = false; + + if (haveLead) { + if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) { + leadOnScreen = + (leadNdcX >= -1.0f && leadNdcX <= 1.0f && + leadNdcY >= -1.0f && leadNdcY <= 1.0f); + } + } + + // Настройки HUD стилизация Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный float thickness = 2.0f; // толщина линий (px) float z = 0.0f; // 2D слой - // 5) Если цель в кадре: рисуем скобки + auto drawLeadRing2D = [&](float lx, float ly) + { + float distLead = (Environment::shipState.position - leadWorld).norm(); + float r = 30.0f / (distLead * 0.01f + 1.0f); + r = std::clamp(r, 6.0f, 18.0f); + + float thicknessPx = 2.5f; + float innerR = max(1.0f, r - thicknessPx); + float outerR = r + thicknessPx; + + Eigen::Vector4f leadColor = enemyColor; + leadColor.w() = leadAlpha; + + renderer.RenderUniform4fv("uColor", leadColor.data()); + VertexDataStruct ring = MakeRing2D(lx, ly, innerR, outerR, 0.0f, 32, enemyColor); + hudTempMesh.AssignFrom(ring); + renderer.DrawVertexRenderStruct(hudTempMesh); + + // вернуть цвет HUD обратно + renderer.RenderUniform4fv("uColor", enemyColor.data()); + }; + + + // Цель в кадре: рамки if (onScreen) { // перевод NDC -> экран (в пикселях) @@ -1257,11 +1279,11 @@ namespace ZL // рисуем 8 тонких прямоугольников (2 на угол) auto drawBar = [&](float cx, float cy, float w, float h) - { - VertexDataStruct v = MakeColoredRect2D(cx, cy, w * 0.5f, h * 0.5f, z, enemyColor); - hudTempMesh.AssignFrom(v); - renderer.DrawVertexRenderStruct(hudTempMesh); - }; + { + VertexDataStruct v = MakeColoredRect2D(cx, cy, w * 0.5f, h * 0.5f, z, enemyColor); + hudTempMesh.AssignFrom(v); + renderer.DrawVertexRenderStruct(hudTempMesh); + }; // включаем 2D режим glDisable(GL_DEPTH_TEST); @@ -1275,40 +1297,9 @@ namespace ZL renderer.LoadIdentity(); renderer.EnableVertexAttribArray("vPosition"); + renderer.RenderUniform4fv("uColor", enemyColor.data()); - Eigen::Vector4f hudColor = enemyColor; - renderer.RenderUniform4fv("uColor", hudColor.data()); - - - if (haveLead) { - float leadNdcX, leadNdcY, leadNdcZ, leadClipW; - if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) { - if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) { - float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth; - float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight; - - float distLead = (Environment::shipState.position - leadWorld).norm(); - float r = 30.0f / (distLead * 0.01f + 1.0f); - r = std::clamp(r, 6.0f, 18.0f); - - float thicknessPx = 2.5f; - float innerR = max(1.0f, r - thicknessPx); - float outerR = r + thicknessPx; - Eigen::Vector4f leadColor = enemyColor; - leadColor.w() = leadAlpha; - renderer.RenderUniform4fv("uColor", leadColor.data()); - - VertexDataStruct ring = MakeRing2D(lx, ly, innerR, outerR, 0.0f, 32, enemyColor); - hudTempMesh.AssignFrom(ring); - renderer.DrawVertexRenderStruct(hudTempMesh); - - renderer.RenderUniform4fv("uColor", hudColor.data()); - } - } - } - - renderer.EnableVertexAttribArray("vPosition"); - + // рамки drawBar(left + cornerLen * 0.5f, top, cornerLen, thickness); drawBar(left, top - cornerLen * 0.5f, thickness, cornerLen); @@ -1321,9 +1312,14 @@ namespace ZL drawBar(right - cornerLen * 0.5f, bottom, cornerLen, thickness); drawBar(right, bottom + cornerLen * 0.5f, thickness, cornerLen); + // LEAD — независимо от рамок: если его точка на экране, рисуем + if (haveLead && leadOnScreen) { + float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth; + float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight; + drawLeadRing2D(lx, ly); + } + renderer.DisableVertexAttribArray("vPosition"); - - renderer.PopMatrix(); renderer.PopProjectionMatrix(); renderer.shaderManager.PopShader(); @@ -1335,6 +1331,8 @@ namespace ZL return; } + + // Цель вне экрана: стрелка float dirX = ndcX; float dirY = ndcY; @@ -1377,26 +1375,26 @@ namespace ZL Vector3f right{ edgeX - px * (arrowWid * 0.5f), edgeY - py * (arrowWid * 0.5f), z }; auto drawTri = [&](const Vector3f& a, const Vector3f& b, const Vector3f& c) - { - VertexDataStruct v; - v.PositionData = { a, b, c }; - Vector3f rgb{ enemyColor.x(), enemyColor.y(), enemyColor.z() }; - v.ColorData = { rgb, rgb, rgb }; - // defaultColor vertex shader expects vNormal and vTexCoord (avoids NaN on WebGL). - const Vector3f n{ 0.f, 0.f, 1.f }; - v.NormalData = { n, n, n }; - const Vector2f uv{ 0.f, 0.f }; - v.TexCoordData = { uv, uv, uv }; - hudTempMesh.AssignFrom(v); - renderer.DrawVertexRenderStruct(hudTempMesh); - }; + { + VertexDataStruct v; + v.PositionData = { a, b, c }; + Vector3f rgb{ enemyColor.x(), enemyColor.y(), enemyColor.z() }; + v.ColorData = { rgb, rgb, rgb }; + // defaultColor vertex shader expects vNormal and vTexCoord (avoids NaN on WebGL). + const Vector3f n{ 0.f, 0.f, 1.f }; + v.NormalData = { n, n, n }; + const Vector2f uv{ 0.f, 0.f }; + v.TexCoordData = { uv, uv, uv }; + hudTempMesh.AssignFrom(v); + renderer.DrawVertexRenderStruct(hudTempMesh); + }; auto drawBar = [&](float cx, float cy, float w, float h) - { - VertexDataStruct v = MakeColoredRect2D(cx, cy, w * 0.5f, h * 0.5f, z, enemyColor); - hudTempMesh.AssignFrom(v); - renderer.DrawVertexRenderStruct(hudTempMesh); - }; + { + VertexDataStruct v = MakeColoredRect2D(cx, cy, w * 0.5f, h * 0.5f, z, enemyColor); + hudTempMesh.AssignFrom(v); + renderer.DrawVertexRenderStruct(hudTempMesh); + }; glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); @@ -1407,18 +1405,30 @@ namespace ZL renderer.PushMatrix(); renderer.LoadIdentity(); + renderer.EnableVertexAttribArray("vPosition"); + renderer.RenderUniform4fv("uColor", enemyColor.data()); + + // стрелка drawTri(tip, left, right); float tailLen = 14.0f; float tailX = edgeX - dirX * 6.0f; float tailY = edgeY - dirY * 6.0f; - drawBar(tailX, tailY, max(thickness, tailLen), thickness); + // LEAD — рисуем даже когда цель вне экрана (если lead точка на экране) + if (haveLead && leadOnScreen) { + float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth; + float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight; + drawLeadRing2D(lx, ly); + } + + renderer.DisableVertexAttribArray("vPosition"); renderer.PopMatrix(); renderer.PopProjectionMatrix(); renderer.shaderManager.PopShader(); + // дистанция около стрелки { std::string d = std::to_string((int)dist) + "m"; float tx = edgeX + px * 18.0f; @@ -1444,7 +1454,6 @@ namespace ZL firePressed = false; if (now_ms - lastProjectileFireTime >= static_cast(projectileCooldownMs)) { lastProjectileFireTime = now_ms; - // const float projectileSpeed = 250.0f; const float projectileSpeed = PROJECTILE_VELOCITY; this->fireProjectiles(); @@ -1455,70 +1464,6 @@ namespace ZL Eigen::Vector3f centerPos = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0, 0.9f - 6.0f, 5.0f }; -#if DEBUG_SHOTLOG - // лог не чаще чем раз в 250мс, чтобы не заспамить консоль - if (now_ms - g_lastShotLogMs > 250) { - g_lastShotLogMs = now_ms; - - // 1) кого мы сейчас таргетим (как в HUD) - int tid = pickTargetId(); - if (tid >= 0 && remotePlayerStates.count(tid)) { - const ClientState& st = remotePlayerStates.at(tid); - - // shooterPos ДОЛЖЕН совпадать с тем, что используется в lead (HUD) - Vector3f shooterPos = Environment::shipState.position - + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f }; - - Vector3f targetPos = st.position; - Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity; - - const float clientDelaySec = (float)CLIENT_DELAY / 1000.0f; - Vector3f targetPosNow = targetPos + targetVel * clientDelaySec; - - float shooterSpeed = std::abs(Environment::shipState.velocity); - float effSpeed = PROJECTILE_VELOCITY + shooterSpeed; - - float dist = (shooterPos - targetPosNow).norm(); - float projectileLifeSec = (float)PROJECTILE_LIFE / 1000.0f; - float tStraight = dist / max(1e-3f, effSpeed); - - // попытка solve lead - float tLead = 0.0f; - bool ok = SolveLeadInterceptTime(shooterPos, Vector3f::Zero(), targetPosNow, targetVel, effSpeed, tLead); - - // clamp как у HUD - float maxLeadTime = std::clamp(tStraight * 1.25f, 0.01f, projectileLifeSec * 0.98f); - if (!ok || !(tLead > 0.0f) || tLead > maxLeadTime) { - tLead = std::clamp(tStraight, 0.05f, maxLeadTime); - } - - Vector3f leadWorld = targetPosNow + targetVel * tLead; - - std::cout - << "\n[SHOTLOG] now_ms=" << now_ms - << " tid=" << tid - << " dist=" << dist - << " tStraight=" << tStraight - << " tLead=" << tLead - << " lifeSec=" << projectileLifeSec - << " effSpeed=" << effSpeed - << " shipSpeed=" << shooterSpeed - << "\n shooterPos=(" << shooterPos.x() << "," << shooterPos.y() << "," << shooterPos.z() << ")" - << "\n centerPos (sent)=(" << centerPos.x() << "," << centerPos.y() << "," << centerPos.z() << ")" - << "\n targetPos(raw)=(" << targetPos.x() << "," << targetPos.y() << "," << targetPos.z() << ")" - << "\n targetPosNow =(" << targetPosNow.x() << "," << targetPosNow.y() << "," << targetPosNow.z() << ")" - << "\n targetVel=(" << targetVel.x() << "," << targetVel.y() << "," << targetVel.z() << ")" - << "\n leadWorld=(" << leadWorld.x() << "," << leadWorld.y() << "," << leadWorld.z() << ")" - << "\n okSolve=" << (ok ? "true" : "false") - << "\n WILL_REACH=" << ((tStraight <= projectileLifeSec) ? "YES" : "NO (life too short)") - << std::endl; - } - else { - std::cout << "\n[SHOTLOG] now_ms=" << now_ms << " no target\n"; - } - } -#endif - Eigen::Quaternionf q(Environment::shipState.rotation); float speedToSend = projectileSpeed + Environment::shipState.velocity; int shotCount = 2; From ef7617701ee5e97e9bbfda6c470751525a13ec7d Mon Sep 17 00:00:00 2001 From: vottozi Date: Wed, 4 Mar 2026 03:37:58 +0600 Subject: [PATCH 5/6] fix server collision radius mismatch --- server/server.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/server.cpp b/server/server.cpp index dd1a900..ebfef93 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -526,9 +526,7 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) { if (!session->fetchStateAtTime(now, targetState)) continue; Eigen::Vector3f diff = pr.pos - targetState.position; - const float shipRadius = 15.0f; - const float projectileRadius = 1.5f; - float combinedRadius = shipRadius + projectileRadius; + float combinedRadius = shipCollisionRadius + projectileHitRadius; if (diff.squaredNorm() <= combinedRadius * combinedRadius) { DeathInfo death; From e78429b600c6f2dd74a81baa638cdea7ebf59edf Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Wed, 4 Mar 2026 21:41:51 +0300 Subject: [PATCH 6/6] Working on running the game even in Telegram --- proj-web/CMakeLists.txt | 14 ++-- proj-web/space-game001.html | 2 + src/Game.cpp | 43 ++++++------ src/Game.h | 4 +- src/main.cpp | 79 ++++++++++++++++++----- src/network/WebSocketClientEmscripten.cpp | 12 ++-- src/network/WebSocketClientEmscripten.h | 1 - src/utils/TaskManager.cpp | 20 +++++- src/utils/TaskManager.h | 18 ++++-- 9 files changed, 133 insertions(+), 60 deletions(-) create mode 100644 proj-web/space-game001.html diff --git a/proj-web/CMakeLists.txt b/proj-web/CMakeLists.txt index 8b02c39..0f353b1 100644 --- a/proj-web/CMakeLists.txt +++ b/proj-web/CMakeLists.txt @@ -63,8 +63,6 @@ set(SOURCES ../src/network/LocalClient.cpp ../src/network/ClientState.h ../src/network/ClientState.cpp - ../src/network/WebSocketClient.h - ../src/network/WebSocketClient.cpp ../src/network/WebSocketClientBase.h ../src/network/WebSocketClientBase.cpp ../src/network/WebSocketClientEmscripten.h @@ -95,10 +93,7 @@ set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE) add_subdirectory("../thirdparty/libzip-1.11.4" libzip-build) -# Линковка: -# 'zip' берется из add_subdirectory -# 'z' - это системный zlib Emscripten-а (флаг -sUSE_ZLIB=1 добавим ниже) -target_link_libraries(space-game001 PRIVATE zip z websocket) +target_link_libraries(space-game001 PRIVATE zip z websocket.js) # Эмскриптен-флаги set(EMSCRIPTEN_FLAGS @@ -107,8 +102,8 @@ set(EMSCRIPTEN_FLAGS "-sUSE_LIBPNG=1" "-sUSE_ZLIB=1" "-sUSE_SDL_TTF=2" - "-pthread" - "-sUSE_PTHREADS=1" + #"-pthread" + #"-sUSE_PTHREADS=1" "-fexceptions" "-DNETWORK" ) @@ -120,8 +115,9 @@ target_compile_options(space-game001 PRIVATE ${EMSCRIPTEN_FLAGS} "-O2") set(EMSCRIPTEN_LINK_FLAGS ${EMSCRIPTEN_FLAGS} "-O2" - "-sPTHREAD_POOL_SIZE=4" + #"-sPTHREAD_POOL_SIZE=4" "-sALLOW_MEMORY_GROWTH=1" + "-sFULL_ES3=1" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/loading.png@resources/loading.png" "--preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../resources/shaders@resources/shaders" ) diff --git a/proj-web/space-game001.html b/proj-web/space-game001.html new file mode 100644 index 0000000..710f6f4 --- /dev/null +++ b/proj-web/space-game001.html @@ -0,0 +1,2 @@ +Emscripten-Generated Code +
Downloading...
Resize canvas Lock/hide mouse pointer    
\ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index 210d181..e833928 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -57,9 +57,9 @@ namespace ZL #endif Game::Game() - : window(nullptr) + : /*window(nullptr) , glContext(nullptr) - , newTickCount(0) + , */newTickCount(0) , lastTickCount(0) , menuManager(renderer) , space(renderer, taskManager, mainThreadHandler, networkClient, menuManager) @@ -67,12 +67,13 @@ namespace ZL } Game::~Game() { + /* if (glContext) { SDL_GL_DeleteContext(glContext); } if (window) { SDL_DestroyWindow(window); - } + }*/ #ifndef EMSCRIPTEN // In Emscripten, SDL must stay alive across context loss/restore cycles // so the window remains valid when the game object is re-created. @@ -81,14 +82,15 @@ namespace ZL } void Game::setup() { - glContext = SDL_GL_CreateContext(ZL::Environment::window); + //glContext = SDL_GL_CreateContext(ZL::Environment::window); + //glContext = in_glContext; Environment::computeProjectionDimensions(); ZL::BindOpenGlFunctions(); ZL::CheckGlError(); renderer.InitOpenGL(); - + #ifdef EMSCRIPTEN // These shaders and loading.png are preloaded separately (not from zip), // so they are available immediately without waiting for resources.zip. @@ -101,19 +103,23 @@ namespace ZL loadingTexture = std::make_unique(CreateTextureDataFromPng("resources/loading.png", CONST_ZIP_FILE)); #endif - loadingMesh.data = CreateRect2D({ Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, { Environment::projectionWidth * 0.5f, Environment::projectionHeight * 0.5f }, 3); + loadingMesh.data = CreateRect2D({ 0.5f, 0.5f }, { 0.5f, 0.5f }, 3); loadingMesh.RefreshVBO(); - + #ifdef EMSCRIPTEN // Asynchronously download resources.zip; setupPart2() is called on completion. // The loading screen stays visible until the download finishes. s_instance = this; + std::cout << "Load resurces step 1" << std::endl; emscripten_async_wget("resources.zip", "resources.zip", onResourcesZipLoaded, onResourcesZipError); #else mainThreadHandler.EnqueueMainThreadTask([this]() { + std::cout << "Load resurces step 2" << std::endl; this->setupPart2(); + std::cout << "Load resurces step 3" << std::endl; }); #endif + } @@ -164,10 +170,10 @@ namespace ZL networkClient = std::make_unique(); networkClient->Connect("localhost", 8081); #else - networkClient = std::make_unique(taskManager.getIOContext()); - networkClient->Connect("localhost", 8081); + //networkClient = std::make_unique(taskManager.getIOContext()); + //networkClient->Connect("localhost", 8081); #endif - + if (networkClient) { std::string joinMsg = std::string("JOIN:") + nickname + ":" + std::to_string(shipType); networkClient->Send(joinMsg); @@ -249,12 +255,12 @@ namespace ZL renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vTexCoordName); - float width = Environment::projectionWidth; - float height = Environment::projectionHeight; + //float width = Environment::projectionWidth; + //float height = Environment::projectionHeight; renderer.PushProjectionMatrix( - 0, width, - 0, height, + 0, 1, + 0, 1, -10, 10); renderer.PushMatrix(); @@ -275,8 +281,7 @@ namespace ZL int64_t Game::getSyncTimeMs() { int64_t localNow = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); - // Добавляем смещение, полученное от сервера - if (networkClient) + if(networkClient) { return localNow + networkClient->getTimeOffset(); } @@ -317,10 +322,10 @@ namespace ZL } void Game::render() { - SDL_GL_MakeCurrent(ZL::Environment::window, glContext); + //SDL_GL_MakeCurrent(ZL::Environment::window, glContext); ZL::CheckGlError(); - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawScene(); @@ -438,7 +443,7 @@ namespace ZL #endif } render(); - + if (networkClient) { #ifndef NETWORK auto localClient = dynamic_cast(networkClient.get()); diff --git a/src/Game.h b/src/Game.h index ab4ab9f..208fa1e 100644 --- a/src/Game.h +++ b/src/Game.h @@ -60,8 +60,8 @@ namespace ZL { static void onResourcesZipError(const char* filename); #endif - SDL_Window* window; - SDL_GLContext glContext; + //SDL_Window* window; + //SDL_GLContext glContext; int64_t newTickCount; int64_t lastTickCount; diff --git a/src/main.cpp b/src/main.cpp index 6b62092..bc9aa54 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,37 +16,46 @@ // For Android and Desktop a plain global value is used (no context loss). #ifdef EMSCRIPTEN ZL::Game* g_game = nullptr; +//static SDL_Window* win_x = nullptr; +static SDL_GLContext glContext_x; // Не указатель, а сам объект (который суть void*) + #else ZL::Game game; #endif -void MainLoop() { #ifdef EMSCRIPTEN - if (g_game) g_game->update(); -#else - game.update(); -#endif +void MainLoop() { + // SDL_GL_MakeCurrent тут не нужен каждый раз + /*glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + SDL_GL_SwapWindow(ZL::Environment::window);*/ + g_game->update(); } +#else +void MainLoop() { + game.update(); +} +#endif #ifdef EMSCRIPTEN EM_BOOL onWebGLContextLost(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) { - delete g_game; - g_game = nullptr; + //delete g_game; + //g_game = nullptr; return EM_TRUE; } EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void* /*userData*/) { - g_game = new ZL::Game(); - g_game->setup(); + //g_game = new ZL::Game(); + //g_game->setup(); return EM_TRUE; } static void applyResize(int logicalW, int logicalH) { // Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung) - double dpr = emscripten_get_device_pixel_ratio(); + /*double dpr = emscripten_get_device_pixel_ratio(); // Вычисляем реальные физические пиксели int physicalW = static_cast(logicalW * dpr); @@ -72,7 +81,7 @@ static void applyResize(int logicalW, int logicalH) { e.window.event = SDL_WINDOWEVENT_RESIZED; e.window.data1 = physicalW; e.window.data2 = physicalH; - SDL_PushEvent(&e); + SDL_PushEvent(&e);*/ } EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) { @@ -91,6 +100,44 @@ EM_BOOL onFullscreenChanged(int /*eventType*/, const EmscriptenFullscreenChangeE return EM_FALSE; } +int main(int argc, char* argv[]) { + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); // Для WebGL 2.0 + + ZL::Environment::window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + glContext_x = SDL_GL_CreateContext(ZL::Environment::window); + + SDL_GL_MakeCurrent(ZL::Environment::window, glContext_x); + + g_game = new ZL::Game(); + g_game->setup(); + + emscripten_set_webglcontextlost_callback("#canvas", nullptr, EM_TRUE, onWebGLContextLost); + emscripten_set_webglcontextrestored_callback("#canvas", nullptr, EM_TRUE, onWebGLContextRestored); + + // Keep Environment::width/height in sync when the canvas is resized. + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_FALSE, onWindowResized); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_FALSE, onFullscreenChanged); + + // 2. ИНИЦИАЛИЗАЦИЯ РАЗМЕРОВ: + // Получаем реальные размеры окна браузера на момент запуска + int canvasW = EM_ASM_INT({ return window.innerWidth; }); + int canvasH = EM_ASM_INT({ return window.innerHeight; }); + + // Вызываем вашу функцию — она сама применит DPR, выставит физический размер + // канваса и отправит SDL_WINDOWEVENT_RESIZED для настройки проекции. + applyResize(canvasW, canvasH); + + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); + + emscripten_set_main_loop(MainLoop, 0, 1); + + + return 0; +} + #endif @@ -178,7 +225,9 @@ extern "C" int SDL_main(int argc, char* argv[]) { } -#else +#endif + +#ifdef WIN32_LEAN_AND_MEAN int main(int argc, char *argv[]) { try @@ -192,7 +241,7 @@ int main(int argc, char *argv[]) { ZL::Environment::height = CONST_HEIGHT; -#ifdef EMSCRIPTEN +/*#ifdef EMSCRIPTEN if (SDL_Init(SDL_INIT_VIDEO) != 0) { std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl; return 1; @@ -251,7 +300,7 @@ int main(int argc, char *argv[]) { SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); emscripten_set_main_loop(MainLoop, 0, 1); -#else +#else*/ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { SDL_Log("SDL init failed: %s", SDL_GetError()); return 1; @@ -277,7 +326,7 @@ int main(int argc, char *argv[]) { game.update(); SDL_Delay(2); } -#endif +//#endif } catch (const std::exception& e) diff --git a/src/network/WebSocketClientEmscripten.cpp b/src/network/WebSocketClientEmscripten.cpp index 9534cf0..3d598e8 100644 --- a/src/network/WebSocketClientEmscripten.cpp +++ b/src/network/WebSocketClientEmscripten.cpp @@ -7,8 +7,8 @@ namespace ZL { void WebSocketClientEmscripten::Connect(const std::string& host, uint16_t port) { // Формируем URL. Обратите внимание, что в Web часто лучше использовать ws://localhost - std::string url = "ws://" + host + ":" + std::to_string(port); - //std::string url = "wss://api.spacegame.fishrungames.com"; + //std::string url = "ws://" + host + ":" + std::to_string(port); + std::string url = "wss://api.spacegame.fishrungames.com"; EmscriptenWebSocketCreateAttributes attr = { url.c_str(), @@ -36,11 +36,8 @@ namespace ZL { // Локальная очередь для минимизации времени блокировки мьютекса std::queue localQueue; - { - std::lock_guard lock(queueMutex); - if (messageQueue.empty()) return; - std::swap(localQueue, messageQueue); - } + if (messageQueue.empty()) return; + std::swap(localQueue, messageQueue); while (!localQueue.empty()) { const std::string& msg = localQueue.front(); @@ -67,7 +64,6 @@ namespace ZL { auto* self = static_cast(userData); if (e->isText && e->data) { std::string msg(reinterpret_cast(e->data), e->numBytes); - std::lock_guard lock(self->queueMutex); self->messageQueue.push(msg); } return EM_TRUE; diff --git a/src/network/WebSocketClientEmscripten.h b/src/network/WebSocketClientEmscripten.h index 2aac059..10a7a49 100644 --- a/src/network/WebSocketClientEmscripten.h +++ b/src/network/WebSocketClientEmscripten.h @@ -18,7 +18,6 @@ namespace ZL { // Очередь для хранения сырых строк от браузера std::queue messageQueue; - std::mutex queueMutex; public: WebSocketClientEmscripten() = default; diff --git a/src/utils/TaskManager.cpp b/src/utils/TaskManager.cpp index aef62a1..c4fe7f1 100644 --- a/src/utils/TaskManager.cpp +++ b/src/utils/TaskManager.cpp @@ -5,35 +5,52 @@ namespace ZL { TaskManager::TaskManager(size_t threadCount) { - workGuard = std::make_unique>(ioContext.get_executor()); + +#ifndef EMSCRIPTEN + workGuard = std::make_unique>(ioContext.get_executor()); for (size_t i = 0; i < threadCount; ++i) { workers.emplace_back([this]() { ioContext.run(); }); } +#endif } void TaskManager::EnqueueBackgroundTask(std::function task) { +#ifdef EMSCRIPTEN + task(); +#else boost::asio::post(ioContext, task); +#endif } TaskManager::~TaskManager() { +#ifndef EMSCRIPTEN workGuard.reset(); // ioContext.run() , ioContext.stop(); // : for (auto& t : workers) { if (t.joinable()) t.join(); } +#endif } void MainThreadHandler::EnqueueMainThreadTask(std::function task) { +#ifndef EMSCRIPTEN std::lock_guard lock(mainThreadMutex); +#endif mainThreadTasks.push(task); } void MainThreadHandler::processMainThreadTasks() { std::function task; +#ifdef EMSCRIPTEN + if (!mainThreadTasks.empty()) { + task = std::move(mainThreadTasks.front()); + mainThreadTasks.pop(); + } +#else // , update { std::lock_guard lock(mainThreadMutex); @@ -42,6 +59,7 @@ namespace ZL mainThreadTasks.pop(); } } +#endif if (task) { task(); // RefreshVBO diff --git a/src/utils/TaskManager.h b/src/utils/TaskManager.h index 66423f8..afb18df 100644 --- a/src/utils/TaskManager.h +++ b/src/utils/TaskManager.h @@ -1,8 +1,12 @@ #pragma once + +#ifndef EMSCRIPTEN #include +#include +#endif + #include #include -#include #include #include @@ -11,12 +15,12 @@ namespace ZL { class TaskManager { private: +#ifndef EMSCRIPTEN boost::asio::io_context ioContext; std::unique_ptr> workGuard; std::vector workers; - +#endif public: - //TaskManager(size_t threadCount = std::thread::hardware_concurrency()); TaskManager(size_t threadCount = 2); // @@ -24,11 +28,13 @@ namespace ZL { // Graceful shutdown ~TaskManager(); - + +#ifndef EMSCRIPTEN boost::asio::io_context& getIOContext() { return ioContext; } +#endif }; @@ -36,8 +42,10 @@ namespace ZL { { private: std::queue> mainThreadTasks; - std::mutex mainThreadMutex; +#ifndef EMSCRIPTEN + std::mutex mainThreadMutex; +#endif public: void EnqueueMainThreadTask(std::function task);