refraction/src/handlers/event/analyze_logs/issues.rs
2024-08-05 14:14:22 +03:00

401 lines
13 KiB
Rust

use crate::{api, utils::semver_split, Data};
use std::sync::OnceLock;
use eyre::Result;
use log::trace;
use regex::Regex;
pub type Issue = Option<(String, String)>;
pub async fn find(log: &str, data: &Data) -> Result<Vec<(String, String)>> {
trace!("Checking log for issues");
let issues = [
fabric_internal,
flatpak_nvidia,
forge_java,
intel_hd,
java_option,
lwjgl_2_java_9,
macos_ns,
oom,
optinotfine,
pre_1_12_native_transport_java_9,
wrong_java,
forge_missing_dependencies,
legacyjavafixer,
locked_jar,
offline_launch,
frapi,
no_disk_space,
java_32_bit,
intermediary_mappings,
old_forge_new_java,
];
let mut res: Vec<(String, String)> = issues.iter().filter_map(|issue| issue(log)).collect();
if let Some(issues) = outdated_launcher(log, data).await? {
res.push(issues);
}
Ok(res)
}
fn fabric_internal(log: &str) -> Issue {
const CLASS_NOT_FOUND: &str = "Caused by: java.lang.ClassNotFoundException: ";
let issue = (
"Fabric Internal Access".to_string(),
"The mod you are using is using fabric internals that are not meant \
to be used by anything but the loader itself.
Those mods break both on Quilt and with fabric updates.
If you're using fabric, downgrade your fabric loader could work, \
on Quilt you can try updating to the latest beta version, \
but there's nothing much to do unless the mod author stops using them."
.to_string(),
);
let errors = [
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.impl"),
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.mixin"),
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.impl"),
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.mixin"),
"org.quiltmc.loader.impl.FormattedException: java.lang.NoSuchMethodError:",
];
let found = errors.iter().any(|e| log.contains(e));
found.then_some(issue)
}
fn flatpak_nvidia(log: &str) -> Issue {
let issue = (
"Outdated Nvidia Flatpak Driver".to_string(),
"The Nvidia driver for flatpak is outdated.
Please run `flatpak update` to fix this issue. \
If that does not solve it, \
please wait until the driver is added to Flathub and run it again."
.to_string(),
);
let found = log.contains("org.lwjgl.LWJGLException: Could not choose GLX13 config")
|| log.contains("GLFW error 65545: GLX: Failed to find a suitable GLXFBConfig");
found.then_some(issue)
}
fn forge_java(log: &str) -> Issue {
let issue = (
"Forge Java Bug".to_string(),
"Old versions of Forge crash with Java 8u321+.
To fix this, update forge to the latest version via the Versions tab
(right click on Forge, click Change Version, and choose the latest one)
Alternatively, you can download 8u312 or lower. \
See [archive](https://github.com/adoptium/temurin8-binaries/releases/tag/jdk8u312-b07)"
.to_string(),
);
let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.<init>(Ljava/util/jar/Manifest;)V");
found.then_some(issue)
}
fn intel_hd(log: &str) -> Issue {
let issue =
(
"Intel HD Windows 10".to_string(),
"Your drivers don't support windows 10 officially
See https://prismlauncher.org/wiki/getting-started/installing-java/#a-note-about-intel-hd-20003000-on-windows-10 for more info".to_string()
);
let found = log.contains("org.lwjgl.LWJGLException: Pixel format not accelerated");
found.then_some(issue)
}
fn java_option(log: &str) -> Issue {
static VM_OPTION_REGEX: OnceLock<Regex> = OnceLock::new();
static UNRECOGNIZED_OPTION_REGEX: OnceLock<Regex> = OnceLock::new();
let vm_option =
VM_OPTION_REGEX.get_or_init(|| Regex::new(r"Unrecognized VM option '(.+)'[\r\n]").unwrap());
let unrecognized_option = UNRECOGNIZED_OPTION_REGEX
.get_or_init(|| Regex::new(r"Unrecognized option: (.+)[\r\n]").unwrap());
if let Some(captures) = vm_option.captures(log) {
let title = if &captures[1] == "UseShenandoahGC" {
"Wrong Java Arguments"
} else {
"Java 8 and below don't support ShenandoahGC"
};
return Some((
title.to_string(),
format!("Remove `-XX:{}` from your Java arguments", &captures[1]),
));
}
if let Some(captures) = unrecognized_option.captures(log) {
return Some((
"Wrong Java Arguments".to_string(),
format!("Remove `{}` from your Java arguments", &captures[1]),
));
}
None
}
fn lwjgl_2_java_9(log: &str) -> Issue {
let issue = (
"Linux: crash with pre-1.13 and Java 9+".to_string(),
"Using pre-1.13 (which uses LWJGL 2) with Java 9 or later usually causes a crash. \
Switching to Java 8 or below will fix your issue.
Alternatively, you can use [Temurin](https://adoptium.net/temurin/releases). \
However, multiplayer will not work in versions from 1.8 to 1.11.
For more information, type `/tag java`."
.to_string(),
);
let found = log.contains("check_match: Assertion `version->filename == NULL || ! _dl_name_match_p (version->filename, map)' failed!");
found.then_some(issue)
}
fn macos_ns(log: &str) -> Issue {
let issue = (
"MacOS NSInternalInconsistencyException".to_string(),
"You need to downgrade your Java 8 version. See https://prismlauncher.org/wiki/getting-started/installing-java/#older-minecraft-on-macos".to_string()
);
let found =
log.contains("Terminating app due to uncaught exception 'NSInternalInconsistencyException");
found.then_some(issue)
}
fn oom(log: &str) -> Issue {
let issue = (
"Out of Memory".to_string(),
"Allocating more RAM to your instance could help prevent this crash.".to_string(),
);
let found = log.contains("java.lang.OutOfMemoryError");
found.then_some(issue)
}
fn optinotfine(log: &str) -> Issue {
let issue = (
"Potential OptiFine Incompatibilities".to_string(),
"OptiFine is known to cause problems when paired with other mods. \
Try to disable OptiFine and see if the issue persists.
Check `/tag optifine` for more info & some typically more compatible alternatives you can use."
.to_string(),
);
let found = log.contains("[✔] OptiFine_") || log.contains("[✔] optifabric-");
found.then_some(issue)
}
async fn outdated_launcher(log: &str, data: &Data) -> Result<Issue> {
static OUTDATED_LAUNCHER_REGEX: OnceLock<Regex> = OnceLock::new();
let outdated_launcher = OUTDATED_LAUNCHER_REGEX.get_or_init(|| {
Regex::new("Prism Launcher version: ((?:([0-9]+)\\.)?([0-9]+)\\.([0-9]+))").unwrap()
});
let Some(captures) = outdated_launcher.captures(log) else {
return Ok(None);
};
let octocrab = &data.octocrab;
let log_version = &captures[1];
let log_version_parts = semver_split(log_version);
let latest_version = if let Some(storage) = &data.storage {
if let Ok(version) = storage.launcher_version().await {
version
} else {
let version = api::github::get_latest_prism_version(octocrab).await?;
storage.cache_launcher_version(&version).await?;
version
}
} else {
trace!("Not caching launcher version, as we're running without a storage backend");
api::github::get_latest_prism_version(octocrab).await?
};
let latest_version_parts = semver_split(&latest_version);
if log_version_parts.len() != 2
|| log_version_parts[0] < latest_version_parts[0]
|| (log_version_parts[0] == latest_version_parts[0]
&& log_version_parts[1] < latest_version_parts[1])
{
let issue = if log_version_parts[0] < 8 {
(
"Outdated Prism Launcher".to_string(),
format!("Your installed version is {log_version}, while the newest version is {latest_version}.\nPlease update; for more info see https://prismlauncher.org/download/")
)
} else {
(
"Outdated Prism Launcher".to_string(),
format!("Your installed version is {log_version}, while the newest version is {latest_version}.\nPlease update by pressing the `Update` button in the launcher or using your package manager.")
)
};
Ok(Some(issue))
} else {
Ok(None)
}
}
fn pre_1_12_native_transport_java_9(log: &str) -> Issue {
let issue = (
"Linux: broken multiplayer with 1.8-1.11 and Java 9+".to_string(),
"These versions of Minecraft use an outdated version of Netty which does not properly support Java 9.
Switching to Java 8 or below will fix this issue. For more information, type `/tag java`.
If you must use a newer version, do the following:
- Open `options.txt` (in the main window Edit -> Open .minecraft) and change.
- Find `useNativeTransport:true` and change it to `useNativeTransport:false`.
Note: whilst Netty was introduced in 1.7, this option did not exist \
which is why the issue was not present."
.to_string(),
);
let found = log.contains(
"java.lang.RuntimeException: Unable to access address of buffer\n\tat io.netty.channel.epoll"
);
found.then_some(issue)
}
fn wrong_java(log: &str) -> Issue {
static SWITCH_VERSION_REGEX: OnceLock<Regex> = OnceLock::new();
let switch_version = SWITCH_VERSION_REGEX.get_or_init(|| Regex::new(
r"(?m)Please switch to one of the following Java versions for this instance:[\r\n]+(Java version [\d.]+)",
).unwrap());
if let Some(captures) = switch_version.captures(log) {
let versions = captures[1].split('\n').collect::<Vec<&str>>().join(", ");
return Some((
"Wrong Java Version".to_string(),
format!("Please switch to one of the following: `{versions}`\nFor more information, type `/tag java`"),
));
}
let issue = (
"Java compatibility check skipped".to_string(),
"The Java major version may not work with your Minecraft instance. Please switch to a compatible version".to_string()
);
log.contains("Java major version is incompatible. Things might break.")
.then_some(issue)
}
fn forge_missing_dependencies(log: &str) -> Issue {
let issue = (
"Missing mod dependencies".to_string(),
"You seem to be missing mod dependencies.
Search for \"mandatory dependencies\" in your log."
.to_string(),
);
let found = log.contains("Missing or unsupported mandatory dependencies");
found.then_some(issue)
}
fn legacyjavafixer(log: &str) -> Issue {
let issue = (
"LegacyJavaFixer".to_string(),
"You are using a modern Java version with an old Forge version, which is causing this crash.
MinecraftForge provides a coremod to fix this issue, download it [here](https://dist.creeper.host/FTB2/maven/net/minecraftforge/lex/legacyjavafixer/1.0/legacyjavafixer-1.0.jar)."
.to_string(),
);
let found = log.contains(
"[SEVERE] [ForgeModLoader] Unable to launch\njava.util.ConcurrentModificationException",
);
found.then_some(issue)
}
fn locked_jar(log: &str) -> Issue {
let issue = (
"Locked Jars".to_string(),
"Something is locking your library jars.
To fix this, try rebooting your PC."
.to_string(),
);
let found = log.contains("Couldn't extract native jar");
found.then_some(issue)
}
fn offline_launch(log: &str) -> Issue {
let issue = (
"Missing Libraries".to_string(),
"You seem to be missing libraries. This is usually caused by launching offline before they can be downloaded.
To fix this, first ensure you are connected to the internet. Then, try selecting Edit > Version > Download All and launching your instance again."
.to_string(),
);
let found = log.contains("(missing)\n");
found.then_some(issue)
}
fn frapi(log: &str) -> Issue {
let issue = (
"Missing Indium".to_string(),
"You are using a mod that needs Indium.
Please install it by going to Edit > Mods > Download Mods."
.to_string(),
);
let found = log
.contains("Cannot invoke \"net.fabricmc.fabric.api.renderer.v1.Renderer.meshBuilder()\"");
found.then_some(issue)
}
fn no_disk_space(log: &str) -> Issue {
let issue = (
"Out of disk space".to_string(),
"You ran out of disk space. You should free up some space on it.".to_string(),
);
let found = log.contains("There is not enough space on the disk");
found.then_some(issue)
}
fn java_32_bit(log: &str) -> Issue {
let issue = (
"32 bit Java crash".to_string(),
"You are using a 32 bit Java version. Please select 64 bit Java instead.
Check `/tag java` for more information."
.to_string(),
);
let found = log.contains("Could not reserve enough space for ")
|| log.contains("Invalid maximum heap size: ");
found.then_some(issue)
}
fn intermediary_mappings(log: &str) -> Issue {
let issue = (
"Wrong Intermediary Mappings version".to_string(),
"You are using Intermediary Mappings for the wrong Minecraft version.
Please select Change Version while it is selected in Edit > Version."
.to_string(),
);
let found = log.contains("Mapping source name conflicts detected:");
found.then_some(issue)
}
fn old_forge_new_java(log: &str) -> Issue {
let issue = (
"Forge on old Minecraft versions".to_string(),
"This crash is caused by using an old Forge version with a modern Java version.
To fix it, add the flag `-Dfml.ignoreInvalidMinecraftCertificates=true` to Edit > Settings > Java arguments."
.to_string(),
);
let found = log.contains(
"add the flag -Dfml.ignoreInvalidMinecraftCertificates=true to the 'JVM settings'",
);
found.then_some(issue)
}