The page was a mock: it never read the database (always 0 accounts),
showed a hard-coded schema and core version, looked for server files in
the wrong folder, and its "transfer" reported accounts, characters and
tables as moved without moving anything. Its locale was fixed to "ru".
- remove server_migration.rs and its three commands
- modular_copy::preview: classic/modular core, version, database state,
counts and schemas from a running database (read only), database size,
settings files, extra worlds; nothing is started or changed
- the page shows that and hands over to the same modular-copy install
screen as "Manage another server"; texts in en and ru via i18n
- the modular copy now carries world settings changed in Server
Configuration (Settings/worldserver.conf.template was not copied)
- E2E preview_bare_and_copy_e2e checks the preview and the carried
setting; passes against preview core 0.261108.9501
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A console shutdown request is sometimes lost, and the world then keeps
running until the stop times out. While the world still runs and its
console still answers after 15 s, the launcher asks again (twice at most);
asking twice is harmless. Verified on a disposable repack with the first
World::ShutdownServ call suppressed in a debugger: the second request
stopped the world.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Manager starts servers with coa-core::launcher (no Python bridge),
the bug-report relay runs as coa-launcher relay-worker, and manage.py is
no longer edited. Preview identity in tauri.conf.json is kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Repacks ship no .bat files, and the Manager starts servers with
coa-core::launcher, so the realm-profile patch it wrote into manage.py
served no one. It is no longer applied (realm creation, realm switching and
start).
Recognising the edits older Managers made (realm patch, bridge import fix)
moves into legacy_launcher, so updates and file checks still treat such a
file as unmodified. Nothing there writes a file.
manage.py itself stays complete in the repack: Managers up to 0.9.10 run it
through their bridge and patch its text, so it cannot become a thin wrapper
until those Managers are no longer supported.
Verified: coa-core unit tests; examples/manager_launcher_e2e on a disposable
repack (manage.py unchanged after a start with a realm profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
launcher::relay does what BugReport/relay.py and manage.py relay-worker do,
with the same spool: the <key>.status files the world reads, the journal
relay.sqlite3 (deliveries, settings), relay.lock (byte 0) and the
relay-ready record. Service answers map to the same states and retry times;
a post interrupted mid-flight stays uncertain and is never resent.
When coa-launcher.exe supervises a world it runs the relay itself; the
Python supervisor still runs relay.py.
Verified:
- examples/relay_equivalence: the same reports and scripted service answers
through relay.py and the Rust relay give identical statuses, journal rows
(including retry times) and posts, and each continues the other's journal.
- examples/manager_launcher_e2e on a disposable repack: supervisor and relay
are coa-launcher.exe, start/stop interoperates with manage.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
send_without_answer closed the socket right after writing "server shutdown".
With the reply still unread, Windows resets the connection, and the world
can drop the command: a world supervised by coa-launcher.exe ignored the
shutdown and the stop timed out after 90 seconds. The reply is now read up
to the next prompt, the end of the connection or 10 seconds.
The E2E also checks which supervisor runs the world and that manage.py
stops a world the Manager started.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
driver::run now calls coa-core::launcher::ops::run in-process instead of
running Scripts/manage.py through launcher_bridge.py. Failures carry a typed
code (Error::Service) instead of being guessed from stderr text.
Moved into the launcher: realm profiles (realm id, name and databases from
Settings/realm-profile.json), extra worlds sharing the main database,
the shared world startup deadline and console readiness check, the owned-world
check on shutdown, force-stop-all, and an environment block without
AC_REALM_SERVER_PORT for update validation.
The world supervisor is Scripts/coa-launcher.exe when the repack ships it,
otherwise manage.py watch-world. Extra worlds get an unmodified manage.py.
The realm patch of manage.py stays for the repack's own .bat files.
Verified with examples/manager_launcher_e2e on a disposable repack.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std::process::Command lets a Windows child inherit every inheritable handle of the parent: a
MySQL started by the launcher kept the caller's files and pipes open (found by the SQUID test,
whose log stayed locked). Services now start through CreateProcessW with
PROC_THREAD_ATTRIBUTE_HANDLE_LIST = [log, NUL], as Python's subprocess does. The MySQL client's
outputs are read while it runs (a full pipe stopped it) and its last error line is reported.
E2E on a disposable repack: launcher_e2e PASS again; squid_equivalence PASS: Rust and
squid_playerbots.py leave identical journals, acore_playerbots tables (row counts) and
playerbots.conf (4106 lines), and a second run of either changes nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- services: exclusive-bind port check, readiness by listener row or the server side of a
loopback connection, MySQL client, start MySQL/auth, stop-all (world through its console,
database last), status.
- world: start-world through a supervisor process (coa-launcher watch-world) that keeps the
bug-report relay (still BugReport/relay.py via the repack's Python) alive; detailed logging.
- squid: squid_playerbots.py in Rust with the same journals (coa_squid_migrations,
coa_squid_pending, coa_bots_installed).
- coa-launcher.exe: the manage.py commands and error texts (the Manager reads them).
- Services no longer inherit the launcher's stdin/stdout/stderr: a server holding the
output pipe kept whoever read the launcher's output waiting.
E2E (examples/launcher_e2e, disposable stable repack on free ports): coa-launcher start-all ->
manage.py sees and stops it; manage.py start-all -> coa-launcher sees and stops it; a second
Rust-only cycle leaves nothing running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First part of moving Scripts/manage.py into coa-core (plan §17, PY-1). Same formats on disk as
manage.py so both can run on one server: .state records, the byte-0 control lock msvcrt.locking
takes, configuration.json. prepare renders byte-for-byte what manage.py renders (checked against
the real script, COA_MANAGE_PY). State files are flushed to disk before they replace the old ones,
and a damaged record counts as no process instead of failing every later start.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows were separate grids with an auto-width action column, so the columns moved from row
to row; every column has a fixed width now. CoA's modules are not in the public catalogue:
their last push comes from the GitHub API (cached 12 h).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Build cache (build_cache.rs): the build folder of a module is unpacked for a
build and packed into <module>.tar.zst afterwards (object files ~4-6x
smaller), modification times kept so Ninja recompiles only what changed.
A cache made for another core is dropped instead of unpacked; .ilk and .map
files are not kept; total limit 2 GB, least recently built first. The build
log moves next to the archive (build-cache/<module>.log).
- "Build cache" in the Modules menu: size per module (packed, built when, for
this core or not), remove one, remove all, remove downloaded sources.
Builds now hold the server's busy guard, so the cache is never cleared
under a running build.
- Module sources: "Add from a link" downloads GitHub's archive of the commit
instead of a full git clone, and game-client files (.mpq, .dbc, maps,
textures, archives) are left out and listed in .coa_skipped_assets.
mod-bg-battle-for-gilneas: 365 MB repository, 3.1 MB kept (was refused at
the 300 MB limit).
- Overlay: Helias's battleground modules (Gilneas, Twin Peaks, Slavery Valley)
are Incompatible: they need the server's DBC files replaced (CoA ships its
own) and a client patch.
Checked: build_cache unit tests; examples/build_cache_ninja_check with real
MSVC + CMake + Ninja: after pack/unpack nothing is recompiled, after one edit
one file is; coa-core lib tests 865 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Header is just "Modules" with "Add from a link" (the GitHub URL bar is folded
away until asked for) and a menu for builds/logs, build tools and the
diagnostics export. The build-tools bar shows only when something is missing.
- Two tabs (Installed, Catalog) styled like the settings page.
- Catalogue: one list of rows (name, owner, category, stars, one action); CoA's
own modules first. Compatibility and sort are drop-downs; 20 functional
categories as chips with counts. "Checked by CoA" is the curated list only.
- Categories: owner's reviewed list (181 modules, high/medium confidence) plus
keyword rules for every other module (coa-module-categories.json).
- Catalogue statuses come from the current overlay on every read (a cached copy
kept the old Verified marks), and no more made-up entries for overlay rules
the public catalogue does not list (fake AGPL-3.0, 0 stars, duplicate mod-cfbg).
- Window size, position and maximized state persist (tauri-plugin-window-state,
visibility left to the app).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overlay reason was drawn as a red blocked notice on every card, so modules
marked Verified looked incompatible. mod-cfbg is built into the CoA core and is
now Incompatible; the Verified marks on duel-reset, pvp-titles, starter-guild and
skip-dk-starting-area had no build or test behind them and are removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A new server goes on to the preview core without compiling any module
(recommended modules are declined once; modules come from the Modules page).
- Choosing a server with the classic core makes a modular copy next to it:
every acore_* database (read from the running database, or from a private
copy of the stopped data files opened by the new server's own mysqld),
module configs and realm profiles, ports and console login. The old server
is only read. The copy then takes the preview core with the modules the old
server had switched on.
- Installs and the preview update run on unused ports, so a server running on
the shipped ports (the old one, or any other) no longer stops them; the
schema check after "Preparing database" used the shipped port before.
- Dungeon Clear is part of SQUID's Playerbots: no card of its own (it said
"Always on" while SQUID was off), switched with SQUID, settings on the SQUID card.
E2E (examples/preview_bare_and_copy_e2e, disposable servers, another server
running on the shipped ports): fresh install committed with no plugins and a
clean boot report; copy of a stopped and of a running classic server: data
identical, old server's files byte-for-byte unchanged, Content Scaling and
Companions built on the copy, preview core committed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Modules page had a 'Register test-realm-v03' button and the app registered a hardcoded local
folder into an empty server list at startup. Both pointed at a developer's machine. The startup
registration now happens only when COA_TEST_REALM_DIR is set. test_detect_toolchain skips when the
local spike realm is absent (CI runners).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build_module_with_bundle reports a failed compile as Ok(success: false). rebuild ignored the flag,
installed the package left by the previous build (made for the old core) and only then switched the
module off as 'does not match the installed core'. Now the failed build stops there and the reason
names the first compiler error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a stable->preview update the recommended modules are built from their sources and deploy
their own .conf.dist over the one the package shipped. The next core update saw a file that
matched neither the recorded nor the new hash and stopped with 'modified outside CoA Server
Manager'. A template that exactly matches what an installed module deploys is now skipped.
Adds preview_core_update_e2e (core with a new plugin ABI on an existing preview server).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
preview_channel_e2e installs from the stable channel, takes the preview from the published preview channel
the way the Manager does, and checks the server is current afterwards. preview_migration_e2e gains a squid
mode that keeps SQUID's SQL journals and expects SQUID's Playerbots instead of Companions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The core matches a DLL's .coapl id with its plugin lock entry. The adapter
used the repository name (mod-coa-content-scaling) while the registry and
lock use the module id (content-scaling), so the stable->preview e2e saw the
core reject Content Scaling. The build now writes the module id and refuses
a DLL that does not carry it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
derive-preview takes the higher of the requested minimum and the stable
package's. Adds the stable->preview migration e2e example (disposable server,
own ports, seeded character and module setting, production updater).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stable packages from 0.261104.94 need Manager 0.9.8; the preview was 0.9.7.
Conflicts kept the preview's module screen and plugin-aware module logic, took
master's requires-squid compatibility value and version 0.9.9 under the
preview's own product name and identifier.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
coa-release derive-preview verifies the stable update package, unpacks it,
replaces the core binaries with the plugin host build (same core commit),
adds Core/plugin-sdk.zip and Core/plugins/recommended.json, and repacks it
with the stable migrations, configuration and scripts unchanged. The result
is signed with the preview key by coa-release sign.
Tried on server-0.261104.94 and a CI plugin host: 37256 files, 3417
migrations, signature/archive/schema contract verified; the stable key
rejects the package (env-driven test).
New servers in the preview Manager are installed from the stable image; the
preview core arrives as the first update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- keys/preview-signing.pub: preview packages and the preview pointer are
signed with a separate key, so a stable Manager (stable key only) can never
accept them. The preview Manager trusts both keys for packages (it starts
from stable servers and stable base packages); each channel pointer must
carry its own key's signature.
- Channel 'preview': pointer preview.json on the preview-channels branch,
releases preview-server-<version>. The app resolves it first and falls back
to stable until a preview release exists.
- coa-release public-key prints the public half of the signing key;
sign-channel writes the channel's own release tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A core that loads Content Scaling, Companions and SQUID as plugins ships
Core/plugins/recommended.json with the module repositories and commits. During
the update (the move from a monolithic stable server) the Manager downloads
each module as GitHub's archive of that commit (no git needed), builds it with
its bundled trees, installs and enables it, before the trial start. Rules keep
the bot choice: SQUID only if it was switched on in playerbots.conf, otherwise
Companions. Handled modules are remembered so a removed module is not
reinstalled; a failed one is retried at the next core change.
Verified on a CI core: Content Scaling (+packs) and Companions downloaded,
built, installed and written to the plugin lock in 225 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modules must be compiled by the core's exact compiler. The Toolchain tab now
offers Microsoft's stable-channel Build Tools bootstrapper (VS 18) with the
14.44 toolset, core build tools, Windows SDK 26100 and CMake preselected:
downloaded from aka.ms, Authenticode-checked for Microsoft, run in its own UI
(modify mode for an existing Build Tools 18). Shows the required and selected
cl.exe versions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The core changes almost daily and a plugin loads only into the core it was
built for. update::apply now brings every enabled plugin to the new core
before the trial start: a build already in the store is reused, otherwise it
is rebuilt from the sources recorded at its last build (build recipe). A
plugin that cannot be rebuilt is switched off with the reason (and plugins
that depend on it); the core update always goes ahead. A rollback puts the
builds for the restored core back. The update result lists rebuilt and
switched-off modules.
Also: plugin.json records the real compiler instead of a fixed string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The loader is chosen only from directories the build compiles: Companions
has a reference pilot/ whose loader was picked and never compiled (LNK2019).
- A bundled tree inside the primary one (Content Scaling's content-packs/)
is compiled only as the bundle (was LNK2005 on every pack symbol).
- Env-driven test builds the preview modules against a CI core pair and
requires the realm's host fingerprint.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finds every Visual Studio / Build Tools install with vswhere, reads each toolset's cl.exe
version, and calls vcvars64 (or VsDevCmd in a minimal Build Tools install) with -vcvars_ver
for the one the SDK's compiler= input names. A missing toolset is reported with the version
needed and the ones installed. CMake/Ninja come from any install, not only VS 18 Insiders.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A core update or rollback then always brings the matching SDK; an archive of
another core is ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The host records its own ABI fingerprint (.coapl); the manager reads it from
worldserver.exe instead of hardcoded fingerprints (solver, provenance, analyzer).
- SDK store per realm: <realm>/.manager/sdk/<16 hex of fingerprint>/, chosen by
the host fingerprint; install_realm_sdk refuses an SDK of another core.
- Dungeon Clear is built into the SQUID DLL (bundle); persistent build cache.
- Refuse layouts whose header paths exceed Ninja's Windows path limit (such
headers counted as changed and every build became a full rebuild).
- Compile jobs limited by free RAM; full build log kept in build.log; failures
without an MSVC diagnostic report the failed step and the log tail.
Bundle test: full build 1425 s, after a one-file change 9.7 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Removed COMPANIONS_SHIMS_CPP: it redefined core functions inside the plugin as
stubs (Unit::CalculateSpellDamage/SpellDamageBonusDone/Taken -> 0,
AuraEffect::GetMiscValue -> 0, LFGMgr::GetDungeon/GetOptions/GetLockedDungeons,
Player::HasQuestForItem -> false, ...) and replaced the whole training arena
with empty functions. The DLL linked, but bots silently lost real damage, LFG
and quest logic. The core now exports those symbols (spike/plugin-host 9575b8d8e,
plus SQUID's in the next commit there).
- Stopped dropping training/ and world/encounters/ sources and the string
rewriting of BotMgr.cpp (upstream already uses the public guild API).
- Qualification reads the fingerprint from the DLL's .coapl section and compares
it with the SDK's sdk.json instead of a hard-coded constant.
- Real-build tests take turns (the engine allows one build at a time).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>