Skip to content

Commit

Permalink
Implement auto-verification of non-gameplay-affecting mods
Browse files Browse the repository at this point in the history
Also includes an auto-verification mod lint to check if mods
are elligible for auto-verification.
  • Loading branch information
jieyouxu committed Mar 2, 2024
1 parent 4efb24d commit 0d85857
Show file tree
Hide file tree
Showing 14 changed files with 557 additions and 93 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ repak.workspace = true
include_dir = "0.7.3"
postcard.workspace = true
fs-err.workspace = true
snafu = "0.8.0"
snafu = "0.8.1"

[target.'cfg(target_env = "msvc")'.dependencies]
hook = { path = "hook", artifact = "cdylib", optional = true, target = "x86_64-pc-windows-msvc"}
Expand Down
4 changes: 3 additions & 1 deletion hook/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ pub unsafe fn initialize() -> Result<()> {
)?;
HookUFunctionBind.enable()?;

if let Ok(server_name) = &globals().resolution.server_name {
if let Ok(server_name) = &globals().resolution.server_name
&& globals().meta.mods.iter().any(|m| m.gameplay_affecting)
{
GetServerName
.initialize(
std::mem::transmute(server_name.get_server_name.0),
Expand Down
2 changes: 2 additions & 0 deletions hook/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(let_chains)]

mod hooks;
mod ue;

Expand Down
10 changes: 9 additions & 1 deletion mint_lib/src/mod_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub enum ApprovalStatus {
}

/// Whether a mod can be resolved by clients or not
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)]
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Hash, Serialize, Deserialize)]
pub enum ResolvableStatus {
Unresolvable(String),
Resolvable,
Expand Down Expand Up @@ -112,11 +112,13 @@ impl ModIdentifier {
Self(s)
}
}

impl From<String> for ModIdentifier {
fn from(value: String) -> Self {
Self::new(value)
}
}

impl From<&str> for ModIdentifier {
fn from(value: &str) -> Self {
Self::new(value.to_owned())
Expand All @@ -130,21 +132,25 @@ pub struct Meta {
pub mods: Vec<MetaMod>,
pub config: MetaConfig,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MetaConfig {
pub disable_fix_exploding_gas: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SemverVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
}

impl Display for SemverVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MetaMod {
pub name: String,
Expand All @@ -153,7 +159,9 @@ pub struct MetaMod {
pub author: String,
pub approval: ApprovalStatus,
pub required: bool,
pub gameplay_affecting: bool,
}

impl Meta {
pub fn to_server_list_string(&self) -> String {
use itertools::Itertools;
Expand Down
1 change: 1 addition & 0 deletions src/gui/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ async fn integrate_async(
crate::integrate::integrate(
fsd_pak,
config,
store,
to_integrate.into_iter().zip(paths).collect(),
)
})
Expand Down
196 changes: 122 additions & 74 deletions src/gui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ struct LintOptions {
non_asset_files: bool,
split_asset_pairs: bool,
unmodified_game_assets: bool,
auto_verification: bool,
}

enum LastActionStatus {
Expand Down Expand Up @@ -241,6 +242,68 @@ impl App {
})
.collect::<Vec<_>>();

let mk_searchable_tag = |ctx: &mut Ctx,
tag_str: &str,
ui: &mut Ui,
color: Option<egui::Color32>,
hover_str: Option<&str>| {
let text_color = if color.is_some() {
Color32::BLACK
} else {
Color32::GRAY
};
let mut job = LayoutJob::default();
let mut is_match = false;
if let Some(search_string) = &self.search_string {
for (m, chunk) in find_string::FindString::new(tag_str, search_string) {
let background = if m {
is_match = true;
TextFormat {
background: Color32::YELLOW,
color: text_color,
..Default::default()
}
} else {
TextFormat {
color: text_color,
..Default::default()
}
};
job.append(chunk, 0.0, background);
}
} else {
job.append(
tag_str,
0.0,
TextFormat {
color: text_color,
..Default::default()
},
);
}

let button = if let Some(color) = color {
egui::Button::new(job)
.small()
.fill(color)
.stroke(egui::Stroke::NONE)
} else {
egui::Button::new(job).small().stroke(egui::Stroke::NONE)
};

let res = if let Some(hover_str) = hover_str {
ui.add_enabled(false, button)
.on_disabled_hover_text(hover_str)
} else {
ui.add_enabled(false, button)
};

if is_match && self.scroll_to_match {
res.scroll_to_me(None);
ctx.scroll_to_match = false;
}
};

let ui_mod_tags = |ctx: &mut Ctx, ui: &mut Ui, info: &ModInfo| {
if let Some(ModioTags {
qol,
Expand All @@ -253,95 +316,40 @@ impl App {
versions: _,
}) = info.modio_tags.as_ref()
{
let mut mk_searchable_modio_tag =
|tag_str: &str,
ui: &mut Ui,
color: Option<egui::Color32>,
hover_str: Option<&str>| {
let text_color = if color.is_some() {
Color32::BLACK
} else {
Color32::GRAY
};
let mut job = LayoutJob::default();
let mut is_match = false;
if let Some(search_string) = &self.search_string {
for (m, chunk) in
find_string::FindString::new(tag_str, search_string)
{
let background = if m {
is_match = true;
TextFormat {
background: Color32::YELLOW,
color: text_color,
..Default::default()
}
} else {
TextFormat {
color: text_color,
..Default::default()
}
};
job.append(chunk, 0.0, background);
}
} else {
job.append(
tag_str,
0.0,
TextFormat {
color: text_color,
..Default::default()
},
);
}

let button = if let Some(color) = color {
egui::Button::new(job)
.small()
.fill(color)
.stroke(egui::Stroke::NONE)
} else {
egui::Button::new(job).small().stroke(egui::Stroke::NONE)
};

let res = if let Some(hover_str) = hover_str {
ui.add_enabled(false, button)
.on_disabled_hover_text(hover_str)
} else {
ui.add_enabled(false, button)
};

if is_match && self.scroll_to_match {
res.scroll_to_me(None);
ctx.scroll_to_match = false;
}
};

match approval_status {
ApprovalStatus::Verified => {
mk_searchable_modio_tag(
mk_searchable_tag(
ctx,
"Verified",
ui,
Some(egui::Color32::LIGHT_GREEN),
Some("Does not contain any gameplay affecting features or changes"),
);
}
ApprovalStatus::Approved => {
mk_searchable_modio_tag(
mk_searchable_tag(
ctx,
"Approved",
ui,
Some(egui::Color32::LIGHT_BLUE),
Some("Contains gameplay affecting features or changes"),
);
}
ApprovalStatus::Sandbox => {
mk_searchable_modio_tag("Sandbox", ui, Some(egui::Color32::LIGHT_YELLOW), Some("Contains significant, possibly progression breaking, changes to gameplay"));
mk_searchable_tag(
ctx,
"Sandbox",
ui,
Some(egui::Color32::LIGHT_YELLOW),
Some("Contains significant, possibly progression breaking, changes to gameplay")
);
}
}

match required_status {
RequiredStatus::RequiredByAll => {
mk_searchable_modio_tag(
mk_searchable_tag(
ctx,
"RequiredByAll",
ui,
Some(egui::Color32::LIGHT_RED),
Expand All @@ -351,7 +359,8 @@ impl App {
);
}
RequiredStatus::Optional => {
mk_searchable_modio_tag(
mk_searchable_tag(
ctx,
"Optional",
ui,
None,
Expand All @@ -361,20 +370,49 @@ impl App {
}

if *qol {
mk_searchable_modio_tag("QoL", ui, None, None);
mk_searchable_tag(ctx, "QoL", ui, None, None);
}
if *gameplay {
mk_searchable_modio_tag("Gameplay", ui, None, None);
mk_searchable_tag(ctx, "Gameplay", ui, None, None);
}
if *audio {
mk_searchable_modio_tag("Audio", ui, None, None);
mk_searchable_tag(ctx, "Audio", ui, None, None);
}
if *visual {
mk_searchable_modio_tag("Visual", ui, None, None);
mk_searchable_tag(ctx, "Visual", ui, None, None);
}
if *framework {
mk_searchable_modio_tag("Framework", ui, None, None);
mk_searchable_tag(ctx, "Framework", ui, None, None);
}
} else if let Some(status) = self
.state
.store
.get_gameplay_affecting_status(&info.resolution.url)
{
match status {
false => mk_searchable_tag(
ctx,
"Verified",
ui,
Some(egui::Color32::LIGHT_GREEN),
Some("Does not contain any gameplay affecting features or changes"),
),
true => mk_searchable_tag(
ctx,
"Not Verified",
ui,
Some(egui::Color32::LIGHT_RED),
Some("Contains gameplay affecting features or changes, or cannot be auto-verified"),
),
}
} else {
mk_searchable_tag(
ctx,
"Not Verified",
ui,
Some(egui::Color32::LIGHT_RED),
Some("Contains gameplay affecting features or changes, or cannot be auto-verified"),
);
}
};

Expand Down Expand Up @@ -1093,6 +1131,16 @@ impl App {
"This lint requires DRG pak path to be specified",
);
ui.end_row();

ui.label("Check if mods can be auto-verified");
ui.add_enabled(
self.state.config.drg_pak_path.is_some(),
toggle_switch(&mut self.lint_options.auto_verification),
)
.on_disabled_hover_text(
"This lint requires DRG pak path to be specified",
);
ui.end_row();
});
});

Expand Down
Loading

0 comments on commit 0d85857

Please sign in to comment.