mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-20 23:16:31 +02:00
Game data of another build: the launcher asks for a verify, and mods are looked at again
After Steam updated skate., some installs had the supported Skate.exe over the new build's data (a copied game, or a download that stopped part-way). The launcher called that ready, every mod was left out as "made for game build 29544144, not the installed 29663206; rebuild it with ReSkate Studio", and the mods stayed out after the game files were put right. Launcher - Ready now also needs the game's data to be the supported build, read from Data\layout.toc against supported_build::game_data_changelist (29544144). Data of another build, or none, reads "Game files need verifying". - DOWNLOAD in that state checks every file (-validate) instead of fetching only what is missing. A download that fails part-way no longer ends on ready because Skate.exe arrived. - Data whose build cannot be read does not block. Mods - A mod left out because the game was of another build is no longer remembered in .reskate-excluded.json: it is checked on every launch. One already remembered for it is looked at again. - When it is the game that is not the supported build, the mod's message says to verify the game files in the launcher, not to rebuild the mod.
This commit is contained in:
1 parent
9124869ba4
commit
2e40aeb6e2
5 files changed
+73
-6
No files matched your search
@@ -23,6 +23,10 @@ inline constexpr std::uint32_t steam_app_id = 3354750;
|
||||
inline constexpr std::uint32_t steam_depot_id = 3354751;
|
||||
inline constexpr std::string_view steam_manifest_id = "4621099302092747785";
|
||||
inline constexpr std::string_view steam_build_id = "25414733";
|
||||
// The build number this build's data files carry (Data\layout.toc, "pipelineCodeChangelists"):
|
||||
// what mods made for it carry too. Data of another number beside a supported Skate.exe is a
|
||||
// game that was updated or copied over in part, and has to be verified.
|
||||
inline constexpr std::string_view game_data_changelist = "29544144";
|
||||
// The live game's HTTP content cache for this build (catalogue chunks and CDN
|
||||
// images), published as a ReSkateCache release and installed by the launcher.
|
||||
inline constexpr std::wstring_view content_cache_url =
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "mod_merge.h"
|
||||
#include "native_db.h"
|
||||
#include "Engine/Core/Json/json.h"
|
||||
#include "Engine/Game/Build/supported_build.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
@@ -402,7 +403,8 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
auto exclusions = read_exclusions(result.root);
|
||||
const auto sdk = sdk_identity();
|
||||
std::map<std::string, Exclusion, std::less<>> still_excluded;
|
||||
const auto leave_out = [&](std::size_t index, std::vector<std::string> problems, const std::string& fingerprint) {
|
||||
// `remembered`: it stays out on later launches without a retry, until its files or ReSkate change.
|
||||
const auto leave_out = [&](std::size_t index, std::vector<std::string> problems, const std::string& fingerprint, bool remembered = true) {
|
||||
auto mod = std::move(result.mods[index]);
|
||||
result.mods.erase(result.mods.begin() + static_cast<std::ptrdiff_t>(index));
|
||||
// Level bundles decide whether a map loads at all, so their problems lead.
|
||||
@@ -410,7 +412,7 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
return problem.find("levels/game/") != std::string::npos;
|
||||
});
|
||||
mod.problems = std::move(problems);
|
||||
still_excluded[mod.name] = {fingerprint, sdk, mod.problems};
|
||||
if (remembered) still_excluded[mod.name] = {fingerprint, sdk, mod.problems};
|
||||
result.excluded.push_back(std::move(mod));
|
||||
};
|
||||
for (std::size_t i = result.mods.size(); i-- > 0;) {
|
||||
@@ -418,6 +420,10 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
if (found == list.excluded.end()) continue;
|
||||
const auto& previous = exclusions.at(result.mods[i].name);
|
||||
if (sdk.empty() || previous.sdk != sdk) continue; // a new ReSkate may merge it now
|
||||
// One left out because the game was of another build is looked at again below: the
|
||||
// game may have been put right since (its files verified), with the mod untouched.
|
||||
// Remembered, it stayed out for good with the game fixed (seen 2026-10-13).
|
||||
if (!found->second.empty() && found->second.front().starts_with("made for game build ")) continue;
|
||||
result.notes.push_back("Leaving out " + result.mods[i].name +
|
||||
": it could not be merged cleanly before and its files have not changed since");
|
||||
leave_out(i, found->second, previous.fingerprint);
|
||||
@@ -438,9 +444,13 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
if (build.empty()) continue;
|
||||
result.notes.push_back("Leaving out " + result.mods[i].name + ": it was made for game build " + build +
|
||||
", the installed game is " + game_build);
|
||||
// (When it is the game that is of another build than ReSkate's, the mod is not what
|
||||
// needs seeing to.)
|
||||
leave_out(i, {"made for game build " + build + ", not the installed " + game_build +
|
||||
"; rebuild it with ReSkate Studio for this game build"},
|
||||
mod_fingerprint(result.mods[i].directory));
|
||||
(game_build != supported_build::game_data_changelist
|
||||
? "; your game files are not the version ReSkate supports: in the launcher, open Settings and press Verify game files"
|
||||
: "; rebuild it with ReSkate Studio for this game build")},
|
||||
mod_fingerprint(result.mods[i].directory), false); // (checked on every launch: the game's build can change under it)
|
||||
}
|
||||
for (std::size_t i = result.inactive.size(); i-- > 0;) {
|
||||
const auto build = made_for(result.inactive[i]);
|
||||
|
||||
@@ -181,7 +181,9 @@ void Launcher::download(bool validate, bool qr, std::string password) {
|
||||
password_ = std::move(password);
|
||||
qr_login_ = qr;
|
||||
}
|
||||
start([this, validate] { run_download(validate); });
|
||||
// Data of another build is put right by checking every file, not only fetching what is missing.
|
||||
const bool verify = validate || launcher_app::game_data_mismatched(session_.paths);
|
||||
start([this, verify] { run_download(verify); });
|
||||
}
|
||||
|
||||
void Launcher::play() { start([this] { run_play(); }); }
|
||||
@@ -333,7 +335,13 @@ void Launcher::run_check() {
|
||||
fail("This launcher is out of date. Download the latest ReSkate release.");
|
||||
return;
|
||||
}
|
||||
if (installed) set(Phase::game_outdated, "Steam updated Skate",
|
||||
// (Data of another build under a Skate.exe that may itself be right: every file is
|
||||
// checked, which download() sees to.)
|
||||
if (installed && launcher_app::game_data_mismatched(session_.paths))
|
||||
set(Phase::game_outdated, "Game files need verifying",
|
||||
"Some of the game's files are from another version of skate. (Steam updated it, or a download did not finish). "
|
||||
"Press DOWNLOAD: every file is checked and only the wrong ones are downloaded.");
|
||||
else if (installed) set(Phase::game_outdated, "Steam updated Skate",
|
||||
"ReSkate needs the build below. Only the files that differ are downloaded.");
|
||||
else set(Phase::game_missing, "Skate is not installed here",
|
||||
"Download the build below from Steam, about 14 GB, with an account that owns skate.");
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include "Engine/Game/Build/supported_build.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
@@ -468,6 +470,33 @@ Session open_session(const std::string& log_level) {
|
||||
return session;
|
||||
}
|
||||
|
||||
std::string game_data_build(const launcher::SiblingPaths& paths) {
|
||||
try {
|
||||
std::ifstream file(paths.directory / L"Data" / L"layout.toc", std::ios::binary);
|
||||
if (!file) return {};
|
||||
const std::string bytes{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
||||
// The number follows its name as text, after a few bytes that say what kind of value it is.
|
||||
constexpr std::string_view name = "pipelineCodeChangelists";
|
||||
const auto at = bytes.find(name);
|
||||
if (at == std::string::npos) return {};
|
||||
const auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
|
||||
auto from = at + name.size();
|
||||
const auto limit = std::min(bytes.size(), from + 16);
|
||||
while (from < limit && !is_digit(bytes[from])) ++from;
|
||||
auto to = from;
|
||||
while (to < bytes.size() && to - from < 16 && is_digit(bytes[to])) ++to;
|
||||
return to - from >= 6 ? bytes.substr(from, to - from) : std::string{};
|
||||
} catch (...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
bool game_data_mismatched(const launcher::SiblingPaths& paths) {
|
||||
std::error_code error;
|
||||
if (!fs::is_regular_file(paths.directory / L"Data" / L"layout.toc", error)) return true;
|
||||
const auto build = game_data_build(paths);
|
||||
return !build.empty() && build != supported_build::game_data_changelist;
|
||||
}
|
||||
|
||||
bool game_files_supported(const launcher::SiblingPaths& paths) {
|
||||
std::error_code error;
|
||||
if (!fs::is_regular_file(paths.game, error)) {
|
||||
@@ -477,6 +506,14 @@ bool game_files_supported(const launcher::SiblingPaths& paths) {
|
||||
try {
|
||||
launcher::validate_game_file(paths.game);
|
||||
launcher::validate_steam_api_file(paths.steam_api);
|
||||
// The right Skate.exe over another build's data is no supported game: its mods are
|
||||
// left out and it may not load (seen 2026-10-13, after a Steam update of the game).
|
||||
if (game_data_mismatched(paths)) {
|
||||
const auto build = game_data_build(paths);
|
||||
logging::log(logging::Level::warning, logging::Channel::launcher, "Game files are not the supported build: Skate.exe is, but the game's data is {} (supported: {}).",
|
||||
build.empty() ? std::string("missing") : "build " + build, supported_build::game_data_changelist);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (const std::exception& exception) {
|
||||
logging::log(logging::Level::warning, logging::Channel::launcher,
|
||||
|
||||
@@ -19,6 +19,14 @@ struct Session {
|
||||
Session open_session(const std::string& log_level);
|
||||
// Skate.exe and steam_api64.dll are present and exactly the supported build.
|
||||
bool game_files_supported(const launcher::SiblingPaths& paths);
|
||||
// The build number the installed game's data says it is (Data\layout.toc), or empty when
|
||||
// there is none or it cannot be read.
|
||||
std::string game_data_build(const launcher::SiblingPaths& paths);
|
||||
// Whether the game's data is missing or of another build than the supported one, whatever
|
||||
// Skate.exe is: Steam updated the game under it, a copy of an updated game was put here, or a
|
||||
// download stopped part-way. Such a game has to be verified against the supported build, file
|
||||
// by file, not only topped up. False when the data's build cannot be told.
|
||||
bool game_data_mismatched(const launcher::SiblingPaths& paths);
|
||||
// The config's depot/manifest ships the build this launcher was compiled for.
|
||||
bool config_matches_build(const launcher_update::Config& config);
|
||||
void relaunch(const std::filesystem::path& self, const std::vector<std::wstring>& arguments);
|
||||
|
||||
Reference in new issue
Block a user