From d85d86a3dce941cb2d55c11c4eb22d2aff36843f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Thu, 8 Oct 2026 07:12:01 +0500 Subject: [PATCH] Recognise the coa-bots world server and explain the undo step Co-Authored-By: Claude Sonnet 5.5 --- ...261008-world-server-from-coa-bots-folder.md | 7 +++++++ crates/coa-core/src/process.rs | 18 +++++++++++++++--- crates/coa-core/src/update.rs | 6 +++++- src/i18n/locales/de.ts | 2 +- src/i18n/locales/en.ts | 2 +- src/i18n/locales/es.ts | 2 +- src/i18n/locales/fr.ts | 2 +- src/i18n/locales/ru.ts | 2 +- src/i18n/locales/zh.ts | 2 +- 9 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 changelog.d/20261008-world-server-from-coa-bots-folder.md diff --git a/changelog.d/20261008-world-server-from-coa-bots-folder.md b/changelog.d/20261008-world-server-from-coa-bots-folder.md new file mode 100644 index 0000000..a6e3f1e --- /dev/null +++ b/changelog.d/20261008-world-server-from-coa-bots-folder.md @@ -0,0 +1,7 @@ +--- +area: manager +type: fixed +audience: admins +title: A world server started from the coa-bots folder is recognised +--- +The Manager no longer reports a port conflict when the world server runs from the coa-bots folder inside the server folder, and a failed update now says that undoing it can take several minutes. diff --git a/crates/coa-core/src/process.rs b/crates/coa-core/src/process.rs index 646eead..3e38392 100644 --- a/crates/coa-core/src/process.rs +++ b/crates/coa-core/src/process.rs @@ -278,12 +278,24 @@ fn uptime_secs(p: &ProcessIdentity) -> Option { (now >= p.created).then(|| (now - p.created) / 10_000_000) } +/// The world server of the `coa-bots` repack folder lives inside the server folder and is started by its own +/// launcher (`coa-bots\Core\worldserver.exe`). It serves the same realm, so it counts as this server's world. +fn world_alternatives(root: &Path) -> Vec { + vec![root.join("coa-bots").join("Core").join("worldserver.exe")] +} + fn service(root: &Path, name: &'static str, exe: PathBuf, port: u16, listen: &[(u16, u32)]) -> ServiceStatus { + service_with(root, name, exe, &[], port, listen) +} + +fn service_with(root: &Path, name: &'static str, exe: PathBuf, alternatives: &[PathBuf], port: u16, listen: &[(u16, u32)]) -> ServiceStatus { // Prefer the repack's own record when it still matches a live process; otherwise discover by exact exe path. + let norm = |p: &Path| p.to_string_lossy().replace('/', "\\"); + let expected: Vec = std::iter::once(&exe).chain(alternatives).map(|p| norm(p)).collect(); let proc = read_state_record(root, name) .filter(is_alive) - .filter(|r| r.exe.eq_ignore_ascii_case(&exe.to_string_lossy().replace('/', "\\"))) - .or_else(|| find_by_exe(&exe).into_iter().next()); + .filter(|r| expected.iter().any(|e| r.exe.eq_ignore_ascii_case(e))) + .or_else(|| std::iter::once(&exe).chain(alternatives).find_map(|p| find_by_exe(p).into_iter().next())); let owner = listen.iter().find(|(p, _)| *p == port).map(|(_, pid)| *pid); let (state, port_ready, conflict) = match (&proc, owner) { @@ -319,7 +331,7 @@ pub fn observe(root: &Path, ports: &Ports) -> Observed { Observed { mysql: service(root, "mysql", mysql, ports.mysql, &listen), auth: service(root, "auth", auth, ports.auth, &listen), - world: service(root, "world", world, ports.world, &listen), + world: service_with(root, "world", world, &world_alternatives(root), ports.world, &listen), secondary_world, } } diff --git a/crates/coa-core/src/update.rs b/crates/coa-core/src/update.rs index 96c3ba9..ab83667 100644 --- a/crates/coa-core/src/update.rs +++ b/crates/coa-core/src/update.rs @@ -492,9 +492,13 @@ pub fn apply(p: &Params, report: &dyn Fn(&str, u8)) -> Result { Ok(r) if r.failed.is_none() => migrated = Some(r), Ok(r) => { let (mid, why) = r.failed.clone().unwrap(); + step(report, "Undoing the update", 76); return fail_after_apply(p, &mut txn, &before, &tree, format!("Database update {mid} failed: {why}"), Some(r)); } - Err(e) => return fail_after_apply(p, &mut txn, &before, &tree, e.to_string(), None), + Err(e) => { + step(report, "Undoing the update", 76); + return fail_after_apply(p, &mut txn, &before, &tree, e.to_string(), None); + } } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 831611a..7eee400 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -495,7 +495,7 @@ const de: Partial> = { "ustep.Updating the database": "Datenbank wird aktualisiert", "ustep.Starting the updated server": "Aktualisierter Server wird gestartet", "ustep.Finishing": "Abschluss", - "ustep.Undoing the update": "Update wird rückgängig gemacht", + "ustep.Undoing the update": "Update wird rückgängig gemacht (das Wiederherstellen der Datenbanken kann mehrere Minuten dauern)", "ustep.Done": "Fertig", // Companion sizes diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index a7aa2d8..4c058c4 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -530,7 +530,7 @@ const en = { "ustep.Updating the database": "Updating the database", "ustep.Starting the updated server": "Starting the updated server", "ustep.Finishing": "Finishing", - "ustep.Undoing the update": "Undoing the update", + "ustep.Undoing the update": "Undoing the update (restoring the databases can take several minutes)", "ustep.Done": "Done", // Companion sizes diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index af95035..418d13e 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -495,7 +495,7 @@ const es: Partial> = { "ustep.Updating the database": "Actualizando la base de datos", "ustep.Starting the updated server": "Iniciando el servidor actualizado", "ustep.Finishing": "Finalizando", - "ustep.Undoing the update": "Deshaciendo la actualización", + "ustep.Undoing the update": "Deshaciendo la actualización (restaurar las bases de datos puede tardar varios minutos)", "ustep.Done": "Hecho", // Companion sizes diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index c3a7ab3..cee9ef5 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -495,7 +495,7 @@ const fr: Partial> = { "ustep.Updating the database": "Mise à jour de la base de données", "ustep.Starting the updated server": "Démarrage du serveur mis à jour", "ustep.Finishing": "Finalisation", - "ustep.Undoing the update": "Annulation de la mise à jour", + "ustep.Undoing the update": "Annulation de la mise à jour (la restauration des bases de données peut prendre plusieurs minutes)", "ustep.Done": "Terminé", // Companion sizes diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index a38477c..a8eb78b 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -528,7 +528,7 @@ const ru: Partial> = { "ustep.Updating the database": "Обновление базы данных", "ustep.Starting the updated server": "Запуск обновлённого сервера", "ustep.Finishing": "Завершение", - "ustep.Undoing the update": "Откат обновления", + "ustep.Undoing the update": "Отмена обновления (восстановление баз данных может занять несколько минут)", "ustep.Done": "Готово", // Companion sizes diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index 1b88128..d0d0ecf 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -507,7 +507,7 @@ const zh = { "ustep.Updating the database": "正在更新数据库结构", "ustep.Starting the updated server": "正在启动更新后的服务器", "ustep.Finishing": "正在收尾完成", - "ustep.Undoing the update": "正在撤销更新并回滚", + "ustep.Undoing the update": "正在撤销更新(恢复数据库可能需要几分钟)", "ustep.Done": "已完成", // Companion sizes