Module confs get newly documented keys from their .dist; sharing also allows remote CoA clients

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ДмитрийandClaude Sonnet 5.5 committed 2026-09-30 20:28:42 +05:00
1 parent db16d95558
commit fb25ffa04b
2 files changed
+43 -4

No files matched your search

+16 -4
View File
@@ -122,7 +122,9 @@ pub struct SettingsView {
/// Modules read `Core/configs/modules/<name>.conf`, but packages ship only the documented `<name>.conf.dist` (the active
/// files are the server owner's). Create every missing active file from its `.dist`, so a fresh server runs with the
/// documented defaults (for example `CoA.Enable = 1`, without which the Ascension client is dropped after login).
/// Existing files are never touched. Files the launcher writes itself are skipped. Returns the created file names.
/// An existing file only receives keys its `.dist` gained since (added with their documentation, values never changed),
/// so a server updated to a build with a new setting gets that setting's documented default. Files the launcher writes
/// itself are skipped. Returns what was created (`name.conf`) or extended (`name.conf (+n keys)`).
pub fn materialize_module_configs(root: &Path) -> Result<Vec<String>> {
let dir = root.join("Core").join("configs").join("modules");
let mut created = Vec::new();
@@ -137,6 +139,14 @@ pub fn materialize_module_configs(root: &Path) -> Result<Vec<String>> {
if !active.exists() {
fs::copy(e.path(), &active)?;
created.push(format!("{stem}.conf"));
} else if let (Ok(have), Ok(dist)) = (fs::read(&active), fs::read(e.path())) {
if let (Ok(mut conf), Ok(dist)) = (parser::ConfFile::parse_bytes(&have), parser::ConfFile::parse_bytes(&dist)) {
let plan = merge::apply(&mut conf, &dist);
if !plan.added.is_empty() {
fsx::atomic_write(&active, conf.to_text().as_bytes())?;
created.push(format!("{stem}.conf (+{} keys)", plan.added.len()));
}
}
}
}
created.sort();
@@ -495,16 +505,18 @@ mod tests {
fs::write(m.join("coa.conf.dist"), "CoA.Enable = 1
").unwrap();
fs::write(m.join("spellbook.conf.dist"), "Spellbook.Enable = 1
Spellbook.New = 5
").unwrap();
fs::write(m.join("spellbook.conf"), "Spellbook.Enable = 0
").unwrap();
fs::write(m.join("coa_bugreport.conf.dist"), "x = 1
").unwrap();
assert_eq!(materialize_module_configs(&root).unwrap(), vec!["coa.conf".to_string()]);
assert_eq!(materialize_module_configs(&root).unwrap(), vec!["coa.conf".to_string(), "spellbook.conf (+1 keys)".to_string()]);
assert_eq!(fs::read_to_string(m.join("coa.conf")).unwrap(), "CoA.Enable = 1
");
assert_eq!(fs::read_to_string(m.join("spellbook.conf")).unwrap(), "Spellbook.Enable = 0
");
let sb = fs::read_to_string(m.join("spellbook.conf")).unwrap();
assert!(sb.contains("Spellbook.Enable = 0"), "existing values are never changed: {sb}");
assert!(sb.contains("Spellbook.New = 5"), "new documented key was added: {sb}");
assert!(!m.join("coa_bugreport.conf").exists());
assert!(materialize_module_configs(&root).unwrap().is_empty());
}
+27
View File
@@ -89,6 +89,18 @@ pub fn set_open(root: &Path, meta: &Path, open: bool) -> Result<bool> {
originals.push((p.clone(), bytes));
edits.push((p, conf.to_text()));
}
// Friends connect from other addresses, and the server only applies the CoA client protocol (extension packets,
// header mode) to loopback unless remote clients are allowed - so sharing must switch that on as well.
let coa_conf = root.join("Core/configs/modules/coa.conf");
if let Ok(bytes) = fs::read(&coa_conf) {
let mut conf = ConfFile::parse_bytes(&bytes)?;
let want_remote = if open { "1" } else { "0" };
if conf.get("CoA.AllowRemoteClients").map(str::trim) != Some(want_remote) {
conf.set("CoA.AllowRemoteClients", want_remote, &["Set by CoA Server Manager together with the friends mode"]);
originals.push((coa_conf.clone(), bytes));
edits.push((coa_conf, conf.to_text()));
}
}
if edits.is_empty() {
return Ok(false);
}
@@ -200,6 +212,21 @@ mod tests {
assert!(fs::read_to_string(root.join("Core/configs/worldserver.conf")).unwrap().contains("BindIP = \"127.0.0.1\""));
}
#[test]
fn sharing_also_allows_remote_coa_clients_and_closing_takes_it_back() {
let (_d, root, meta) = setup();
let coa = root.join("Core/configs/modules/coa.conf");
fs::create_dir_all(coa.parent().unwrap()).unwrap();
fs::write(&coa, "CoA.Enable = 1
CoA.AllowRemoteClients = 0
").unwrap();
assert!(set_open(&root, &meta, true).unwrap());
assert!(fs::read_to_string(&coa).unwrap().contains("CoA.AllowRemoteClients = 1"));
assert!(fs::read_to_string(&coa).unwrap().contains("CoA.Enable = 1"));
assert!(set_open(&root, &meta, false).unwrap());
assert!(fs::read_to_string(&coa).unwrap().contains("CoA.AllowRemoteClients = 0"));
}
#[test]
fn a_shared_server_gets_its_bind_address_back_before_start_and_a_local_one_is_left_alone() {
let (_d, root, meta) = setup();