feat(bootstrap): implement FastFlag manager, presets, and allowlist validation

This commit is contained in:
katR 2026-09-29 08:03:33 +05:30
parent 44a858ace2
commit 72dcee25a8
3 changed files with 185 additions and 0 deletions

View file

@ -0,0 +1,159 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use crate::error::MolotovError;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum FlagValue {
Boolean(bool),
Integer(i64),
Text(String),
}
impl From<bool> for FlagValue {
fn from(v: bool) -> Self {
FlagValue::Boolean(v)
}
}
impl From<i64> for FlagValue {
fn from(v: i64) -> Self {
FlagValue::Integer(v)
}
}
impl From<i32> for FlagValue {
fn from(v: i32) -> Self {
FlagValue::Integer(v as i64)
}
}
impl From<&str> for FlagValue {
fn from(v: &str) -> Self {
FlagValue::Text(v.to_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PresetProfile {
Default,
LowVram,
CompetitiveUncapped,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FastFlagProfile {
pub name: String,
pub flags: HashMap<String, FlagValue>,
}
impl FastFlagProfile {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
flags: HashMap::new(),
}
}
pub fn set(&mut self, key: &str, val: FlagValue) {
self.flags.insert(key.to_string(), val);
}
pub fn get_int(&self, key: &str) -> Option<i64> {
match self.flags.get(key) {
Some(FlagValue::Integer(v)) => Some(*v),
_ => None,
}
}
pub fn get_bool(&self, key: &str) -> Option<bool> {
match self.flags.get(key) {
Some(FlagValue::Boolean(v)) => Some(*v),
_ => None,
}
}
}
pub struct FastFlagManager {
active_profile: FastFlagProfile,
allowlist: HashSet<String>,
}
impl Default for FastFlagManager {
fn default() -> Self {
Self::new()
}
}
impl FastFlagManager {
pub fn new() -> Self {
let mut allowlist = HashSet::new();
// Seed standard safe FastFlags
allowlist.insert("FIntDebugTextureManagerSkipMips".to_string());
allowlist.insert("DFIntTextureCompositorActiveJobs".to_string());
allowlist.insert("DFIntTaskSchedulerTargetFps".to_string());
allowlist.insert("FFlagDebugGraphicsDisableDirect3D11".to_string());
allowlist.insert("DFFlagDebugPauseVoxelizer".to_string());
Self {
active_profile: FastFlagProfile::new("Default"),
allowlist,
}
}
pub fn active_profile(&self) -> &FastFlagProfile {
&self.active_profile
}
pub fn active_profile_mut(&mut self) -> &mut FastFlagProfile {
&mut self.active_profile
}
pub fn set_flag(&mut self, key: &str, val: FlagValue) {
self.active_profile.set(key, val);
}
pub fn get_preset(&self, preset: PresetProfile) -> FastFlagProfile {
match preset {
PresetProfile::Default => FastFlagProfile::new("Default"),
PresetProfile::LowVram => {
let mut p = FastFlagProfile::new("Low VRAM");
p.set("FIntDebugTextureManagerSkipMips", 1.into());
p.set("DFIntTextureCompositorActiveJobs", 2.into());
p
}
PresetProfile::CompetitiveUncapped => {
let mut p = FastFlagProfile::new("Competitive Uncapped");
p.set("DFIntTaskSchedulerTargetFps", 240.into());
p
}
}
}
pub fn apply_preset(&mut self, preset: PresetProfile) {
self.active_profile = self.get_preset(preset);
}
pub fn validate_against_allowlist(&self) -> Vec<String> {
let mut unverified = Vec::new();
for key in self.active_profile.flags.keys() {
if !self.allowlist.contains(key) {
unverified.push(key.clone());
}
}
unverified.sort();
unverified
}
pub fn export_client_app_settings(&self, target_path: &Path) -> Result<(), MolotovError> {
if let Some(parent) = target_path.parent() {
std::fs::create_dir_all(parent)?;
}
let json = serde_json::to_string_pretty(&self.active_profile.flags)
.map_err(|e| MolotovError::FastFlag(e.to_string()))?;
std::fs::write(target_path, json)?;
Ok(())
}
}

View file

@ -2,3 +2,4 @@
pub mod apk;
pub mod cdn;
pub mod error;
pub mod fflags;

View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use molotov_bootstrap::fflags::{FastFlagManager, PresetProfile};
#[test]
fn test_low_vram_preset_contains_mip_skip() {
let manager = FastFlagManager::new();
let profile = manager.get_preset(PresetProfile::LowVram);
assert_eq!(
profile.get_int("FIntDebugTextureManagerSkipMips"),
Some(1),
"Low VRAM preset must set FIntDebugTextureManagerSkipMips to 1"
);
}
#[test]
fn test_allowlist_validation_warning() {
let mut manager = FastFlagManager::new();
manager.set_flag("FIntDebugTextureManagerSkipMips", 1.into());
manager.set_flag("FUnknownArbitraryFlagExploit", true.into());
let warnings = manager.validate_against_allowlist();
assert_eq!(warnings.len(), 1);
assert_eq!(warnings[0], "FUnknownArbitraryFlagExploit");
}