mirror of
https://github.com/acidicoala/SmokeAPI.git
synced 2026-01-24 21:42:53 -05:00
bump to v2, koalageddon mode improvements
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
project(SmokeAPI VERSION 1.1.0)
|
||||
project(SmokeAPI VERSION 2.0.0)
|
||||
|
||||
include(KoalaBox/cmake/KoalaBox.cmake)
|
||||
|
||||
@@ -35,7 +35,13 @@ configure_build_config(extra_build_config)
|
||||
|
||||
set(
|
||||
SMOKE_API_SOURCES
|
||||
src/core/cache.cpp
|
||||
src/core/cache.hpp
|
||||
src/core/globals.cpp
|
||||
src/core/globals.hpp
|
||||
src/core/macros.hpp
|
||||
src/core/paths.cpp
|
||||
src/core/paths.hpp
|
||||
src/smoke_api/smoke_api.cpp
|
||||
src/smoke_api/smoke_api.hpp
|
||||
src/steam_api_exports/steam_api_flat.cpp
|
||||
@@ -61,6 +67,7 @@ set(
|
||||
${GENERATED_LINKER_EXPORTS}
|
||||
)
|
||||
|
||||
# Include koalageddon mode sources only in 32-bit builds
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(
|
||||
SMOKE_API_SOURCES ${SMOKE_API_SOURCES}
|
||||
|
||||
2
KoalaBox
2
KoalaBox
Submodule KoalaBox updated: 63b3efc76a...418cb437a7
80
src/core/cache.cpp
Normal file
80
src/core/cache.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include <core/cache.hpp>
|
||||
#include <core/paths.hpp>
|
||||
#include <koalabox/io.hpp>
|
||||
|
||||
namespace cache {
|
||||
Cache read_cache_from_disk() {
|
||||
try {
|
||||
const auto cache_string = io::read_file(paths::get_cache_path());
|
||||
|
||||
if (cache_string.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return nlohmann::json::parse(cache_string).get<Cache>();
|
||||
} catch (const Exception& e) {
|
||||
logger->warn("{} -> Failed to read cache from disk: {}", __func__, e.what());
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void write_cache_to_disk(const Cache& cache) {
|
||||
try {
|
||||
const auto cache_string = nlohmann::json(cache).dump(2);
|
||||
|
||||
io::write_file(paths::get_cache_path(), cache_string);
|
||||
} catch (const Exception& e) {
|
||||
logger->error("{} -> Failed to write cache to disk: {}", __func__, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
Vector<AppId_t> get_dlc_ids(AppId_t app_id) {
|
||||
const auto cache = read_cache_from_disk();
|
||||
|
||||
const auto app_id_str = std::to_string(app_id);
|
||||
|
||||
if (cache.apps.contains(app_id_str)) {
|
||||
return cache.apps.at(app_id_str).dlc_ids;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<koalageddon::KoalageddonConfig> get_koalageddon_config() {
|
||||
const auto cache = read_cache_from_disk();
|
||||
|
||||
if (cache.koalageddon_config.is_null()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto config = cache.koalageddon_config.get<koalageddon::KoalageddonConfig>();
|
||||
return std::optional{config};
|
||||
}
|
||||
|
||||
void save_dlc_ids(AppId_t app_id, const Vector<AppId_t>& dlc_ids) {
|
||||
logger->debug("{} -> Caching DLC IDs for the app: {}", __func__, app_id);
|
||||
|
||||
auto cache = read_cache_from_disk();
|
||||
|
||||
const auto app_id_str = std::to_string(app_id);
|
||||
|
||||
if (not cache.apps.contains(app_id_str)) {
|
||||
cache.apps[app_id_str] = {};
|
||||
}
|
||||
|
||||
cache.apps[app_id_str].dlc_ids = dlc_ids;
|
||||
|
||||
write_cache_to_disk(cache);
|
||||
}
|
||||
|
||||
void save_koalageddon_config(const koalageddon::KoalageddonConfig& config) {
|
||||
logger->debug("{} -> Caching koalageddon config", __func__);
|
||||
|
||||
auto cache = read_cache_from_disk();
|
||||
|
||||
cache.koalageddon_config = config;
|
||||
|
||||
write_cache_to_disk(cache);
|
||||
}
|
||||
}
|
||||
36
src/core/cache.hpp
Normal file
36
src/core/cache.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/koalabox.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <steam_types/steam_types.hpp>
|
||||
|
||||
/**
|
||||
* This namespace contains utility functions for reading from and writing to cache file on disk.
|
||||
* All functions are intended to be safe to call, i.e. they should not throw exceptions.
|
||||
*/
|
||||
namespace cache {
|
||||
using namespace koalabox;
|
||||
|
||||
struct App {
|
||||
Vector<AppId_t> dlc_ids;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(App, dlc_ids) // NOLINT(misc-const-correctness)
|
||||
};
|
||||
|
||||
struct Cache {
|
||||
// Key represents App ID
|
||||
Map<String, App> apps;
|
||||
nlohmann::json koalageddon_config;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Cache, apps, koalageddon_config) // NOLINT(misc-const-correctness)
|
||||
};
|
||||
|
||||
Vector<AppId_t> get_dlc_ids(AppId_t app_id);
|
||||
|
||||
std::optional<koalageddon::KoalageddonConfig> get_koalageddon_config();
|
||||
|
||||
void save_dlc_ids(AppId_t app_id, const Vector<AppId_t>& dlc_ids);
|
||||
|
||||
void save_koalageddon_config(const koalageddon::KoalageddonConfig& config);
|
||||
}
|
||||
4
src/core/globals.cpp
Normal file
4
src/core/globals.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace globals {
|
||||
HMODULE self_module = nullptr;
|
||||
// TODO: Original module (rename to proxy module?)
|
||||
}
|
||||
5
src/core/globals.hpp
Normal file
5
src/core/globals.hpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace globals {
|
||||
extern HMODULE self_module;
|
||||
}
|
||||
25
src/core/paths.cpp
Normal file
25
src/core/paths.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <koalabox/loader.hpp>
|
||||
#include <core/paths.hpp>
|
||||
#include <core/globals.hpp>
|
||||
|
||||
namespace paths {
|
||||
Path get_self_path() {
|
||||
static const auto path = loader::get_module_dir(globals::self_module);
|
||||
return path;
|
||||
}
|
||||
|
||||
Path get_config_path() {
|
||||
static const auto path = get_self_path() / "SmokeAPI.config.json";
|
||||
return path;
|
||||
}
|
||||
|
||||
Path get_cache_path() {
|
||||
static const auto path = get_self_path() / "SmokeAPI.cache.json";
|
||||
return path;
|
||||
}
|
||||
|
||||
Path get_log_path() {
|
||||
static const auto path = get_self_path() / "SmokeAPI.log.log";
|
||||
return path;
|
||||
}
|
||||
}
|
||||
16
src/core/paths.hpp
Normal file
16
src/core/paths.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <koalabox/koalabox.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace paths {
|
||||
using namespace koalabox;
|
||||
|
||||
/**
|
||||
* @return An std::path instance representing the directory containing this DLL
|
||||
*/
|
||||
Path get_self_path();
|
||||
Path get_config_path();
|
||||
Path get_cache_path();
|
||||
Path get_log_path();
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
#include <build_config.h>
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
#include <core/cache.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <koalabox/dll_monitor.hpp>
|
||||
#include <koalabox/http_client.hpp>
|
||||
#include <koalabox/io.hpp>
|
||||
|
||||
namespace koalageddon {
|
||||
KoalageddonConfig config = {};
|
||||
KoalageddonConfig config; // NOLINT(cert-err58-cpp)
|
||||
|
||||
/**
|
||||
* @return A string representing the source of the config.
|
||||
@@ -19,41 +19,38 @@ namespace koalageddon {
|
||||
|
||||
return "local config override";
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Local koalageddon config parse exception: {}", ex.what());
|
||||
logger->error("Failed to get local koalageddon config: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
const auto config_cache_path = smoke_api::self_directory / "SmokeAPI.koalageddon.json";
|
||||
|
||||
try {
|
||||
// Then try to fetch config from GitHub
|
||||
const String url = "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/koalageddon/v2/steam.json";
|
||||
config = http_client::fetch_json(url).get<decltype(config)>();
|
||||
|
||||
io::write_file(config_cache_path, nlohmann::json(config).dump(2));
|
||||
cache::save_koalageddon_config(config);
|
||||
|
||||
return "GitHub repository";
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Remote koalageddon config parse exception: {}", ex.what());
|
||||
logger->error("Failed to get remote koalageddon config: {}", ex.what());
|
||||
}
|
||||
|
||||
try {
|
||||
// Then try to get a cached copy of a previously fetched config
|
||||
const auto cache = io::read_file(config_cache_path);
|
||||
// Then try to get a cached copy of a previously fetched config.
|
||||
// We expect call to value() to throw if no koalageddon config is present
|
||||
config = cache::get_koalageddon_config().value();
|
||||
|
||||
config = nlohmann::json::parse(cache).get<decltype(config)>();
|
||||
|
||||
return "Local cache";
|
||||
return "disk cache";
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Cached koalageddon config parse exception: {}", ex.what());
|
||||
logger->error("Failed to get cached koalageddon config: {}", ex.what());
|
||||
}
|
||||
|
||||
// Finally, fallback on the default config
|
||||
config = {};
|
||||
return "default config bundled in the binary";
|
||||
}
|
||||
|
||||
void init() {
|
||||
#ifndef _WIN64
|
||||
logger->info("🐨 Detected Koalageddon mode 💥");
|
||||
|
||||
std::thread([]() {
|
||||
@@ -66,13 +63,13 @@ namespace koalageddon {
|
||||
smoke_api::original_library = library;
|
||||
|
||||
static auto init_count = 0;
|
||||
if (name == VSTDLIB_DLL) {
|
||||
if (util::strings_are_equal(name, VSTDLIB_DLL)) {
|
||||
// VStdLib DLL handles Family Sharing functions
|
||||
if (smoke_api::config.unlock_family_sharing) {
|
||||
init_vstdlib_hooks();
|
||||
}
|
||||
init_count++;
|
||||
} else if (name == STEAMCLIENT_DLL) {
|
||||
} else if (util::strings_are_equal(name, STEAMCLIENT_DLL)) {
|
||||
// SteamClient DLL handles unlocking functions
|
||||
init_steamclient_hooks();
|
||||
init_count++;
|
||||
@@ -85,6 +82,5 @@ namespace koalageddon {
|
||||
logger->error("Koalageddon mode dll monitor init error. Module: '{}', Message: {}", name, ex.what());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
// This header will be populated at build time
|
||||
#include <linker_exports.h>
|
||||
|
||||
EXTERN_C [[maybe_unused]] BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID) {
|
||||
EXTERN_C [[maybe_unused]] BOOL WINAPI DllMain(HMODULE module_handle, DWORD reason, LPVOID) {
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
smoke_api::init(module);
|
||||
smoke_api::init(module_handle);
|
||||
} else if (reason == DLL_PROCESS_DETACH) {
|
||||
smoke_api::shutdown();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <core/paths.hpp>
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
#include <build_config.h>
|
||||
|
||||
@@ -8,6 +9,7 @@
|
||||
#include <koalabox/hook.hpp>
|
||||
#include <koalabox/loader.hpp>
|
||||
#include <koalabox/win_util.hpp>
|
||||
#include <core/globals.hpp>
|
||||
|
||||
#ifndef _WIN64
|
||||
#include <koalageddon/koalageddon.hpp>
|
||||
@@ -18,19 +20,14 @@ namespace smoke_api {
|
||||
|
||||
HMODULE original_library = nullptr;
|
||||
|
||||
HMODULE self_module = nullptr;
|
||||
|
||||
bool is_hook_mode = false;
|
||||
|
||||
Path self_directory;
|
||||
|
||||
void init_config() {
|
||||
// TODO: Detect koalageddon mode first, and then fetch config from corresponding directory
|
||||
config = config_parser::parse<Config>(self_directory / PROJECT_NAME".json");
|
||||
}
|
||||
|
||||
void init_proxy_mode() {
|
||||
logger->info("🔀 Detected proxy mode");
|
||||
|
||||
original_library = loader::load_original_library(self_directory, ORIGINAL_DLL);
|
||||
original_library = loader::load_original_library(paths::get_self_path(), ORIGINAL_DLL);
|
||||
}
|
||||
|
||||
void init_hook_mode() {
|
||||
@@ -61,22 +58,22 @@ namespace smoke_api {
|
||||
// the support for it has been dropped from this project.
|
||||
}
|
||||
|
||||
void init(HMODULE self_module) {
|
||||
void init(HMODULE module_handle) {
|
||||
try {
|
||||
DisableThreadLibraryCalls(self_module);
|
||||
DisableThreadLibraryCalls(module_handle);
|
||||
|
||||
globals::self_module = module_handle;
|
||||
|
||||
koalabox::project_name = PROJECT_NAME;
|
||||
|
||||
self_directory = loader::get_module_dir(self_module);
|
||||
|
||||
init_config();
|
||||
config = config_parser::parse<Config>(paths::get_config_path());
|
||||
|
||||
const auto exe_path = Path(win_util::get_module_file_name_or_throw(nullptr));
|
||||
const auto exe_name = exe_path.filename().string();
|
||||
const auto exe_bitness = util::is_x64() ? 64 : 32;
|
||||
|
||||
if (config.logging) {
|
||||
logger = file_logger::create(self_directory / fmt::format("{}.log", PROJECT_NAME));
|
||||
logger = file_logger::create(paths::get_log_path());
|
||||
}
|
||||
|
||||
logger->info("🐨 {} v{}", PROJECT_NAME, PROJECT_VERSION);
|
||||
|
||||
@@ -52,13 +52,13 @@ namespace smoke_api {
|
||||
|
||||
extern Config config;
|
||||
|
||||
extern HMODULE self_module;
|
||||
|
||||
extern HMODULE original_library;
|
||||
|
||||
extern bool is_hook_mode;
|
||||
|
||||
extern Path self_directory;
|
||||
|
||||
void init(HMODULE self_module);
|
||||
void init(HMODULE module_handle);
|
||||
|
||||
void shutdown();
|
||||
|
||||
|
||||
@@ -38,17 +38,10 @@
|
||||
#endif
|
||||
|
||||
class ISteamClient;
|
||||
|
||||
class ISteamApps;
|
||||
|
||||
class ISteamUser;
|
||||
|
||||
class ISteamInventory;
|
||||
|
||||
typedef __int32 HSteamPipe;
|
||||
typedef __int32 HSteamUser;
|
||||
typedef uint32_t AppId_t;
|
||||
typedef uint64_t CSteamID;
|
||||
|
||||
// TODO: Refactor into multiple headers
|
||||
|
||||
|
||||
@@ -1,44 +1,20 @@
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
#include <steam_impl/steam_apps.hpp>
|
||||
|
||||
#include <cpr/cpr.h>
|
||||
#include <koalabox/io.hpp>
|
||||
#include <koalabox/http_client.hpp>
|
||||
|
||||
#include <cpr/cpr.h>
|
||||
#include <core/cache.hpp>
|
||||
#include <smoke_api/smoke_api.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
/// Steamworks may max GetDLCCount value at 64, depending on how much unowned DLCs the user has.
|
||||
/// Despite this limit, some games with more than 64 DLCs still keep using this method.
|
||||
/// This means we have to get extra DLC IDs from local config/remote config/cache.
|
||||
/// This means we have to get extra DLC IDs from local config, remote config, or cache.
|
||||
constexpr auto MAX_DLC = 64;
|
||||
|
||||
Vector<AppId_t> cached_dlcs;
|
||||
// Key: App ID, Value: DLC ID
|
||||
Map<AppId_t, int> original_dlc_count_map; // NOLINT(cert-err58-cpp)
|
||||
|
||||
// FIXME: Path in koalageddon mode
|
||||
Path get_cache_path() {
|
||||
static const auto path = self_directory / "SmokeAPI.cache.json";
|
||||
return path;
|
||||
}
|
||||
|
||||
void save_cache_to_disk(const String& app_id_str) {
|
||||
try {
|
||||
logger->debug("Saving {} DLCs to cache", cached_dlcs.size());
|
||||
|
||||
// TODO: Combine this with existing cache
|
||||
const nlohmann::json json = {
|
||||
{app_id_str, {
|
||||
{"dlc", cached_dlcs}
|
||||
}}
|
||||
};
|
||||
|
||||
io::write_file(get_cache_path(), json.dump(2));
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Error saving DLCs to cache: {}", ex.what());
|
||||
}
|
||||
}
|
||||
Vector<AppId_t> cached_dlcs;
|
||||
|
||||
/**
|
||||
* @param app_id
|
||||
@@ -59,29 +35,6 @@ bool fetch_and_cache_dlcs(AppId_t app_id) {
|
||||
auto total_success = true;
|
||||
const auto app_id_str = std::to_string(app_id);
|
||||
|
||||
const auto read_cache_from_disk = [&]() {
|
||||
Vector<AppId_t> dlcs;
|
||||
|
||||
try {
|
||||
const auto text = io::read_file(get_cache_path());
|
||||
|
||||
if (text.empty()) {
|
||||
return dlcs;
|
||||
}
|
||||
|
||||
auto json = nlohmann::json::parse(text);
|
||||
|
||||
dlcs = json[app_id_str]["dlc"].get<decltype(cached_dlcs)>();
|
||||
|
||||
logger->debug("Read {} DLCs from cache", dlcs.size());
|
||||
} catch (const Exception& ex) {
|
||||
logger->error("Error reading DLCs from cache: {}", ex.what());
|
||||
total_success = false;
|
||||
}
|
||||
|
||||
return dlcs;
|
||||
};
|
||||
|
||||
const auto fetch_from_steam = [&]() {
|
||||
Vector<AppId_t> dlcs;
|
||||
|
||||
@@ -123,22 +76,25 @@ bool fetch_and_cache_dlcs(AppId_t app_id) {
|
||||
return dlcs;
|
||||
};
|
||||
|
||||
const auto cache_dlcs = read_cache_from_disk();
|
||||
const auto steam_dlcs = fetch_from_steam();
|
||||
const auto github_dlcs = fetch_from_github();
|
||||
|
||||
// Any of the sources might fail, so we try to get optimal result
|
||||
// by combining results from all the sources into a single set.
|
||||
Set<AppId_t> combined_dlcs;
|
||||
combined_dlcs.insert(cached_dlcs.begin(), cached_dlcs.end());
|
||||
combined_dlcs.insert(steam_dlcs.begin(), steam_dlcs.end());
|
||||
combined_dlcs.insert(github_dlcs.begin(), github_dlcs.end());
|
||||
// There is no need to insert cached entries if both steam and GitHub requests were successful.
|
||||
if (!total_success) {
|
||||
const auto cache_dlcs = cache::get_dlc_ids(app_id);
|
||||
combined_dlcs.insert(cached_dlcs.begin(), cached_dlcs.end());
|
||||
}
|
||||
|
||||
// We then transfer that set into a list because we need DLCs to be accessible via index.
|
||||
cached_dlcs.clear();
|
||||
cached_dlcs.insert(cached_dlcs.begin(), combined_dlcs.begin(), combined_dlcs.end());
|
||||
|
||||
save_cache_to_disk(app_id_str);
|
||||
cache::save_dlc_ids(app_id, cached_dlcs);
|
||||
|
||||
return total_success;
|
||||
}
|
||||
@@ -195,7 +151,7 @@ namespace steam_apps {
|
||||
static std::mutex mutex;
|
||||
const std::lock_guard<std::mutex> guard(mutex);
|
||||
|
||||
logger->debug("Game has {} or more DLCs. Fetching DLCs from a web API.", MAX_DLC);
|
||||
logger->debug("Game has {} or more DLCs. Fetching DLCs from remote sources.", MAX_DLC);
|
||||
|
||||
if (fetch_and_cache_dlcs(app_id)) {
|
||||
cached_apps.insert(app_id);
|
||||
@@ -273,7 +229,7 @@ namespace steam_apps {
|
||||
// We must have had cached DLC IDs at this point.
|
||||
// It does not matter if we begin the list with injected DLC IDs or cached ones.
|
||||
// However, we must be consistent at all times. Hence, the convention will be that
|
||||
// cached DLC should always follow the injected DLCs as follows:
|
||||
// injected DLCs will be followed by cached DLCs in the following manner:
|
||||
// [injected-dlc-0, injected-dlc-1, ..., cached-dlc-0, cached-dlc-1, ...]
|
||||
|
||||
if (iDLC < 0) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <steam_functions/steam_functions.hpp>
|
||||
|
||||
using namespace smoke_api;
|
||||
|
||||
namespace steam_apps {
|
||||
using namespace koalabox;
|
||||
|
||||
bool IsDlcUnlocked(const String& function_name, AppId_t app_id, AppId_t dlc_id);
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
// results from UserHasLicenseForApp
|
||||
enum EUserHasLicenseForAppResult {
|
||||
k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app
|
||||
k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app
|
||||
k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated
|
||||
};
|
||||
|
||||
typedef uint32_t SteamInventoryResult_t;
|
||||
typedef uint64_t SteamItemInstanceID_t;
|
||||
typedef uint32_t SteamItemDef_t;
|
||||
typedef uint32_t AppId_t;
|
||||
typedef uint32_t HSteamPipe;
|
||||
typedef uint32_t HSteamUser;
|
||||
typedef uint64_t CSteamID;
|
||||
|
||||
struct SteamItemDetails_t {
|
||||
SteamItemInstanceID_t m_itemId;
|
||||
@@ -18,12 +15,19 @@ struct SteamItemDetails_t {
|
||||
uint16_t m_unFlags; // see ESteamItemFlags
|
||||
};
|
||||
|
||||
// results from UserHasLicenseForApp
|
||||
enum EUserHasLicenseForAppResult {
|
||||
k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app
|
||||
k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app
|
||||
k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated
|
||||
};
|
||||
|
||||
enum EResult {
|
||||
k_EResultNone = 0, // no result
|
||||
k_EResultOK = 1, // success
|
||||
k_EResultFail = 2, // generic failure
|
||||
k_EResultNoConnection = 3, // no/failed network connection
|
||||
// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
|
||||
// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
|
||||
k_EResultInvalidPassword = 5, // password/ticket is invalid
|
||||
k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere
|
||||
k_EResultInvalidProtocolVer = 7, // protocol version is incorrect
|
||||
|
||||
Reference in New Issue
Block a user