# Рантайм школы: поток и ECS Договорённость, которую код фазы 2 уже выполняет: каждая школа — свой работник, свой Arch `World`, свой файл. Инвариант в `AGENTS.md` совпадает с этим текстом. Экран и defs: [`near-term.md`](near-term.md), [`defs.md`](defs.md). Куда класть код: [`projects.md`](projects.md). Работник (поток, очередь, сейв-файл) живёт в **Server**. То, что он тикает — `School` в **Simulation**. Каталог, с которым школа создана — **Content**, замороженный у работника. ## Что уже почти есть У каждой `School` уже свой Arch `World`. Это оставляем и делаем жёстким правилом: мир не разделяется между школами и не отдаётся чужому потоку. Arch не потокобезопасен. ## Целевая модель — в этом срезе Каждая школа — **актор**: свой поток (`TaskCreationOptions.LongRunning` / выделенный поток, не пул тиков), свой `World`, свои часы, свой **замороженный** каталог def, свой файл на диске. Над ними — тонкий супервизор (вместо сегодняшнего общего цикла): - создать / удалить школу, знать лимит; - держать ящики: `id →` очередь команд этой школы; - собирать **снимки** для `GET /api/schools` (работник публикует неизменяемое состояние, как сейчас цикл публикует `SchoolsState`); - маршрутизировать сокет: `OpenSchool` / `SetRunning` / кадры часов — только в ящик той школы; - при старте процесса поднять школы с диска, при остановке — дождаться записи работников. Супервизор **не** вызывает `World`, не тикает часы, не читает defs инстанса. Работник **не** трогает чужой мир и не ходит в ASP.NET. Файл школы пишет **работник** (create/delete через команду супервизору, снимок часов — редкий, shutdown — обязательный). Тик по-прежнему фиксированный (`SimulationOptions.FixedDeltaTime`), у каждого работника свой таймер. Школы не синхронизируют календарь друг с другом — так и задумано. Пока школ максимум шесть, шесть потоков — нормально. Не ставить тик симуляции на `ThreadPool`: под нагрузкой его заберут запросы и часы начнут плыть. ## Зачем ломать текущий цикл Один поток на все школы дешевле и проще (нет гонок между мирами). Отдельный поток нужен, когда школы перестанут быть «только часы»: моды, карта, ECS. Тогда одна тяжёлая школа не должна останавливать остальные, и набор модов у школы A не должен быть глобальным синглтоном процесса. Каталог def **на школу**, не на процесс: при создании выбирают моды, работник грузит папки и больше их не перечитывает. Иначе правка JSON на диске внезапно меняет уже идущую школу. ## Как это стыкуется с сокетом и меню Как сейчас по смыслу, другое только «кто трогает School»: - входящее с HTTP/WS → команда в ящик школы (или супервизору, если это create/delete); - меню читает опубликованный снимок, без блокировки работника; - исходящие кадры часов — из работника в outbox соединения, не наоборот. Инвариант вместо «только поток цикла трогает registry» становится: **только работник школы трогает её `School` / `World` / каталог; супервизор трогает только таблицу ящиков.** ## Пока не делаем - Потоки на системы внутри одной школы (job system как у RimWorld). - Миграция живой школы на другой набор модов. - Запись сейва на каждом тике.