LittleSim.Web (Blazor WASM + KNI/WebGL) is a real network client now: it connects to the dedicated server over WebSocket (ClientWebSocket maps to the browser socket), applies MrGameEng.Net delta snapshots into its EntityStore and draws pawns through SpriteBatch — fatigue dims them just like on desktop. The server address comes from ?server=ws://host:port in the page URL, defaulting to the page's host on port 9050. The net contract is mirrored in NetContract.cs (KNI and DesktopGL assemblies can't mix until the graphics libraries build per platform) with loud keep-in-sync comments on both sides. Both clients now smooth replicated positions between 10 Hz snapshots: NetLerp + NetSmoothingSystem lerp the visual position toward the latest server position every frame (exponential, ~0.25 s to converge). Verified against a live LittleSim.Server --listen: the browser client connects (server log), draws ~3.3k lit pixels of pawns whose layout changes between samples, and survives 400+ ticks without errors. Found along the way: requestAnimationFrame freezes in hidden windows — the game loop only runs while the tab is visible. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
69 lines
5.5 KiB
Markdown
69 lines
5.5 KiB
Markdown
# Веб-клиент: spike KNI и решение A/B
|
||
|
||
Дата: 2026-06-12. Спайк жил в `spikes/KniWeb`; после успеха повышен до
|
||
**`src/LittleSim.Web`** — рабочего браузерного клиента мультиплеера (Blazor WASM + KNI):
|
||
он подключается к `LittleSim.Server --listen` по WebSocket, применяет дельта-снапшоты
|
||
`MrGameEng.Net` и рисует жителей через WebGL со сглаживанием позиций. Адрес сервера —
|
||
`?server=ws://host:port` в URL страницы (по умолчанию — хост страницы, порт 9050).
|
||
Сетевой контракт там продублирован бинарным зеркалом (`NetContract.cs`) — KNI- и
|
||
DesktopGL-сборки нельзя смешивать, пока графика движка не собирается пер-платформенно.
|
||
|
||
## Вопрос
|
||
|
||
Путь A — «MonoGame в браузере» через [KNI](https://github.com/kniEngine/kni)
|
||
(форк MonoGame с платформой Blazor WebAssembly/WebGL, те же неймспейсы
|
||
`Microsoft.Xna.Framework.*`). Путь B — тонкий веб-клиент без MonoGame
|
||
(TypeScript/PixiJS поверх сетевой репликации). Спайк проверял минимальную
|
||
жизнеспособность пути A: **ядро движка + Friflo + WebGL-спрайт в браузере**.
|
||
|
||
## Что сделано
|
||
|
||
`dotnet new kni-blazor-gl` (пакет шаблонов `nkast.Kni.Templates`, KNI 4.2.9001,
|
||
net8.0) + ProjectReference на `engine/src/MrGameEng.Core` + мини-хост в духе
|
||
`GameHost` поверх KNI `Game`. Сцена: 300 сущностей в Friflo `EntityStore`,
|
||
`QuerySystem` двигает их в Update-фазе (отскок от краёв, seed 42), Draw-фаза
|
||
рисует через KNI `SpriteBatch` (WebGL).
|
||
|
||
## Результат — путь A жизнеспособен
|
||
|
||
- **`MrGameEng.Core` работает в Blazor WASM без изменений**: `EngineContext`,
|
||
`GameClock`, `Scene`/`SceneManager`, тайминг переходов — всё ядро завелось
|
||
как есть (заслуга расслоения Core/Host: в ядре нет ни MonoGame, ни платформы).
|
||
- **Friflo.Engine.ECS 3.6 работает в wasm**: создание сущностей, архетипы,
|
||
`QuerySystem`, `ForEachEntity` — без ошибок в консоли браузера.
|
||
- **KNI 4.2.9001 рендерит через WebGL** с XNA-API: `Game`,
|
||
`GraphicsDeviceManager`, `SpriteBatch`, `Texture2D.SetData` — совпадает с
|
||
кодом, который пишется под десктопный MonoGame.
|
||
- Сборка тривиальна: обычный `Microsoft.NET.Sdk.BlazorWebAssembly` проект,
|
||
никаких wasm-workload-плясок не понадобилось.
|
||
|
||
## Известные ограничения пути A (работа на этапе «веб-клиент»)
|
||
|
||
1. **Пер-платформенная компиляция библиотек движка.** `MrGameEng.Graphics`,
|
||
`Content`, `Audio`, `UI` ссылаются на `MonoGame.Framework.DesktopGL`; для
|
||
веба их надо собирать против пакетов `nkast.*` (типы те же по API, но другие
|
||
сборки). Решение — msbuild-условие (`KniPlatform=BlazorGL` → nkast-пакеты),
|
||
без изменения исходников.
|
||
2. **Шейдеры.** `Renderer2D` использует прекомпилированные `dotnet-mgfxc`
|
||
эффекты — KNI имеет собственный компилятор эффектов; совместимость надо
|
||
проверять отдельным спайком, прежде чем тащить батчер в веб.
|
||
3. **Нет файловой системы.** Моды/дефы/атласы в браузер приезжают по HTTP;
|
||
текущая схема «собрать атласы при старте из PNG» в вебе не работает —
|
||
атласы пре-билдятся и кладутся в `wwwroot` (или приезжают с сервера).
|
||
4. **Потоки.** `Task.Run`-загрузка контента и `Thread.Sleep`-пейсинг не для
|
||
браузера (клиенту `HeadlessHost.Run` и не нужен — цикл гонит
|
||
`requestAnimationFrame` через KNI).
|
||
5. **Производительность не мерялась** (300 спрайтов — гладко); бюджет
|
||
сущностей в wasm-интерпретаторе будет заметно ниже десктопного, замерять
|
||
на реальной сцене с включённым AOT.
|
||
|
||
## Решение
|
||
|
||
**Путь A (KNI)** — основной для веб-клиента: переиспользуем ядро, сцены и
|
||
в перспективе графику движка; код игры один на все платформы. Путь B остаётся
|
||
запасным, если упрёмся в шейдеры (п. 2) или производительность (п. 5).
|
||
|
||
Порядок работ не меняется: сначала `MrGameEng.Net` (WebSocket-транспорт +
|
||
репликация — нужен любому пути) и сетевой мультиплеер на десктопе, затем
|
||
`MrGameEng.Host.Web` поверх KNI по образцу спайка.
|