Something went wrong. Try again.
Monorepo for PackTools - tooling for Minecraft Modpacks!
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104// AI-authored (Claude).#include "launchers.hpp"
#include "fs.hpp"#include "json.hpp"
#include <array>#include <cstddef>#include <optional>#include <string>#include <string_view>#include <utility>
namespace packutils::launchers {
namespace {
/// `mmc-pack.json` is a list of components; a large one is a few kilobytes.constexpr std::size_t pack_limit = std::size_t{1} << 20U;
struct LoaderUid { std::string_view uid; std::string_view loader;};
constexpr std::array<LoaderUid, 4> loader_uids{{ {.uid = "net.fabricmc.fabric-loader", .loader = "fabric"}, {.uid = "org.quiltmc.quilt-loader", .loader = "quilt"}, {.uid = "net.neoforged", .loader = "neoforge"}, {.uid = "net.minecraftforge", .loader = "forge"},}};
/// Reads the Minecraft and loader components; a file that is missing or does not parse says/// nothing, since the launcher will report it itself.Result<void> read_components(const std::string &directory, Instance &instance) { auto bytes = fs::read(fs::join(directory, "mmc-pack.json"), pack_limit); if (!bytes) { return std::unexpected(bytes.error()); } if (!*bytes) { return {}; } auto document = json::parse(**bytes); const json::Value *components = document ? document->get("components") : nullptr; if (components == nullptr || !components->is(json::Value::Type::array)) { return {}; } for (const json::Value &component : components->items) { const auto uid = component.string_at("uid"); const auto version = component.string_at("version"); if (!uid || !version) { continue; } if (*uid == "net.minecraft") { instance.minecraft = *version; continue; } for (const LoaderUid &known : loader_uids) { if (*uid == known.uid && instance.loader.empty()) { instance.loader = known.loader; instance.loader_version = *version; } } } return {};}
} // namespace
Result<std::optional<Instance>> detect(const std::string &directory) { auto config = fs::state(fs::join(directory, "instance.cfg")); if (!config) { return std::unexpected(config.error()); } if (*config != fs::State::file) { return std::optional<Instance>{}; } Instance instance{ .launcher = "prism", .game = {}, .minecraft = {}, .loader = {}, .loader_version = {}}; for (const std::string_view name : {".minecraft", "minecraft"}) { const std::string game = fs::join(directory, name); auto at = fs::state(game); if (!at) { return std::unexpected(at.error()); } if (*at == fs::State::directory) { instance.game = game; break; } } if (instance.game.empty()) { return std::unexpected(Failure::of( PU_KIND_INVALID_REQUEST, "InstanceInvalid", "the launcher instance " + directory + " has no game directory yet (.minecraft); start it once from the launcher")); } if (auto read = read_components(directory, instance); !read) { return std::unexpected(read.error()); } return std::optional<Instance>{std::move(instance)};}
} // namespace packutils::launchers