Skip to content

Commit 5231b2b

Browse files
authored
Merge pull request #197 from samsoir/feat/152-disable-overlays-config
feat(packages): runtime control to disable XEL overlays (#152)
2 parents 4ffd973 + 23af031 commit 5231b2b

11 files changed

Lines changed: 150 additions & 12 deletions

File tree

‎docs/configuration.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ Controls package manager behavior.
429429
| `install_location` | path | `~/.xearthlayer/packages` | Directory for storing installed packages |
430430
| `custom_scenery_path` | path | (auto-detect) | X-Plane Custom Scenery directory for overlay symlinks |
431431
| `auto_install_overlays` | bool | `false` | Automatically install matching overlay when installing ortho |
432+
| `disable_overlays` | bool | `false` | Suppress XEL overlays at runtime (see notes below) |
432433
| `temp_dir` | path | `~/.xearthlayer/tmp` | Temporary directory for package downloads |
433434
| `concurrent_downloads` | Integer | `5` | Number of concurrent part downloads (1-10) |
434435

@@ -439,13 +440,15 @@ Controls package manager behavior.
439440
install_location = ~/.xearthlayer/packages
440441
custom_scenery_path = /home/user/X-Plane 12/Custom Scenery
441442
auto_install_overlays = true
443+
disable_overlays = false
442444
temp_dir = ~/Downloads/xearthlayer-temp
443445
```
444446

445447
**Notes:**
446448
- The default `library_url` points to the official XEarthLayer package library; override only if using a custom package source
447449
- The `temp_dir` is used for downloading archives before extraction; files are cleaned up after installation. The default avoids the system temp directory which is often RAM-backed (tmpfs) on Linux
448450
- When `auto_install_overlays` is enabled, installing an ortho package will automatically install the matching overlay package for the same region (if available)
451+
- `disable_overlays` is a **runtime** control. When set to `true`, `xearthlayer run` removes the consolidated `yzXEL_overlay/` symlink folder from Custom Scenery on startup so X-Plane does not load XEarthLayer overlays. Overlay packages remain downloaded and stored locally — flipping the setting back to `false` and restarting reinstates them with no re-download. Use this when running third-party overlay scenery (e.g. SimHeaven, Prefab Objects) that conflicts with XEL overlays. Distinct from `auto_install_overlays`, which controls whether overlays are *downloaded* alongside ortho packages.
449452
- If `custom_scenery_path` is not set, it falls back to `[xplane] scenery_dir` or auto-detects from X-Plane installation
450453

451454
---
@@ -569,6 +572,7 @@ file = ~/.xearthlayer/xearthlayer.log
569572
; install_location = ~/.xearthlayer/packages
570573
; custom_scenery_path = /path/to/X-Plane 12/Custom Scenery
571574
; auto_install_overlays = true
575+
; disable_overlays = false
572576
; temp_dir = ~/Downloads/xearthlayer-temp
573577

574578
[patches]
@@ -699,6 +703,7 @@ Run 'xearthlayer config upgrade' to update your configuration.
699703
| `packages.install_location` | path | Package installation directory |
700704
| `packages.custom_scenery_path` | path | Custom Scenery for overlays |
701705
| `packages.auto_install_overlays` | `true`, `false` | Auto-install matching overlays |
706+
| `packages.disable_overlays` | `true`, `false` | Suppress XEL overlays at runtime |
702707
| `packages.temp_dir` | path | Temporary download directory |
703708
| `logging.file` | path | Log file location |
704709

‎xearthlayer-cli/src/commands/packages/args.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ pub struct InstallArgs {
160160
pub custom_scenery_path: Option<PathBuf>,
161161
/// Automatically install overlay package when installing ortho for the same region.
162162
pub auto_install_overlays: bool,
163+
/// Skip rebuild of the consolidated `yzXEL_overlay/` symlink folder when
164+
/// true. The package is still downloaded and installed; only the symlink
165+
/// folder X-Plane reads is left alone. The next `xearthlayer run` will
166+
/// remove any stale folder. See `packages.disable_overlays` config key.
167+
pub disable_overlays: bool,
163168
/// Number of concurrent part downloads.
164169
pub concurrent_downloads: usize,
165170
}
@@ -174,6 +179,8 @@ pub struct UpdateArgs {
174179
pub all: bool,
175180
/// Path to X-Plane Custom Scenery directory for overlay symlinks.
176181
pub custom_scenery_path: Option<PathBuf>,
182+
/// See `InstallArgs::disable_overlays`.
183+
pub disable_overlays: bool,
177184
/// Number of concurrent part downloads.
178185
pub concurrent_downloads: usize,
179186
}
@@ -186,6 +193,8 @@ pub struct RemoveArgs {
186193
pub force: bool,
187194
/// Path to X-Plane Custom Scenery directory for overlay symlinks.
188195
pub custom_scenery_path: Option<PathBuf>,
196+
/// See `InstallArgs::disable_overlays`.
197+
pub disable_overlays: bool,
189198
}
190199

191200
/// Arguments for the info command.

‎xearthlayer-cli/src/commands/packages/handlers.rs‎

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,19 @@ impl CommandHandler for CheckHandler {
188188
fn rebuild_consolidated_overlay(
189189
install_dir: &std::path::Path,
190190
custom_scenery_path: &Option<std::path::PathBuf>,
191+
disable_overlays: bool,
191192
ctx: &CommandContext<'_>,
192193
) {
194+
if disable_overlays {
195+
// Per `packages.disable_overlays` the consolidated overlay is suppressed
196+
// at runtime. We deliberately leave any existing folder alone here; the
197+
// next `xearthlayer run` removes it during startup. Issue #152.
198+
ctx.output.println(
199+
"Note: packages.disable_overlays = true; skipping consolidated overlay rebuild. \
200+
The folder will be removed on next 'xearthlayer run'.",
201+
);
202+
return;
203+
}
193204
let Some(ref scenery_path) = custom_scenery_path else {
194205
ctx.output
195206
.println("Note: No Custom Scenery path configured. Overlay symlink not created.");
@@ -292,7 +303,12 @@ impl InstallHandler {
292303
));
293304

294305
// Rebuild consolidated overlay to include the newly installed overlay
295-
rebuild_consolidated_overlay(&args.install_dir, &args.custom_scenery_path, ctx);
306+
rebuild_consolidated_overlay(
307+
&args.install_dir,
308+
&args.custom_scenery_path,
309+
args.disable_overlays,
310+
ctx,
311+
);
296312
}
297313
}
298314

@@ -373,7 +389,12 @@ impl CommandHandler for InstallHandler {
373389
// Rebuild consolidated overlay for overlay packages
374390
if args.package_type == PackageType::Overlay {
375391
ctx.output.newline();
376-
rebuild_consolidated_overlay(&args.install_dir, &args.custom_scenery_path, ctx);
392+
rebuild_consolidated_overlay(
393+
&args.install_dir,
394+
&args.custom_scenery_path,
395+
args.disable_overlays,
396+
ctx,
397+
);
377398
}
378399

379400
// Auto-install overlay when installing ortho (if enabled)
@@ -543,7 +564,12 @@ impl CommandHandler for UpdateHandler {
543564
// Rebuild consolidated overlay if any overlay packages were updated
544565
if overlay_updated {
545566
ctx.output.newline();
546-
rebuild_consolidated_overlay(&args.install_dir, &args.custom_scenery_path, ctx);
567+
rebuild_consolidated_overlay(
568+
&args.install_dir,
569+
&args.custom_scenery_path,
570+
args.disable_overlays,
571+
ctx,
572+
);
547573
}
548574

549575
Ok(())
@@ -628,7 +654,12 @@ impl CommandHandler for RemoveHandler {
628654

629655
// Rebuild consolidated overlay after removing an overlay package
630656
if args.package_type == PackageType::Overlay {
631-
rebuild_consolidated_overlay(&args.install_dir, &args.custom_scenery_path, ctx);
657+
rebuild_consolidated_overlay(
658+
&args.install_dir,
659+
&args.custom_scenery_path,
660+
args.disable_overlays,
661+
ctx,
662+
);
632663
}
633664

634665
Ok(())

‎xearthlayer-cli/src/commands/packages/mod.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ pub fn run(command: PackagesCommands) -> Result<(), CliError> {
154154
temp_dir: temp_dir.unwrap_or_else(|| default_temp_dir(&config)),
155155
custom_scenery_path: default_custom_scenery_dir(&config),
156156
auto_install_overlays: config.packages.auto_install_overlays,
157+
disable_overlays: config.packages.disable_overlays,
157158
concurrent_downloads: config.packages.concurrent_downloads,
158159
},
159160
&ctx,
@@ -175,6 +176,7 @@ pub fn run(command: PackagesCommands) -> Result<(), CliError> {
175176
temp_dir: temp_dir.unwrap_or_else(|| default_temp_dir(&config)),
176177
all,
177178
custom_scenery_path: default_custom_scenery_dir(&config),
179+
disable_overlays: config.packages.disable_overlays,
178180
concurrent_downloads: config.packages.concurrent_downloads,
179181
},
180182
&ctx,
@@ -192,6 +194,7 @@ pub fn run(command: PackagesCommands) -> Result<(), CliError> {
192194
install_dir: install_dir.unwrap_or_else(|| default_install_dir(&config)),
193195
force,
194196
custom_scenery_path: default_custom_scenery_dir(&config),
197+
disable_overlays: config.packages.disable_overlays,
195198
},
196199
&ctx,
197200
),

‎xearthlayer/src/config/defaults.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ impl Default for ConfigFile {
376376
install_location: None,
377377
custom_scenery_path: None,
378378
auto_install_overlays: false,
379+
disable_overlays: false,
379380
temp_dir: None,
380381
concurrent_downloads: 5,
381382
},

‎xearthlayer/src/config/keys.rs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub enum ConfigKey {
6969
PackagesInstallLocation,
7070
PackagesCustomSceneryPath,
7171
PackagesAutoInstallOverlays,
72+
PackagesDisableOverlays,
7273
PackagesTempDir,
7374
PackagesConcurrentDownloads,
7475

@@ -151,6 +152,7 @@ impl FromStr for ConfigKey {
151152
"packages.install_location" => Ok(ConfigKey::PackagesInstallLocation),
152153
"packages.custom_scenery_path" => Ok(ConfigKey::PackagesCustomSceneryPath),
153154
"packages.auto_install_overlays" => Ok(ConfigKey::PackagesAutoInstallOverlays),
155+
"packages.disable_overlays" => Ok(ConfigKey::PackagesDisableOverlays),
154156
"packages.temp_dir" => Ok(ConfigKey::PackagesTempDir),
155157
"packages.concurrent_downloads" => Ok(ConfigKey::PackagesConcurrentDownloads),
156158

@@ -228,6 +230,7 @@ impl ConfigKey {
228230
ConfigKey::PackagesInstallLocation => "packages.install_location",
229231
ConfigKey::PackagesCustomSceneryPath => "packages.custom_scenery_path",
230232
ConfigKey::PackagesAutoInstallOverlays => "packages.auto_install_overlays",
233+
ConfigKey::PackagesDisableOverlays => "packages.disable_overlays",
231234
ConfigKey::PackagesTempDir => "packages.temp_dir",
232235
ConfigKey::PackagesConcurrentDownloads => "packages.concurrent_downloads",
233236
ConfigKey::LoggingFile => "logging.file",
@@ -347,6 +350,7 @@ impl ConfigKey {
347350
ConfigKey::PackagesAutoInstallOverlays => {
348351
config.packages.auto_install_overlays.to_string()
349352
}
353+
ConfigKey::PackagesDisableOverlays => config.packages.disable_overlays.to_string(),
350354
ConfigKey::PackagesTempDir => config
351355
.packages
352356
.temp_dir
@@ -495,6 +499,11 @@ impl ConfigKey {
495499
config.packages.auto_install_overlays =
496500
v == "true" || v == "1" || v == "yes" || v == "on";
497501
}
502+
ConfigKey::PackagesDisableOverlays => {
503+
let v = value.to_lowercase();
504+
config.packages.disable_overlays =
505+
v == "true" || v == "1" || v == "yes" || v == "on";
506+
}
498507
ConfigKey::PackagesTempDir => {
499508
config.packages.temp_dir = optional_path(value);
500509
}
@@ -639,6 +648,7 @@ impl ConfigKey {
639648
ConfigKey::PackagesInstallLocation => Box::new(OptionalPathSpec),
640649
ConfigKey::PackagesCustomSceneryPath => Box::new(OptionalPathSpec),
641650
ConfigKey::PackagesAutoInstallOverlays => Box::new(BooleanSpec),
651+
ConfigKey::PackagesDisableOverlays => Box::new(BooleanSpec),
642652
ConfigKey::PackagesTempDir => Box::new(OptionalPathSpec),
643653
ConfigKey::PackagesConcurrentDownloads => Box::new(IntegerRangeSpec::new(1, 10)),
644654
ConfigKey::LoggingFile => Box::new(PathSpec),
@@ -712,6 +722,7 @@ impl ConfigKey {
712722
ConfigKey::PackagesInstallLocation,
713723
ConfigKey::PackagesCustomSceneryPath,
714724
ConfigKey::PackagesAutoInstallOverlays,
725+
ConfigKey::PackagesDisableOverlays,
715726
ConfigKey::PackagesTempDir,
716727
ConfigKey::PackagesConcurrentDownloads,
717728
ConfigKey::LoggingFile,
@@ -1250,6 +1261,38 @@ mod tests {
12501261
assert!(key.validate("601").is_err());
12511262
}
12521263

1264+
#[test]
1265+
fn test_packages_disable_overlays_round_trip() {
1266+
let key = ConfigKey::from_str("packages.disable_overlays").unwrap();
1267+
assert_eq!(key.name(), "packages.disable_overlays");
1268+
assert_eq!(key.section(), "packages");
1269+
assert!(!key.is_sensitive());
1270+
1271+
let mut config = ConfigFile::default();
1272+
// Default
1273+
assert_eq!(key.get(&config), "false");
1274+
assert!(!config.packages.disable_overlays);
1275+
1276+
// Accepts every truthy form the BooleanSpec allows
1277+
for truthy in &["true", "1", "yes", "on", "TRUE", "YES"] {
1278+
key.set(&mut config, truthy).unwrap();
1279+
assert!(
1280+
config.packages.disable_overlays,
1281+
"Expected '{}' to set true",
1282+
truthy
1283+
);
1284+
}
1285+
// And falsy
1286+
key.set(&mut config, "false").unwrap();
1287+
assert!(!config.packages.disable_overlays);
1288+
1289+
// Validation rejects garbage
1290+
assert!(key.validate("maybe").is_err());
1291+
1292+
// Listed in all()
1293+
assert!(ConfigKey::all().contains(&ConfigKey::PackagesDisableOverlays));
1294+
}
1295+
12531296
#[test]
12541297
fn test_new_prefetch_keys_in_all() {
12551298
let all = ConfigKey::all();

‎xearthlayer/src/config/parser.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,9 @@ pub(super) fn parse_ini(ini: &Ini) -> Result<ConfigFile, ConfigFileError> {
272272
if let Some(v) = section.get("auto_install_overlays") {
273273
config.packages.auto_install_overlays = parse_bool(v);
274274
}
275+
if let Some(v) = section.get("disable_overlays") {
276+
config.packages.disable_overlays = parse_bool(v);
277+
}
275278
if let Some(v) = section.get("temp_dir") {
276279
let v = v.trim();
277280
if !v.is_empty() {

‎xearthlayer/src/config/settings.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ pub struct PackagesSettings {
160160
pub custom_scenery_path: Option<PathBuf>,
161161
/// Automatically install overlay packages when installing ortho for same region.
162162
pub auto_install_overlays: bool,
163+
/// Suppress the consolidated `yzXEL_overlay/` symlink folder in Custom Scenery
164+
/// at runtime. Overlay packages are still downloaded and stored locally; this
165+
/// only controls whether X-Plane sees the consolidated overlay folder when
166+
/// `xearthlayer run` starts. Useful when running third-party overlay scenery
167+
/// (e.g., SimHeaven) that would otherwise conflict with XEL overlays.
168+
pub disable_overlays: bool,
163169
/// Temporary directory for downloads (default: ~/.xearthlayer/tmp).
164170
pub temp_dir: Option<PathBuf>,
165171
/// Number of concurrent part downloads (1-10, default: 5).

‎xearthlayer/src/config/writer.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ pub(super) fn to_config_string(config: &ConfigFile) -> String {
3636
} else {
3737
"false"
3838
};
39+
let disable_overlays = if config.packages.disable_overlays {
40+
"true"
41+
} else {
42+
"false"
43+
};
3944
let temp_dir = config
4045
.packages
4146
.temp_dir
@@ -151,6 +156,12 @@ install_location = {}
151156
custom_scenery_path = {}
152157
; Automatically install overlay packages when installing ortho for same region
153158
auto_install_overlays = {}
159+
; Suppress XEL overlays at runtime by removing the consolidated yzXEL_overlay/
160+
; symlink folder from Custom Scenery on `xearthlayer run` startup. Overlay
161+
; packages are still downloaded and stored locally — only the symlink that
162+
; X-Plane reads is suppressed. Use this when running third-party overlay
163+
; scenery (e.g., SimHeaven) that conflicts with XEL overlays. (default: false)
164+
disable_overlays = {}
154165
; Temporary directory for package downloads (default: ~/.xearthlayer/tmp)
155166
; Large packages are downloaded here before extraction
156167
temp_dir = {}
@@ -280,6 +291,7 @@ congestion_threshold = {}
280291
install_location,
281292
custom_scenery_path,
282293
auto_install_overlays,
294+
disable_overlays,
283295
temp_dir,
284296
config.packages.concurrent_downloads,
285297
path_to_string(&config.logging.file),

‎xearthlayer/src/service/orchestrator/core.rs‎

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use crate::aircraft_position::{SharedAircraftPosition, StateAggregator};
1111
use crate::geo_index::GeoIndex;
1212
use crate::log::TracingLogger;
1313
use crate::manager::{
14-
create_consolidated_overlay, InstalledPackage, LocalPackageStore, MountManager, ServiceBuilder,
14+
create_consolidated_overlay, remove_consolidated_overlay, InstalledPackage, LocalPackageStore,
15+
MountManager, ServiceBuilder,
1516
};
1617
use crate::metrics::TelemetrySnapshot;
1718
use crate::ortho_union::OrthoUnionIndex;
@@ -232,16 +233,33 @@ impl ServiceOrchestrator {
232233
)));
233234
}
234235

235-
// Phase 2: Create overlay symlinks
236+
// Phase 2: Create overlay symlinks (or remove them if disabled)
236237
if let Some(ref cb) = callback {
237238
cb(StartupProgress::CreatingOverlay);
238239
}
239-
let overlay_result = create_consolidated_overlay(store, &self.config.custom_scenery_path);
240-
let overlay_success = overlay_result.success;
241-
let overlay_error = overlay_result.error.clone();
242-
if let Some(ref error) = overlay_result.error {
243-
tracing::warn!(error = %error, "Failed to create consolidated overlay");
244-
}
240+
let (overlay_success, overlay_error) = if self.config.disable_overlays {
241+
match remove_consolidated_overlay(&self.config.custom_scenery_path) {
242+
Ok(true) => {
243+
tracing::info!(
244+
"Removed consolidated overlay folder (packages.disable_overlays = true); \
245+
X-Plane will not load XEarthLayer overlays this session"
246+
);
247+
(true, None)
248+
}
249+
Ok(false) => (true, None),
250+
Err(e) => {
251+
tracing::warn!(error = %e, "Failed to remove consolidated overlay");
252+
(false, Some(e.to_string()))
253+
}
254+
}
255+
} else {
256+
let overlay_result =
257+
create_consolidated_overlay(store, &self.config.custom_scenery_path);
258+
if let Some(ref error) = overlay_result.error {
259+
tracing::warn!(error = %error, "Failed to create consolidated overlay");
260+
}
261+
(overlay_result.success, overlay_result.error.clone())
262+
};
245263

246264
// Phase 3: Start APT telemetry
247265
if let Some(ref cb) = callback {

0 commit comments

Comments
 (0)