Skip to content

Commit

Permalink
Don't deactivate excess active plugins on load
Browse files Browse the repository at this point in the history
Otherwise because only the first 255 normal plugins are loaded, if you
move plugins around in the load order, you can change which active
plugins are within the first 255, and so end up deactivating plugins
the game would have loaded.
  • Loading branch information
Ortham committed Feb 17, 2023
1 parent cfab6df commit d42d36c
Show file tree
Hide file tree
Showing 6 changed files with 5 additions and 140 deletions.
4 changes: 0 additions & 4 deletions ffi/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,6 @@ pub unsafe extern "C" fn lo_is_ambiguous(handle: lo_game_handle, result: *mut bo
/// For the case of a plugin appearing multiple times in a load order / active plugin list,
/// libloadorder discards all but the last instance of that plugin.
///
/// For the case of more than 255 plugins being active, libloadorder deactivates as many plugins as
/// required to bring the number of plugins active below 256, starting from the end of the load
/// order and working towards the beginning.
///
/// This can be useful for when plugins are uninstalled manually or by a utility that does not also
/// update the load order / active plugins systems correctly.
///
Expand Down
6 changes: 5 additions & 1 deletion ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@
//! there may be additional conditions that may be enforced by the game.
//!
//! Libloadorder is less strict when loading load orders and will adjust them at load time to be
//! valid, similar to game behaviour.
//! valid, similar to game behaviour. The exception to this adjustment is that the set of active
//! plugins is not reduced in size if too many plugins are active, so that it is preserved when
//! changing the order in which plugins are loaded. As such, while libloadorder will refuse to
//! activate too many plugins, it will preserve a load order that already has too many plugins
//! activated.

use std::cell::RefCell;
use std::ffi::CString;
Expand Down
17 changes: 0 additions & 17 deletions src/load_order/asterisk_based.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ impl WritableLoadOrder for AsteriskBasedLoadOrder {

self.add_implicitly_active_plugins()?;

self.deactivate_excess_plugins();

Ok(())
}

Expand Down Expand Up @@ -653,21 +651,6 @@ mod tests {
assert_eq!(1, load_order.active_plugin_names().len());
}

#[test]
fn load_should_deactivate_excess_normal_plugins_and_light_masters_using_separate_limits() {
let tmp_dir = tempdir().unwrap();
let mut load_order = prepare(GameId::SkyrimSE, &tmp_dir.path());

let plugins = prepare_bulk_plugins(load_order.game_settings());

load_order.load().unwrap();
let active_plugin_names = load_order.active_plugin_names();

assert_eq!(4351, active_plugin_names.len());
assert_eq!(plugins[..255], active_plugin_names[..255]);
assert_eq!(plugins[261..4357], active_plugin_names[255..]);
}

#[test]
fn load_should_not_duplicate_a_plugin_that_has_a_ghosted_duplicate() {
let tmp_dir = tempdir().unwrap();
Expand Down
38 changes: 0 additions & 38 deletions src/load_order/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,6 @@ pub trait MutableLoadOrder: ReadableLoadOrder + ReadableLoadOrderBase + Sync {
}
}

fn deactivate_excess_plugins(&mut self) {
for index in get_excess_active_plugin_indices(self) {
self.plugins_mut()[index].deactivate();
}
}

fn move_or_insert_plugin_with_index(
&mut self,
plugin_name: &str,
Expand Down Expand Up @@ -310,38 +304,6 @@ fn count_plugins(
.count()
}

fn get_excess_active_plugin_indices<T: MutableLoadOrder + ?Sized>(load_order: &T) -> Vec<usize> {
let implicitly_active_plugins = load_order.game_settings().implicitly_active_plugins();
let mut normal_active_count = load_order.count_active_normal_plugins();
let mut light_plugin_active_count = load_order.count_active_light_plugins();

let mut plugin_indices: Vec<usize> = Vec::new();
for (index, plugin) in load_order.plugins().iter().enumerate().rev() {
if normal_active_count <= MAX_ACTIVE_NORMAL_PLUGINS
&& light_plugin_active_count <= MAX_ACTIVE_LIGHT_PLUGINS
{
break;
}

let can_deactivate = plugin.is_active()
&& !implicitly_active_plugins
.iter()
.any(|i| plugin.name_matches(i));

if can_deactivate {
if plugin.is_light_plugin() && light_plugin_active_count > MAX_ACTIVE_LIGHT_PLUGINS {
plugin_indices.push(index);
light_plugin_active_count -= 1;
} else if !plugin.is_light_plugin() && normal_active_count > MAX_ACTIVE_NORMAL_PLUGINS {
plugin_indices.push(index);
normal_active_count -= 1;
}
}
}

plugin_indices
}

fn validate_master_file_index(
plugins: &[Plugin],
plugin: &Plugin,
Expand Down
41 changes: 0 additions & 41 deletions src/load_order/textfile_based.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ impl WritableLoadOrder for TextfileBasedLoadOrder {

self.add_implicitly_active_plugins()?;

self.deactivate_excess_plugins();

Ok(())
}

Expand Down Expand Up @@ -696,45 +694,6 @@ mod tests {
assert_eq!(1, load_order.active_plugin_names().len());
}

#[test]
fn load_should_deactivate_excess_plugins_not_including_implicitly_active_plugins() {
let tmp_dir = tempdir().unwrap();
let mut load_order = prepare(GameId::Skyrim, &tmp_dir.path());

let mut plugins: Vec<String> = Vec::new();
plugins.push(load_order.game_settings().master_file().to_string());
for i in 0..260 {
plugins.push(format!("Blank{}.esm", i));
copy_to_test_dir(
"Blank - Different.esm",
&plugins.last().unwrap(),
load_order.game_settings(),
);
}
copy_to_test_dir("Blank.esm", "Update.esm", &load_order.game_settings());

{
let plugins_as_ref: Vec<&str> = plugins.iter().map(AsRef::as_ref).collect();
write_active_plugins_file(load_order.game_settings(), &plugins_as_ref);
set_timestamps(
&load_order.game_settings().plugins_directory(),
&plugins_as_ref,
);
}

plugins = plugins[0..254].to_vec();
plugins.push("Update.esm".to_string());

load_order.load().unwrap();
let active_plugin_names = load_order.active_plugin_names();

assert_eq!(255, active_plugin_names.len());
for i in 0..255 {
assert_eq!(plugins[i], active_plugin_names[i]);
}
assert_eq!(plugins, active_plugin_names);
}

#[test]
fn load_should_not_duplicate_a_plugin_that_is_ghosted_and_in_load_order_file() {
let tmp_dir = tempdir().unwrap();
Expand Down
39 changes: 0 additions & 39 deletions src/load_order/timestamp_based.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ impl WritableLoadOrder for TimestampBasedLoadOrder {

self.add_implicitly_active_plugins()?;

self.deactivate_excess_plugins();

Ok(())
}

Expand Down Expand Up @@ -547,43 +545,6 @@ mod tests {
assert_eq!(expected_filenames, load_order.active_plugin_names());
}

#[test]
fn load_should_deactivate_excess_plugins() {
let tmp_dir = tempdir().unwrap();
let mut load_order = prepare(GameId::Oblivion, &tmp_dir.path());

let mut plugins: Vec<String> = Vec::new();
plugins.push(load_order.game_settings().master_file().to_string());
for i in 0..260 {
plugins.push(format!("Blank{}.esm", i));
copy_to_test_dir(
"Blank.esm",
&plugins.last().unwrap(),
load_order.game_settings(),
);
}

{
let plugins_as_ref: Vec<&str> = plugins.iter().map(AsRef::as_ref).collect();
write_active_plugins_file(load_order.game_settings(), &plugins_as_ref);
set_timestamps(
&load_order.game_settings().plugins_directory(),
&plugins_as_ref,
);
}

plugins = plugins[0..255].to_vec();

load_order.load().unwrap();
let active_plugin_names = load_order.active_plugin_names();

assert_eq!(255, active_plugin_names.len());
for i in 0..255 {
assert_eq!(plugins[i], active_plugin_names[i]);
}
assert_eq!(plugins, active_plugin_names);
}

#[test]
fn save_should_preserve_the_existing_set_of_timestamps() {
let tmp_dir = tempdir().unwrap();
Expand Down

0 comments on commit d42d36c

Please sign in to comment.