diff --git a/lang/en.json b/lang/en.json index 087ff993..08e54848 100644 --- a/lang/en.json +++ b/lang/en.json @@ -119,5 +119,7 @@ "paper-newer-world-config-problem": "The Paper config file of the world '{{world-name}}' was created by a newer Paper version (config version {{config-version}}, this server supports up to {{supported-version}}). Settings in it might not work.", "paper-config-reset-solution": "Back up and delete the file '{{file-path}}'. Paper creates it again with default settings.", "paper-world-config-reset-solution": "Back up and delete the file 'paper-world.yml' of the world '{{world-name}}'. Paper creates it again with default settings.", - "paper-install-newer-version-solution": "Install the newer Paper version that created this config file." + "paper-install-newer-version-solution": "Install the newer Paper version that created this config file.", + "mod-fabric-loader-version-problem": "The mod '{{mod-name}}' requires Fabric Loader version {{loader-version}} or later, but version {{installed-version}} is installed.", + "fabric-loader-update-solution": "Update Fabric Loader to version {{loader-version}} or later." } diff --git a/src/Analyser/FabricAnalyser.php b/src/Analyser/FabricAnalyser.php index e6d4a273..e9e9071e 100644 --- a/src/Analyser/FabricAnalyser.php +++ b/src/Analyser/FabricAnalyser.php @@ -10,6 +10,7 @@ use Aternos\Codex\Minecraft\Analysis\Problem\Fabric\FabricDuplicateModProblem; use Aternos\Codex\Minecraft\Analysis\Problem\Fabric\FabricEntryStageProblem; use Aternos\Codex\Minecraft\Analysis\Problem\Fabric\FabricIncompatibleModsProblem; +use Aternos\Codex\Minecraft\Analysis\Problem\Fabric\FabricLoaderVersionProblem; use Aternos\Codex\Minecraft\Analysis\Problem\Fabric\FabricMixinProblem; use Aternos\Codex\Minecraft\Analysis\Problem\Fabric\FabricModDependencyProblem; use Aternos\Codex\Minecraft\Analysis\Problem\Fabric\FabricModIncompatibleMinecraftVersionProblem; @@ -26,6 +27,7 @@ public function __construct() $this->addPossibleInsightClass(FabricModDependencyProblem::class); $this->addPossibleInsightClass(FabricModIncompatibleMinecraftVersionProblem::class); $this->addPossibleInsightClass(FabricModJavaVersionProblem::class); + $this->addPossibleInsightClass(FabricLoaderVersionProblem::class); $this->addPossibleInsightClass(FabricMixinProblem::class); $this->addPossibleInsightClass(FabricIncompatibleModsProblem::class); $this->addPossibleInsightClass(FabricDuplicateModProblem::class); diff --git a/src/Analysis/Problem/Fabric/FabricLoaderVersionProblem.php b/src/Analysis/Problem/Fabric/FabricLoaderVersionProblem.php new file mode 100644 index 00000000..9231a522 --- /dev/null +++ b/src/Analysis/Problem/Fabric/FabricLoaderVersionProblem.php @@ -0,0 +1,112 @@ +loaderVersion; + } + + /** + * @param string $loaderVersion + * @return $this + */ + protected function setLoaderVersion(string $loaderVersion): static + { + $this->loaderVersion = $loaderVersion; + return $this; + } + + /** + * @return string + */ + public function getInstalledVersion(): string + { + return $this->installedVersion; + } + + /** + * @param string $installedVersion + * @return $this + */ + protected function setInstalledVersion(string $installedVersion): static + { + $this->installedVersion = $installedVersion; + return $this; + } + + /** + * @inheritDoc + */ + public function getMessage(): string + { + return Translator::getInstance()->getTranslation("mod-fabric-loader-version-problem", [ + "mod-name" => $this->getModName(), + "loader-version" => $this->getLoaderVersion(), + "installed-version" => $this->getInstalledVersion() + ]); + } + + /** + * Matches a Fabric Loader version requirement in the dependency list of the loader, e.g. + * - Mod 'Foo' (foo) 1.0.0 requires version 99.0.0 or later of mod 'Fabric Loader' (fabricloader), but only the wrong version is present: 0.20.1! + * - Mod 'Foo' (foo) 1.0.0 requires version 99.0.0 of mod 'Fabric Loader' (fabricloader), but only the wrong version is present: 0.20.1! + * + * @inheritDoc + */ + public static function getPatterns(): array + { + $mod = "/- Mod " . static::getModNamePattern() . "(?: [^ ]+)? "; + $loader = " of " . static::LOADER_MOD . ", but only the wrong version is present: (?[^ !]+)!/"; + + return [ + 'minimum' => $mod . "requires version (?[^ ]+) or later" . $loader, + 'specific' => $mod . "requires version (?[^ ]+)" . $loader, + ]; + } + + /** + * @inheritDoc + */ + public function setMatches(array $matches, mixed $patternKey): void + { + $this->setModName($matches["mod"]); + $this->setLoaderVersion($matches["required"]); + $this->setInstalledVersion($matches["installed"]); + $this->addSolution(new FabricLoaderUpdateSolution($this->getLoaderVersion())); + } + + /** + * @param InsightInterface $insight + * @return bool + */ + public function isEqual(InsightInterface $insight): bool + { + return $insight instanceof static + && parent::isEqual($insight) + && $this->getLoaderVersion() === $insight->getLoaderVersion(); + } +} diff --git a/src/Analysis/Problem/Fabric/FabricModDependencyProblem.php b/src/Analysis/Problem/Fabric/FabricModDependencyProblem.php index 4aa7aa7f..b1d818dc 100644 --- a/src/Analysis/Problem/Fabric/FabricModDependencyProblem.php +++ b/src/Analysis/Problem/Fabric/FabricModDependencyProblem.php @@ -35,8 +35,10 @@ public function getMessage(): string public static function getPatterns(): array { $mod = "/- Mod " . static::getModNamePattern() . "(?: [^ ]+)? "; - // Java has its own problem, FabricModJavaVersionProblem - $dependency = " of (?:mod )?(?!" . FabricModJavaVersionProblem::JAVA_MOD . ")" . static::getModNamePattern("dependency") . ",/"; + // Java and the Fabric Loader have their own problems, FabricModJavaVersionProblem and FabricLoaderVersionProblem + $dependency = " of (?!" . FabricModJavaVersionProblem::JAVA_MOD + . "|" . FabricLoaderVersionProblem::LOADER_MOD . ")(?:mod )?" + . static::getModNamePattern("dependency") . ",/"; return [ 'short-error' => '/net\.fabricmc\.loader\.discovery\.ModResolutionException: Could not find required mod: ' . static::getModNamePattern() diff --git a/src/Analysis/Solution/Fabric/FabricLoaderUpdateSolution.php b/src/Analysis/Solution/Fabric/FabricLoaderUpdateSolution.php new file mode 100644 index 00000000..60a81fc6 --- /dev/null +++ b/src/Analysis/Solution/Fabric/FabricLoaderUpdateSolution.php @@ -0,0 +1,45 @@ +getTranslation("fabric-loader-update-solution", ["loader-version" => $this->getVersion()]); + } + + /** + * @return string + */ + public function getVersion(): string + { + return $this->version; + } + + /** + * @param string $version + * @return $this + */ + public function setVersion(string $version): static + { + $this->version = $version; + return $this; + } +} diff --git a/test/data/Vanilla/Fabric/fabric-loader-outdated.json b/test/data/Vanilla/Fabric/fabric-loader-outdated.json new file mode 100644 index 00000000..de4789a0 --- /dev/null +++ b/test/data/Vanilla/Fabric/fabric-loader-outdated.json @@ -0,0 +1,272 @@ +{ + "id": "fabric\/server", + "name": "Fabric", + "type": "Server Log", + "version": "26.3", + "title": "Fabric 26.3 Server Log", + "entries": [ + { + "level": 6, + "time": null, + "prefix": null, + "lines": [ + { + "number": 1, + "content": "Starting net.fabricmc.loader.impl.game.minecraft.BundlerClassPathCapture" + } + ] + }, + { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 2, + "content": "[18:57:49] [main\/INFO]: Loading Minecraft 26.3 with Fabric Loader 0.20.1" + } + ] + }, + { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 3, + "content": "[18:57:49] [main\/INFO]: Mappings not present!" + } + ] + }, + { + "level": 4, + "time": null, + "prefix": "[18:57:49] [main\/WARN]:", + "lines": [ + { + "number": 4, + "content": "[18:57:49] [main\/WARN]: Mod resolution failed" + } + ] + }, + { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 5, + "content": "[18:57:49] [main\/INFO]: Immediate reason: [HARD_DEP_NO_CANDIDATE foo 1.0.0 {depends fabricloader @ [>=99.0.0]}, ROOT_FORCELOAD_SINGLE foo 1.0.0]" + } + ] + }, + { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 6, + "content": "[18:57:49] [main\/INFO]: Reason: [HARD_DEP foo 1.0.0 {depends fabricloader @ [>=99.0.0]}]" + } + ] + }, + { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 7, + "content": "[18:57:49] [main\/INFO]: SERVER environment disabled: [fabric-renderer-api-v1, fabric-model-loading-api-v1, fabric-key-mapping-api-v1, fabric-screen-api-v1, fabric-sound-api-v1, fabric-rendering-v1, fabric-renderer-indigo]" + } + ] + }, + { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 8, + "content": "[18:57:49] [main\/INFO]: Fix: add [], remove [], replace [[fabricloader 0.20.1] -> add:fabricloader 99.0.0 ([[99.0.0,\u221e)])]" + } + ] + }, + { + "level": 3, + "time": null, + "prefix": "[18:57:49] [main\/ERROR]:", + "lines": [ + { + "number": 9, + "content": "[18:57:49] [main\/ERROR]: Incompatible mods found!" + }, + { + "number": 10, + "content": "net.fabricmc.loader.impl.FormattedException: Some of your mods are incompatible with the game or each other!" + }, + { + "number": 11, + "content": "A potential solution has been determined, this may resolve your problem:" + }, + { + "number": 12, + "content": "\t - Replace mod 'Fabric Loader' (fabricloader) 0.20.1 with version 99.0.0 or later." + }, + { + "number": 13, + "content": "More details:" + }, + { + "number": 14, + "content": "\t - Mod 'Foo' (foo) 1.0.0 requires version 99.0.0 or later of mod 'Fabric Loader' (fabricloader), but only the wrong version is present: 0.20.1!" + }, + { + "number": 15, + "content": "\tat net.fabricmc.loader.impl.FormattedException.ofLocalized(FormattedException.java:51)" + }, + { + "number": 16, + "content": "\tat net.fabricmc.loader.impl.FabricLoaderImpl.load(FabricLoaderImpl.java:207)" + }, + { + "number": 17, + "content": "\tat net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:142)" + }, + { + "number": 18, + "content": "\tat net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:66)" + }, + { + "number": 19, + "content": "\tat net.fabricmc.loader.impl.launch.knot.KnotServer.main(KnotServer.java:23)" + }, + { + "number": 20, + "content": "\tat net.fabricmc.loader.impl.launch.server.FabricServerLauncher.main(FabricServerLauncher.java:69)" + }, + { + "number": 21, + "content": "\tat net.fabricmc.installer.ServerLauncher.main(ServerLauncher.java:69)" + }, + { + "number": 22, + "content": "" + } + ] + } + ], + "analysis": { + "problems": [ + { + "message": "The mod 'Foo' requires Fabric Loader version 99.0.0 or later, but version 0.20.1 is installed.", + "counter": 1, + "entry": { + "level": 3, + "time": null, + "prefix": "[18:57:49] [main\/ERROR]:", + "lines": [ + { + "number": 9, + "content": "[18:57:49] [main\/ERROR]: Incompatible mods found!" + }, + { + "number": 10, + "content": "net.fabricmc.loader.impl.FormattedException: Some of your mods are incompatible with the game or each other!" + }, + { + "number": 11, + "content": "A potential solution has been determined, this may resolve your problem:" + }, + { + "number": 12, + "content": "\t - Replace mod 'Fabric Loader' (fabricloader) 0.20.1 with version 99.0.0 or later." + }, + { + "number": 13, + "content": "More details:" + }, + { + "number": 14, + "content": "\t - Mod 'Foo' (foo) 1.0.0 requires version 99.0.0 or later of mod 'Fabric Loader' (fabricloader), but only the wrong version is present: 0.20.1!" + }, + { + "number": 15, + "content": "\tat net.fabricmc.loader.impl.FormattedException.ofLocalized(FormattedException.java:51)" + }, + { + "number": 16, + "content": "\tat net.fabricmc.loader.impl.FabricLoaderImpl.load(FabricLoaderImpl.java:207)" + }, + { + "number": 17, + "content": "\tat net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:142)" + }, + { + "number": 18, + "content": "\tat net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:66)" + }, + { + "number": 19, + "content": "\tat net.fabricmc.loader.impl.launch.knot.KnotServer.main(KnotServer.java:23)" + }, + { + "number": 20, + "content": "\tat net.fabricmc.loader.impl.launch.server.FabricServerLauncher.main(FabricServerLauncher.java:69)" + }, + { + "number": 21, + "content": "\tat net.fabricmc.installer.ServerLauncher.main(ServerLauncher.java:69)" + }, + { + "number": 22, + "content": "" + } + ] + }, + "solutions": [ + { + "message": "Update Fabric Loader to version 99.0.0 or later." + } + ] + } + ], + "information": [ + { + "message": "Minecraft version: 26.3", + "counter": 1, + "entry": { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 2, + "content": "[18:57:49] [main\/INFO]: Loading Minecraft 26.3 with Fabric Loader 0.20.1" + } + ] + }, + "label": "Minecraft version", + "value": "26.3" + }, + { + "message": "Fabric loader version: 0.20.1", + "counter": 1, + "entry": { + "level": 6, + "time": null, + "prefix": "[18:57:49] [main\/INFO]:", + "lines": [ + { + "number": 2, + "content": "[18:57:49] [main\/INFO]: Loading Minecraft 26.3 with Fabric Loader 0.20.1" + } + ] + }, + "label": "Fabric loader version", + "value": "0.20.1" + } + ] + } +} \ No newline at end of file diff --git a/test/data/Vanilla/Fabric/fabric-loader-outdated.log b/test/data/Vanilla/Fabric/fabric-loader-outdated.log new file mode 100644 index 00000000..d3830180 --- /dev/null +++ b/test/data/Vanilla/Fabric/fabric-loader-outdated.log @@ -0,0 +1,21 @@ +Starting net.fabricmc.loader.impl.game.minecraft.BundlerClassPathCapture +[18:57:49] [main/INFO]: Loading Minecraft 26.3 with Fabric Loader 0.20.1 +[18:57:49] [main/INFO]: Mappings not present! +[18:57:49] [main/WARN]: Mod resolution failed +[18:57:49] [main/INFO]: Immediate reason: [HARD_DEP_NO_CANDIDATE foo 1.0.0 {depends fabricloader @ [>=99.0.0]}, ROOT_FORCELOAD_SINGLE foo 1.0.0] +[18:57:49] [main/INFO]: Reason: [HARD_DEP foo 1.0.0 {depends fabricloader @ [>=99.0.0]}] +[18:57:49] [main/INFO]: SERVER environment disabled: [fabric-renderer-api-v1, fabric-model-loading-api-v1, fabric-key-mapping-api-v1, fabric-screen-api-v1, fabric-sound-api-v1, fabric-rendering-v1, fabric-renderer-indigo] +[18:57:49] [main/INFO]: Fix: add [], remove [], replace [[fabricloader 0.20.1] -> add:fabricloader 99.0.0 ([[99.0.0,∞)])] +[18:57:49] [main/ERROR]: Incompatible mods found! +net.fabricmc.loader.impl.FormattedException: Some of your mods are incompatible with the game or each other! +A potential solution has been determined, this may resolve your problem: + - Replace mod 'Fabric Loader' (fabricloader) 0.20.1 with version 99.0.0 or later. +More details: + - Mod 'Foo' (foo) 1.0.0 requires version 99.0.0 or later of mod 'Fabric Loader' (fabricloader), but only the wrong version is present: 0.20.1! + at net.fabricmc.loader.impl.FormattedException.ofLocalized(FormattedException.java:51) + at net.fabricmc.loader.impl.FabricLoaderImpl.load(FabricLoaderImpl.java:207) + at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:142) + at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:66) + at net.fabricmc.loader.impl.launch.knot.KnotServer.main(KnotServer.java:23) + at net.fabricmc.loader.impl.launch.server.FabricServerLauncher.main(FabricServerLauncher.java:69) + at net.fabricmc.installer.ServerLauncher.main(ServerLauncher.java:69) diff --git a/test/tests/Logs/AutoLogsTest.php b/test/tests/Logs/AutoLogsTest.php index 92f8fece..5d55bdb2 100644 --- a/test/tests/Logs/AutoLogsTest.php +++ b/test/tests/Logs/AutoLogsTest.php @@ -1320,6 +1320,16 @@ public function test_fabric_java8(): void $this->assertStringEqualsFile($log->getExpectedPath(), $log->getOutput(), $log->getLogPath()); } + /** + * @return void + * @throws Exception + */ + public function test_fabric_loader_outdated(): void + { + $log = new TestLog('Vanilla/Fabric/fabric-loader-outdated.log'); + $this->assertStringEqualsFile($log->getExpectedPath(), $log->getOutput(), $log->getLogPath()); + } + /** * @return void * @throws Exception