mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 07:28:22 -10:00
* Add command-line option overrides to options system - Extend `OptionsManager` to support storing and displaying reasons for overrides. - Update related code paths to prioritize command-line overrides, ensure correct option resolution, and display override details in the UI. - Add unit tests for override behavior and round-trip validation. - Remove unused legacy functions (`removeVSyncOption`, `removeWindowModeOption`) in favor of the new system. * Add unit tests for `Graphics.Anisotropy` option validation with and without OpenGL * Add support for associating command-line option names to `OptionsManager` overrides - Update HDR, shadow rendering, and graphics API option overrides to include their respective command-line flag names. - Refactor `Graphics.RenderAPI` to use the `GraphicsAPI` enum instead of raw integers for better type safety and clarity. - Ensure Vulkan support logic and fallback behavior function correctly with the updated options system. - Update unit tests to validate override functionality and enum-based API configuration. * Remove unused `removeVSyncOption` function * Apply the PR review feedback for the command-line option overrides Delete the unit-test run artifacts that this branch committed by mistake, and add .gitignore rules for the files that the unittests binary writes. Give an override priority over an in-session edit again. The options UI now shows a disabled control for an overridden option, and setConfigValue() refuses an edit for an overridden key. Before this change, a save wrote the edit to the config file and applied it to the bound global, but getValue() kept returning the override. The game and the options menu then showed different values for the rest of the session. Add a setOverride() overload that takes a JSON value. Every command-line flag now builds its override with json_integer, json_real, json_true, json_false or json_pack, in place of a hand-formatted string. Make Graphics.CockpitFOV and Graphics.CockpitFOVToggle independent of the order in which the options manager loads them, and remove the forced override on the player's toggle preference. The toggle listener also replaces an identical bind_to() call that made the listener dead code. Add 16 to the values of Graphics.MSAASamples. The -msaa flag accepts 16, so an override with that value made printValues() report an invalid value. Keep the render API that the caller gives to gr_init(). Only a Default value now comes from the Graphics.RenderAPI option, so FRED and the unit tests keep the backend that they ask for. Make the default_func of Graphics.Shadows and Graphics.ShadowRenderMethod return their global. A constant discarded the value that -enable_shadows and the mod table set. Set Max_rt_shadow_local_lights to 4, because 3 is not one of the values that the option offers. Also: resolve -lang one time, share the render API name with version.cpp, correct the stale comments, and add unit tests with a fixture that clears the overrides after each test. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UGh4ZM2u4oUGSbrP122Uyr * Fix the order-dependent crash in the MSAA option test The test `MsaaOverrideAcceptsEverySampleCountTheFlagAllows` calls `getValidValues()`. `Option<T>` makes a display string for each value, and the display function of `Graphics.MSAASamples` calls `XSTR()`. `XSTR()` runs `Int3()` if `lcl_xstr_init()` did not run before. Only `FSTestFixture` runs it, and `Xstr_inited` stays set for the remainder of the process. Thus the shuffled test order decided if the test passed or if it stopped the test binary with SIGILL. Move that one test to a new fixture that derives from `FSTestFixture`. The fixture initializes localization, and it also clears the overrides after each test. Refresh the comment that told that the test binary never runs `lcl_init()`. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UGh4ZM2u4oUGSbrP122Uyr --------- Co-authored-by: Claude Opus 5 <[email protected]>
100 lines
3.4 KiB
C++
100 lines
3.4 KiB
C++
#pragma once
|
|
|
|
#include "globalincs/pstypes.h"
|
|
#include "libs/jansson.h"
|
|
#include <memory>
|
|
#include <optional>
|
|
|
|
namespace options {
|
|
|
|
// Forward declaration
|
|
class OptionBase;
|
|
|
|
class OptionsManager {
|
|
OptionsManager();
|
|
|
|
// A value forced by something outside of the options system itself (currently only the command line).
|
|
// This takes priority over the persisted config value and over any in-session edit. The options UI shows
|
|
// an overridden option as a disabled control with the reason in its tooltip.
|
|
struct OverrideEntry {
|
|
std::unique_ptr<json_t> value;
|
|
SCP_string reason; // human-readable source, e.g. "-no_vsync", shown in the options UI
|
|
};
|
|
SCP_unordered_map<SCP_string, OverrideEntry> _config_overrides;
|
|
|
|
SCP_unordered_map<SCP_string, std::unique_ptr<json_t>> _changed_values;
|
|
|
|
SCP_unordered_map<SCP_string, const OptionBase*> _optionsMapping;
|
|
|
|
// Enforced options are hidden from the player and do not load values from user settings
|
|
SCP_unordered_set<SCP_string> _enforcedOptions;
|
|
|
|
SCP_vector<std::shared_ptr<const OptionBase>> _options;
|
|
bool _optionsSorted = false;
|
|
|
|
public:
|
|
static OptionsManager* instance();
|
|
|
|
~OptionsManager();
|
|
|
|
std::optional<std::unique_ptr<json_t>> getValueFromConfig(const SCP_string& key) const;
|
|
|
|
void setConfigValue(const SCP_string& key, std::unique_ptr<json_t>&& value);
|
|
|
|
void setOverride(const SCP_string& key, const SCP_string& json, const SCP_string& reason);
|
|
|
|
// Overload that takes an already-built JSON value. Prefer this one: it lets a caller reuse the option's
|
|
// own serializer instead of formatting the JSON by hand, which can produce a value the option's
|
|
// deserializer then rejects.
|
|
void setOverride(const SCP_string& key, std::unique_ptr<json_t>&& value, const SCP_string& reason);
|
|
|
|
// Returns true if this key currently has an override.
|
|
bool isOverridden(const SCP_string& key) const;
|
|
|
|
// Returns the reason string passed to setOverride() for this key, if it is currently overridden.
|
|
std::optional<SCP_string> getOverrideReason(const SCP_string& key) const;
|
|
|
|
// Removes all overrides. For tests, so that one test's overrides do not leak into the next.
|
|
void clearOverrides();
|
|
|
|
const OptionBase* addOption(std::shared_ptr<const OptionBase>&& option);
|
|
|
|
void removeOption(const std::shared_ptr<const OptionBase>& option);
|
|
|
|
const OptionBase* getOptionByKey(SCP_string name);
|
|
|
|
void enforceOption(const SCP_string& key);
|
|
|
|
void unenforceOption(const SCP_string& key);
|
|
|
|
bool isOptionEnforced(const SCP_string& key) const;
|
|
|
|
const SCP_vector<std::shared_ptr<const options::OptionBase>>& getOptions();
|
|
|
|
bool persistOptionChanges(const options::OptionBase* option);
|
|
|
|
/**
|
|
* @brief Persists changes to the disk
|
|
*
|
|
* This writes all the outstanding changes to the config file and returns a vector of all the options that indicated
|
|
* that they could not change the value. If the return value is empty then all changes were applied but if it is not
|
|
* empty then a restart is required for all the options to take effect.
|
|
*
|
|
* @return A vector of options that did not support changing the value
|
|
*/
|
|
SCP_vector<const options::OptionBase*> persistChanges();
|
|
|
|
void discardChanges();
|
|
|
|
void loadInitialValues();
|
|
|
|
void printValues();
|
|
|
|
void set_ingame_binary_option(SCP_string key, bool value);
|
|
void set_ingame_multi_option(SCP_string key, int value);
|
|
void set_ingame_range_option(SCP_string key, int value);
|
|
void set_ingame_range_option(SCP_string key, float value);
|
|
};
|
|
|
|
}
|