Compare commits
2 Commits
eef92f1fe6
...
5234d8b0e6
Author | SHA1 | Date |
---|---|---|
Mr_Goldberg | 5234d8b0e6 | |
Mr_Goldberg | e59508a696 |
|
@ -47,6 +47,7 @@ public:
|
|||
static constexpr auto settings_storage_folder = "settings";
|
||||
static constexpr auto remote_storage_folder = "remote";
|
||||
static constexpr auto stats_storage_folder = "stats";
|
||||
static constexpr auto leaderboard_storage_folder = "leaderboard";
|
||||
static constexpr auto user_data_storage = "local";
|
||||
static constexpr auto screenshots_folder = "screenshots";
|
||||
static constexpr auto game_settings_folder = "steam_settings";
|
||||
|
|
|
@ -21,10 +21,17 @@
|
|||
#include "base.h"
|
||||
#include "../overlay_experimental/steam_overlay.h"
|
||||
|
||||
struct Steam_Leaderboard_Score {
|
||||
CSteamID steam_id;
|
||||
int32 score = 0;
|
||||
std::vector<int32> score_details;
|
||||
};
|
||||
|
||||
struct Steam_Leaderboard {
|
||||
std::string name;
|
||||
ELeaderboardSortMethod sort_method;
|
||||
ELeaderboardDisplayType display_type;
|
||||
Steam_Leaderboard_Score self_score;
|
||||
};
|
||||
|
||||
struct achievement_trigger {
|
||||
|
@ -121,6 +128,60 @@ void save_achievements()
|
|||
local_storage->write_json_file("", achievements_user_file, user_achievements);
|
||||
}
|
||||
|
||||
void save_leaderboard_score(Steam_Leaderboard *leaderboard)
|
||||
{
|
||||
std::vector<uint32_t> output;
|
||||
uint64_t steam_id = leaderboard->self_score.steam_id.ConvertToUint64();
|
||||
output.push_back(steam_id & 0xFFFFFFFF);
|
||||
output.push_back(steam_id >> 32);
|
||||
|
||||
output.push_back(leaderboard->self_score.score);
|
||||
output.push_back(leaderboard->self_score.score_details.size());
|
||||
for (auto &s : leaderboard->self_score.score_details) {
|
||||
output.push_back(s);
|
||||
}
|
||||
|
||||
std::string leaderboard_name = ascii_to_lowercase(leaderboard->name);
|
||||
local_storage->store_data(Local_Storage::leaderboard_storage_folder, leaderboard_name, (char* )output.data(), sizeof(uint32_t) * output.size());
|
||||
}
|
||||
|
||||
std::vector<Steam_Leaderboard_Score> load_leaderboard_scores(std::string name)
|
||||
{
|
||||
std::vector<Steam_Leaderboard_Score> out;
|
||||
|
||||
std::string leaderboard_name = ascii_to_lowercase(name);
|
||||
unsigned size = local_storage->file_size(Local_Storage::leaderboard_storage_folder, leaderboard_name);
|
||||
if (size == 0 || (size % sizeof(uint32_t)) != 0) return out;
|
||||
|
||||
std::vector<uint32_t> output(size / sizeof(uint32_t));
|
||||
if (local_storage->get_data(Local_Storage::leaderboard_storage_folder, leaderboard_name, (char* )output.data(), size) != size) return out;
|
||||
|
||||
unsigned i = 0;
|
||||
while (true) {
|
||||
if ((i + 4) > output.size()) break;
|
||||
|
||||
Steam_Leaderboard_Score score;
|
||||
score.steam_id = CSteamID((uint64)output[i] + (((uint64)output[i + 1]) << 32));
|
||||
i += 2;
|
||||
score.score = output[i];
|
||||
i += 1;
|
||||
unsigned count = output[i];
|
||||
i += 1;
|
||||
|
||||
if ((i + count) > output.size()) break;
|
||||
|
||||
for (unsigned j = 0; j < count; ++j) {
|
||||
score.score_details.push_back(output[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
PRINT_DEBUG("loaded leaderboard score %llu %u\n", score.steam_id.ConvertToUint64(), score.score);
|
||||
out.push_back(score);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public:
|
||||
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, Steam_Overlay* overlay):
|
||||
settings(settings),
|
||||
|
@ -734,6 +795,14 @@ SteamAPICall_t FindOrCreateLeaderboard( const char *pchLeaderboardName, ELeaderb
|
|||
leaderboard.name = std::string(pchLeaderboardName);
|
||||
leaderboard.sort_method = eLeaderboardSortMethod;
|
||||
leaderboard.display_type = eLeaderboardDisplayType;
|
||||
|
||||
std::vector<Steam_Leaderboard_Score> scores = load_leaderboard_scores(pchLeaderboardName);
|
||||
for (auto &s : scores) {
|
||||
if (s.steam_id == settings->get_local_steam_id()) {
|
||||
leaderboard.self_score = s;
|
||||
}
|
||||
}
|
||||
|
||||
leaderboards.push_back(leaderboard);
|
||||
leader = leaderboards.size();
|
||||
}
|
||||
|
@ -826,10 +895,12 @@ SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard,
|
|||
{
|
||||
PRINT_DEBUG("DownloadLeaderboardEntries %llu %i %i %i\n", hSteamLeaderboard, eLeaderboardDataRequest, nRangeStart, nRangeEnd);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_uAPICallInvalid; //might return callresult even if hSteamLeaderboard is invalid
|
||||
|
||||
LeaderboardScoresDownloaded_t data;
|
||||
data.m_hSteamLeaderboard = hSteamLeaderboard;
|
||||
data.m_hSteamLeaderboardEntries = 123;
|
||||
data.m_cEntryCount = 0;
|
||||
data.m_hSteamLeaderboardEntries = hSteamLeaderboard;
|
||||
data.m_cEntryCount = leaderboards[hSteamLeaderboard - 1].self_score.steam_id.IsValid();
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
|
@ -843,10 +914,19 @@ SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLead
|
|||
{
|
||||
PRINT_DEBUG("DownloadLeaderboardEntriesForUsers %i %llu\n", cUsers, cUsers > 0 ? prgUsers[0].ConvertToUint64() : 0);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_uAPICallInvalid; //might return callresult even if hSteamLeaderboard is invalid
|
||||
|
||||
bool get_for_current_id = false;
|
||||
for (int i = 0; i < cUsers; ++i) {
|
||||
if (prgUsers[i] == settings->get_local_steam_id()) {
|
||||
get_for_current_id = true;
|
||||
}
|
||||
}
|
||||
|
||||
LeaderboardScoresDownloaded_t data;
|
||||
data.m_hSteamLeaderboard = hSteamLeaderboard;
|
||||
data.m_hSteamLeaderboardEntries = 123;
|
||||
data.m_cEntryCount = 0;
|
||||
data.m_hSteamLeaderboardEntries = hSteamLeaderboard;
|
||||
data.m_cEntryCount = get_for_current_id && leaderboards[hSteamLeaderboard - 1].self_score.steam_id.IsValid();
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
|
@ -868,7 +948,20 @@ SteamAPICall_t DownloadLeaderboardEntriesForUsers( SteamLeaderboard_t hSteamLead
|
|||
bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t *pLeaderboardEntry, int32 *pDetails, int cDetailsMax )
|
||||
{
|
||||
PRINT_DEBUG("GetDownloadedLeaderboardEntry\n");
|
||||
return false;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (hSteamLeaderboardEntries > leaderboards.size() || hSteamLeaderboardEntries <= 0) return false;
|
||||
if (index > 0) return false;
|
||||
|
||||
LeaderboardEntry_t entry = {};
|
||||
entry.m_steamIDUser = leaderboards[hSteamLeaderboardEntries - 1].self_score.steam_id;
|
||||
entry.m_nGlobalRank = 1;
|
||||
entry.m_nScore = leaderboards[hSteamLeaderboardEntries - 1].self_score.score;
|
||||
for (int i = 0; i < leaderboards[hSteamLeaderboardEntries - 1].self_score.score_details.size() && i < cDetailsMax; ++i) {
|
||||
pDetails[i] = leaderboards[hSteamLeaderboardEntries - 1].self_score.score_details[i];
|
||||
}
|
||||
|
||||
*pLeaderboardEntry = entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -879,17 +972,39 @@ bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardE
|
|||
STEAM_CALL_RESULT( LeaderboardScoreUploaded_t )
|
||||
SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount )
|
||||
{
|
||||
PRINT_DEBUG("UploadLeaderboardScore\n");
|
||||
PRINT_DEBUG("UploadLeaderboardScore %i\n", nScore);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (hSteamLeaderboard > leaderboards.size() || hSteamLeaderboard <= 0) return k_uAPICallInvalid; //TODO: might return callresult even if hSteamLeaderboard is invalid
|
||||
|
||||
Steam_Leaderboard_Score score;
|
||||
score.score = nScore;
|
||||
score.steam_id = settings->get_local_steam_id();
|
||||
for (int i = 0; i < cScoreDetailsCount; ++i) {
|
||||
score.score_details.push_back(pScoreDetails[i]);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (eLeaderboardUploadScoreMethod == k_ELeaderboardUploadScoreMethodKeepBest) {
|
||||
if (leaderboards[hSteamLeaderboard - 1].self_score.score <= score.score) {
|
||||
leaderboards[hSteamLeaderboard - 1].self_score = score;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if (leaderboards[hSteamLeaderboard - 1].self_score.score != score.score) changed = true;
|
||||
leaderboards[hSteamLeaderboard - 1].self_score = score;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
save_leaderboard_score(&(leaderboards[hSteamLeaderboard - 1]));
|
||||
}
|
||||
|
||||
LeaderboardScoreUploaded_t data;
|
||||
data.m_bSuccess = 1; //needs to be success or DOA6 freezes when uploading score.
|
||||
//data.m_bSuccess = 0;
|
||||
data.m_hSteamLeaderboard = hSteamLeaderboard;
|
||||
data.m_nScore = nScore;
|
||||
//data.m_bScoreChanged = 1;
|
||||
data.m_bScoreChanged = 0;
|
||||
//data.m_nGlobalRankNew = 1;
|
||||
data.m_nGlobalRankNew = 0;
|
||||
data.m_bScoreChanged = changed;
|
||||
data.m_nGlobalRankNew = 1;
|
||||
data.m_nGlobalRankPrevious = 0;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
|
|
@ -3,22 +3,28 @@ USERNAME = ""
|
|||
PASSWORD = ""
|
||||
|
||||
#steam ids with public profiles that own a lot of games
|
||||
TOP_OWNER_IDS = [76561198028121353, 76561198001237877, 76561198001678750, 76561198237402290]
|
||||
TOP_OWNER_IDS = [76561198028121353, 76561198001237877, 76561198355625888, 76561198001678750, 76561198237402290]
|
||||
|
||||
from stats_schema_achievement_gen import achievements_gen
|
||||
from steam.client import SteamClient
|
||||
from steam.client.cdn import CDNClient
|
||||
from steam.enums import common
|
||||
from steam.enums.common import EResult
|
||||
from steam.enums.emsg import EMsg
|
||||
from steam.core.msg import MsgProto
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
prompt_for_unavailable = True
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("\nUsage: {} appid\n\nExample: {} 480\n".format(sys.argv[0], sys.argv[0]))
|
||||
print("\nUsage: {} appid appid appid etc..\n\nExample: {} 480\n".format(sys.argv[0], sys.argv[0]))
|
||||
exit(1)
|
||||
|
||||
appid = int(sys.argv[1])
|
||||
appids = []
|
||||
for id in sys.argv[1:]:
|
||||
appids += [int(id)]
|
||||
|
||||
client = SteamClient()
|
||||
if not os.path.exists("login_temp"):
|
||||
|
@ -28,16 +34,40 @@ client.set_credential_location("login_temp")
|
|||
if (len(USERNAME) == 0 or len(PASSWORD) == 0):
|
||||
client.cli_login()
|
||||
else:
|
||||
while True:
|
||||
ret = client.login(USERNAME, password=PASSWORD)
|
||||
if ret != common.EResult.OK:
|
||||
if ret == common.EResult.TryAnotherCM:
|
||||
continue
|
||||
print("error logging in with set username and password, trying cli login. error was:", ret)
|
||||
client.cli_login()
|
||||
break
|
||||
else:
|
||||
break
|
||||
result = client.login(USERNAME, password=PASSWORD)
|
||||
while result in (EResult.AccountLogonDenied, EResult.InvalidLoginAuthCode,
|
||||
EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch,
|
||||
EResult.TryAnotherCM, EResult.ServiceUnavailable,
|
||||
EResult.InvalidPassword,
|
||||
):
|
||||
|
||||
if result == EResult.InvalidPassword:
|
||||
print("invalid password, the password you set is wrong.")
|
||||
exit(1)
|
||||
|
||||
elif result in (EResult.AccountLogonDenied, EResult.InvalidLoginAuthCode):
|
||||
prompt = ("Enter email code: " if result == EResult.AccountLogonDenied else
|
||||
"Incorrect code. Enter email code: ")
|
||||
auth_code, two_factor_code = input(prompt), None
|
||||
|
||||
elif result in (EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch):
|
||||
prompt = ("Enter 2FA code: " if result == EResult.AccountLoginDeniedNeedTwoFactor else
|
||||
"Incorrect code. Enter 2FA code: ")
|
||||
auth_code, two_factor_code = None, input(prompt)
|
||||
|
||||
elif result in (EResult.TryAnotherCM, EResult.ServiceUnavailable):
|
||||
if prompt_for_unavailable and result == EResult.ServiceUnavailable:
|
||||
while True:
|
||||
answer = input("Steam is down. Keep retrying? [y/n]: ").lower()
|
||||
if answer in 'yn': break
|
||||
|
||||
prompt_for_unavailable = False
|
||||
if answer == 'n': break
|
||||
|
||||
client.reconnect(maxdelay=15)
|
||||
|
||||
result = client.login(USERNAME, PASSWORD, None, auth_code, two_factor_code)
|
||||
|
||||
|
||||
def get_stats_schema(client, game_id, owner_id):
|
||||
message = MsgProto(EMsg.ClientGetUserStats)
|
||||
|
@ -49,27 +79,50 @@ def get_stats_schema(client, game_id, owner_id):
|
|||
client.send(message)
|
||||
return client.wait_msg(EMsg.ClientGetUserStatsResponse, timeout=5)
|
||||
|
||||
def generate_achievement_stats(client, game_id, output_directory):
|
||||
def generate_achievement_stats(client, game_id, output_directory, backup_directory):
|
||||
steam_id_list = TOP_OWNER_IDS + [client.steam_id]
|
||||
for x in steam_id_list:
|
||||
out = get_stats_schema(client, game_id, x)
|
||||
if out is not None:
|
||||
if len(out.body.schema) > 0:
|
||||
with open('UserGameStatsSchema_{}.bin'.format(appid), 'wb') as f:
|
||||
with open(os.path.join(backup_directory, 'UserGameStatsSchema_{}.bin'.format(appid)), 'wb') as f:
|
||||
f.write(out.body.schema)
|
||||
achievements, stats = achievements_gen.generate_stats_achievements(out.body.schema, output_directory)
|
||||
return
|
||||
else:
|
||||
print("no schema", out)
|
||||
|
||||
out_dir = os.path.join("{}".format( "{}_output".format(appid)), "steam_settings")
|
||||
for appid in appids:
|
||||
backup_dir = os.path.join("backup","{}".format(appid))
|
||||
out_dir = os.path.join("{}".format( "{}_output".format(appid)), "steam_settings")
|
||||
|
||||
if not os.path.exists(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
if not os.path.exists(backup_dir):
|
||||
os.makedirs(backup_dir)
|
||||
|
||||
print("outputting config to", out_dir)
|
||||
if not os.path.exists(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
|
||||
generate_achievement_stats(client, appid, out_dir)
|
||||
with open(os.path.join(out_dir, "steam_appid.txt"), 'w') as f:
|
||||
f.write(str(appid))
|
||||
print("outputting config to", out_dir)
|
||||
|
||||
raw = client.get_product_info(apps=[appid])
|
||||
game_info = raw["apps"][appid]
|
||||
|
||||
if "common" in game_info:
|
||||
game_info_common = game_info["common"]
|
||||
if "community_visible_stats" in game_info_common:
|
||||
generate_achievement_stats(client, appid, out_dir, backup_dir)
|
||||
|
||||
with open(os.path.join(out_dir, "steam_appid.txt"), 'w') as f:
|
||||
f.write(str(appid))
|
||||
|
||||
if "depots" in game_info:
|
||||
if "branches" in game_info["depots"]:
|
||||
if "public" in game_info["depots"]["branches"]:
|
||||
if "buildid" in game_info["depots"]["branches"]["public"]:
|
||||
buildid = game_info["depots"]["branches"]["public"]["buildid"]
|
||||
with open(os.path.join(out_dir, "build_id.txt"), 'w') as f:
|
||||
f.write(str(buildid))
|
||||
|
||||
game_info_backup = json.dumps(game_info, indent=4)
|
||||
with open(os.path.join(backup_dir, "product_info.json"), "w") as f:
|
||||
f.write(game_info_backup)
|
||||
|
|
|
@ -4,10 +4,11 @@ This script depends on python files that are in subfolders so make sure to downl
|
|||
|
||||
Using the script: python generate_emu_config.py appid
|
||||
|
||||
You can also pass multiple appids to generate multiple configs: python generate_emu_config.py appid appid appid
|
||||
|
||||
The appid is the number in the steam url.
|
||||
|
||||
The first time you run the script it will ask you for your steam username, password and email code.
|
||||
The email code will only be asked the first time and the sentry will be saved to the login_temp folder.
|
||||
The email code should only be asked the first time and the sentry will be saved to the login_temp folder.
|
||||
|
||||
This script will not save your username/password anywhere. If you don't want to always type it you must
|
||||
open up the script in a text editor and change:
|
||||
|
|
Loading…
Reference in New Issue